diff --git a/.gitignore b/.gitignore index 49b663d..4596550 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,12 @@ mill* bench/mem .metals .bloop -.rpt -.Xil +*.rpt +*.jou +*.log +*.mmi +*.Xil clockInfo.txt -.csv +*.csv +*.bit +*.dcp diff --git a/Makefile b/Makefile index 3473806..ff69a2c 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/bench/programs_rust/.cargo/config.toml b/bench/programs_rust/.cargo/config.toml new file mode 100644 index 0000000..6f20977 --- /dev/null +++ b/bench/programs_rust/.cargo/config.toml @@ -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"] diff --git a/bench/programs_rust/.gitignore b/bench/programs_rust/.gitignore new file mode 100644 index 0000000..a7c787f --- /dev/null +++ b/bench/programs_rust/.gitignore @@ -0,0 +1,3 @@ +.helix/ +target/ +Cargo.lock diff --git a/bench/programs_rust/Cargo.toml b/bench/programs_rust/Cargo.toml new file mode 100644 index 0000000..a22a47c --- /dev/null +++ b/bench/programs_rust/Cargo.toml @@ -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" diff --git a/bench/programs_rust/fontplate.png b/bench/programs_rust/fontplate.png new file mode 100644 index 0000000..7120f47 Binary files /dev/null and b/bench/programs_rust/fontplate.png differ diff --git a/bench/programs_rust/fpga-lib-macros/Cargo.toml b/bench/programs_rust/fpga-lib-macros/Cargo.toml new file mode 100644 index 0000000..34a7a98 --- /dev/null +++ b/bench/programs_rust/fpga-lib-macros/Cargo.toml @@ -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"] } diff --git a/bench/programs_rust/fpga-lib-macros/src/image.rs b/bench/programs_rust/fpga-lib-macros/src/image.rs new file mode 100644 index 0000000..63106d2 --- /dev/null +++ b/bench/programs_rust/fpga-lib-macros/src/image.rs @@ -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, Vec>, width: usize, height: usize) -> Vec { + 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, 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 { + 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, + ), + syn::Error, +> { + // parse the input into a comma separated list of arguments + let parsed_args = syn::parse::(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::>(); + 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() +} diff --git a/bench/programs_rust/fpga-lib-macros/src/lib.rs b/bench/programs_rust/fpga-lib-macros/src/lib.rs new file mode 100644 index 0000000..59ba7d7 --- /dev/null +++ b/bench/programs_rust/fpga-lib-macros/src/lib.rs @@ -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() { + 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) +} diff --git a/bench/programs_rust/fpga-lib/Cargo.toml b/bench/programs_rust/fpga-lib/Cargo.toml new file mode 100644 index 0000000..c2ec95d --- /dev/null +++ b/bench/programs_rust/fpga-lib/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fpga-lib" +version = "0.1.0" +edition = "2024" + +[dependencies] +fpga-lib-macros = { path = "../fpga-lib-macros" } diff --git a/bench/programs_rust/fpga-lib/src/lib.rs b/bench/programs_rust/fpga-lib/src/lib.rs new file mode 100644 index 0000000..7c3af85 --- /dev/null +++ b/bench/programs_rust/fpga-lib/src/lib.rs @@ -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 {} + } + }; +} diff --git a/bench/programs_rust/fpga-lib/src/register.rs b/bench/programs_rust/fpga-lib/src/register.rs new file mode 100644 index 0000000..6077fc8 --- /dev/null +++ b/bench/programs_rust/fpga-lib/src/register.rs @@ -0,0 +1,3 @@ +pub struct Reg {} + +fpga_lib_macros::define_regs! {} diff --git a/bench/programs_rust/fpga-lib/src/vga.rs b/bench/programs_rust/fpga-lib/src/vga.rs new file mode 100644 index 0000000..1957f2d --- /dev/null +++ b/bench/programs_rust/fpga-lib/src/vga.rs @@ -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"}; diff --git a/bench/programs_rust/hello-rust/Cargo.toml b/bench/programs_rust/hello-rust/Cargo.toml new file mode 100644 index 0000000..5e7e4cf --- /dev/null +++ b/bench/programs_rust/hello-rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "hello-rust" +version = "0.1.0" +edition = "2024" + +[dependencies] +fpga-lib = { path = "../fpga-lib" } diff --git a/bench/programs_rust/hello-rust/src/main.rs b/bench/programs_rust/hello-rust/src/main.rs new file mode 100644 index 0000000..2e71d73 --- /dev/null +++ b/bench/programs_rust/hello-rust/src/main.rs @@ -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 {} +} diff --git a/bench/programs_rust/riscv64i.json b/bench/programs_rust/riscv64i.json new file mode 100644 index 0000000..b3831f4 --- /dev/null +++ b/bench/programs_rust/riscv64i.json @@ -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 +} diff --git a/bench/programs_rust/rust-toolchain.toml b/bench/programs_rust/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/bench/programs_rust/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly"