Merge branch 'rust'

This commit is contained in:
2025-11-13 10:41:19 +01:00
17 changed files with 339 additions and 7 deletions

11
.gitignore vendored
View File

@@ -6,7 +6,12 @@ mill*
bench/mem
.metals
.bloop
.rpt
.Xil
*.rpt
*.jou
*.log
*.mmi
*.Xil
clockInfo.txt
.csv
*.csv
*.bit
*.dcp

View File

@@ -61,13 +61,18 @@ TEST_MAX_PROCS := $$(($$(nproc) - 2)) ## Garde un processeur libre
.PRECIOUS: $(MEM_DIR)/%.elf
.PHONY: synthese fpga clean compile autotest simulation
.PHONY: synthese fpga clean compile autotest simulation force
force:
compile: $(MEM)
$(MEM_DIR):
@mkdir -p $@/tests/op
@mkdir -p $@/programs
@mkdir -p $@/programs_rust
$(MEM_DIR)/programs_rust/%.elf: bench/programs_rust/% force |$(MEM_DIR)
cd bench/programs_rust && cargo build --release --bin=$(basename $(notdir $@))
cp -f bench/programs_rust/target/riscv64i/release/$(basename $(notdir $@)) $@
$(MEM_DIR)/crt.o: $(BENCH_DIR)/crt.S |$(MEM_DIR)
$(CC) $(CFLAGS) -c $< -o $@
$(MEM_DIR)/%.elf: $(BENCH_DIR)/%.c $(MEM_DIR)/crt.o |$(MEM_DIR)
@@ -106,14 +111,16 @@ mill:
autotest: mill test-bench/mem/tests/$(PROG).mem ##! Lance la simulation automatique pour tous les tests ou juste celui fourni dans PROG
testall: mill compile
@# Limite le nombre de processeurs et la ram utilisée pour les tests parellèle
testall: mill compile ##! Lance la simulation automatique pour tous les tests en parallèle
@# Limite le nombre de processeurs et la ram utilisée pour les tests parallèle
@( \
ulimit -v $(TEST_MAX_RAM); \
PROOT=$(PWD) taskset -c 0-$(TEST_MAX_PROCS) ./mill TPchisel.test.testOnly $(TOP_REP).ZzTopAllSpec \
)
simulation: mill compile ##! Lance gtkwave sur le test fourni dans PROG
$(PROG): $(MEM_DIR)/$(PROG).mem
simulation: mill $(PROG) ##! Lance gtkwave sur le test fourni dans PROG
@$(call check_vars,PROG)
@PROOT=$(PWD) PROG=$(PROG) CYCLES=$(CYCLES) ./mill TPchisel.test.testOnly $(TOP_REP).$(TOP_TEST) -- -DemitVcd=1 -z "gtkwave"
@gtkwave -a common/config.gtkw ./build/chiselsim/ZzTopSpec/Simulation-pour-gtkwave/workdir-verilator/trace.vcd

View File

@@ -0,0 +1,14 @@
[build]
target = "riscv64i.json"
[target.riscv64i]
linker = "riscv64-unknown-elf-ld"
# linker = "rust-lld"
rustflags = [
# "-C", "link-arg=-nostartfiles",
"-C", "link-arg=-T../link.ld",
]
[unstable]
build-std = ["core", "compiler_builtins"]

3
bench/programs_rust/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.helix/
target/
Cargo.lock

View File

@@ -0,0 +1,13 @@
[workspace]
resolver = "3"
members = ["fpga-lib", "fpga-lib-macros", "hello-rust"]
[profile.release]
panic = "abort"
codegen-units = 1 # better optimizations
lto = true # better optimizations
strip = true
opt-level = "z"
[profile.dev]
panic = "abort"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,14 @@
[package]
name = "fpga-lib-macros"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
[dependencies]
image = "0.25.5"
regex = "1.11.1"
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }

View File

@@ -0,0 +1,106 @@
use image::{ImageBuffer, Luma};
use proc_macro::{Span, TokenStream};
use quote::quote;
use regex::Regex;
use syn::parse::Parse;
fn remove_non_alphanumeric(input: &str) -> String {
let re = Regex::new(r"[^a-zA-Z0-9_]+").unwrap();
re.replace_all(input, "").to_string()
}
fn to_format(img: ImageBuffer<Luma<u8>, Vec<u8>>, width: usize, height: usize) -> Vec<u8> {
let mut output = Vec::new();
let mut bit: u8 = 0;
let mut byte: u8 = 0;
for y in 0..height {
for x in 0..width {
let pixel = img.get_pixel(x as u32, y as u32)[0];
if pixel >= 127 {
byte |= 1 << bit;
}
bit += 1;
if bit == 8 {
output.push(byte);
byte = 0;
bit = 0;
}
}
}
if bit != 0 {
output.push(byte);
}
output
}
fn path_to_image(path: &str) -> (Vec<u8>, String, usize, usize) {
let img = match image::open(path) {
Ok(img) => img.to_luma8(),
Err(e) => panic!("failed to open image {}: {}", path, e),
};
let width = img.width() as usize;
let height = img.height() as usize;
let bytes = to_format(img, width, height);
let path = path
.split('/')
.next_back()
.expect("failed to get last part of path");
let split: Vec<_> = path.split('.').collect();
let name = remove_non_alphanumeric(&split[0..split.len() - 1].join(".")).to_uppercase();
(bytes, name, width, height)
}
struct ParsedArgs {
path: String,
}
impl Parse for ParsedArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let path: syn::LitStr = input.parse()?;
let path = path.value();
Ok(ParsedArgs { path })
}
}
pub fn parse_image(
input: TokenStream,
) -> Result<
(
u8,
u8,
proc_macro2::Ident,
usize,
Vec<proc_macro2::TokenStream>,
),
syn::Error,
> {
// parse the input into a comma separated list of arguments
let parsed_args = syn::parse::<ParsedArgs>(input)?;
// let parsed_args = parse_macro_input!(input as ParsedArgs);
let (bytes, name, width, height) = path_to_image(&parsed_args.path);
let width = width as u8;
let height = height as u8;
let byte_array = bytes.as_slice();
let byte_count = byte_array.len();
let name_ident = syn::Ident::new(&name, Span::call_site().into());
let byte_tokens = bytes.iter().map(|b| quote! { #b }).collect::<Vec<_>>();
Ok((width, height, name_ident, byte_count, byte_tokens))
}
pub fn include_font_plate_impl(input: TokenStream) -> TokenStream {
let (_, _, _, _, byte_tokens) = parse_image(input).unwrap();
let output = quote! {
[#(#byte_tokens),*]
};
output.into()
}

View File

@@ -0,0 +1,40 @@
mod image;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
#[proc_macro]
pub fn define_regs(_input: TokenStream) -> TokenStream {
let mut tokens = proc_macro2::TokenStream::new();
for n in 0..32usize {
let load = format!("li x{}, {{}}", n);
let store = format!("mv {{}}, x{}", n);
let ident = format_ident!("X{}", n);
let t = quote! {
impl Reg<#n> {
pub fn write_const<const X: u64>() {
unsafe {
core::arch::asm!(#load, const X)
}
}
pub fn read() -> u64 {
let out: u64;
unsafe {
core::arch::asm!(#store, out(reg) out)
}
out
}
}
pub type #ident = Reg<#n>;
};
tokens.extend(t);
}
tokens.into()
}
#[proc_macro]
pub fn include_font_plate(input: TokenStream) -> TokenStream {
image::include_font_plate_impl(input)
}

View File

@@ -0,0 +1,7 @@
[package]
name = "fpga-lib"
version = "0.1.0"
edition = "2024"
[dependencies]
fpga-lib-macros = { path = "../fpga-lib-macros" }

View File

@@ -0,0 +1,14 @@
#![no_std]
pub mod register;
pub mod vga;
#[macro_export]
macro_rules! panic_handler {
() => {
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
};
}

View File

@@ -0,0 +1,3 @@
pub struct Reg<const N: usize> {}
fpga_lib_macros::define_regs! {}

View File

@@ -0,0 +1,48 @@
use fpga_lib_macros::include_font_plate;
pub const VGA_ADDRESS: *mut Color = 0x80000000 as *mut Color;
pub const WIDTH: usize = 320;
pub const HEIGHT: usize = 240;
#[repr(transparent)]
pub struct Color(pub(crate) u8);
impl Color {
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self(((r >> 6) << 6) | ((g >> 5) << 3) | (b >> 5))
}
}
pub const WHITE: Color = Color::from_rgb(255, 255, 255);
pub const GRAY: Color = Color::from_rgb(128, 128, 128);
pub const LIGHT_GRAY: Color = Color::from_rgb(128, 64, 64);
pub const BLACK: Color = Color::from_rgb(0, 0, 0);
pub const RED: Color = Color::from_rgb(255, 0, 0);
pub const GREEN: Color = Color::from_rgb(0, 255, 0);
pub const BLUE: Color = Color::from_rgb(0, 0, 255);
pub struct VGA {}
impl VGA {
/// # Safety
/// `x` must be less than `WIDTH` and `y` must be less than `HEIGHT`
pub unsafe fn write_pixel_unsafe(x: u16, y: u16, color: Color) {
unsafe { *VGA_ADDRESS.add(x as usize + y as usize * WIDTH) = color }
}
pub unsafe fn draw_char(x: u16, y: u16, c: char) {
let c = c as u8 - b' ';
for i in 0..FONT_HEIGHT {
for j in 0..FONT_WIDTH {
}
}
}
}
pub const FONT_WIDTH: usize = 6;
pub const FONT_HEIGHT: usize = 13;
pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH;
pub const FONTPLATE_HEIGHT: usize = 3 * FONT_HEIGHT;
pub const FONTPLATE_SIZE: usize = FONTPLATE_WIDTH * FONTPLATE_HEIGHT / 8;
pub const FONTPLATE: [u8; FONTPLATE_SIZE] = include_font_plate! {"fontplate.png"};

View File

@@ -0,0 +1,7 @@
[package]
name = "hello-rust"
version = "0.1.0"
edition = "2024"
[dependencies]
fpga-lib = { path = "../fpga-lib" }

View File

@@ -0,0 +1,28 @@
#![no_std]
#![no_main]
use core::arch::asm;
use fpga_lib::{
panic_handler,
register::X31,
vga::{VGA, WHITE},
};
panic_handler! {}
#[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! {
X31::write_const::<0xdead1ffffffff>();
let test = X31::read() + 10;
unsafe {
asm!(
"add x31, x0, x0",
"add x31, x0, {}", in(reg) test
);
VGA::write_pixel_unsafe(0, 0, WHITE);
}
loop {}
}

View File

@@ -0,0 +1,21 @@
{
"llvm-target": "riscv64",
"llvm-abiname": "lp64",
"abi": "lp64",
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
"target-endian": "little",
"target-pointer-width": 64,
"arch": "riscv64",
"os": "none",
"vendor": "unknown",
"env": "",
"features": "+i",
"linker-flavor": "ld.lld",
"linker": "riscv64-unknown-elf-ld",
"executables": true,
"panic-strategy": "abort",
"relocation-model": "static",
"disable-redzone": true,
"emit-debug-gdb-scripts": false,
"eh-frame-header": false
}

View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"