diff --git a/conn/Cargo.toml b/conn/Cargo.toml index 69c23d2..ec1b92a 100644 --- a/conn/Cargo.toml +++ b/conn/Cargo.toml @@ -1,7 +1,11 @@ [package] name = "vhdl_ui" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] -crate-type = ["staticlib"] \ No newline at end of file +crate-type = ["staticlib"] + + +[lints.clippy] +identity_op = "allow" \ No newline at end of file diff --git a/conn/src/lib.rs b/conn/src/lib.rs index dad0f83..3dd1f5c 100644 --- a/conn/src/lib.rs +++ b/conn/src/lib.rs @@ -1,22 +1,40 @@ use std::{ io::{BufRead, BufReader}, - sync::atomic::{AtomicU32, Ordering}, + sync::atomic::{AtomicU8, AtomicU32, AtomicU64, Ordering}, }; +#[repr(u8)] +pub enum Sig { + SU = 0, + SX = 1, + S0 = 2, + S1 = 3, + SZ = 4, + SW = 5, + SL = 6, + SH = 7, + SD = 8 +} + pub struct SimState{ switch: AtomicU32, button: AtomicU32, led: AtomicU32, - hex: AtomicU32, + segs: [AtomicU32; 4], + updated: AtomicU8, } static STATE: SimState = SimState{ switch: AtomicU32::new(512), button: AtomicU32::new(0), led: AtomicU32::new(0), - hex: AtomicU32::new(0), + segs: [const{AtomicU32::new(0)}; 4], + updated: AtomicU8::new(0) }; +/// default 1 ms +static SLEEP_NANOS: AtomicU64 = AtomicU64::new(1_000_000); + fn client() { let reader = BufReader::new(std::io::stdin()); for line in reader.lines().map_while(Result::ok) { @@ -25,37 +43,65 @@ fn client() { if let Ok(n) = v.parse::() { STATE.switch.store(n, Ordering::Relaxed); } - } else if let Some(v) = line.strip_prefix("key=") { + } else if let Some(v) = line.strip_prefix("btn=") { if let Ok(n) = v.parse::() { STATE.button.store(n, Ordering::Relaxed); } + } else if let value = STATE.updated.swap(0, Ordering::Relaxed) && value!=0 { + if (value >> 0) & 1 == 1{ + eprintln!("led={}", STATE.led.load(Ordering::Relaxed)); + } + for i in 0..4{ + if (value >> (i+1)) & 1 == 1{ + eprintln!("seg{i}={}", STATE.segs[i].load(Ordering::Relaxed)); + } + } } } } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn ffi_init() { std::thread::Builder::new().name("client".into()).spawn(client).expect("Failed to spawn client thread"); + for arg in std::env::args(){ + if let Some(arg) = arg.strip_prefix("--cycle_sleep").or(arg.strip_prefix("-c")){ + if let Ok(nanos) = arg.trim().parse::(){ + SLEEP_NANOS.store(nanos, Ordering::Relaxed); + }else{ + eprintln!("cycle sleep(ns) failed to parse"); + } + } + } eprintln!("[ffi] initialzied"); } -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn ffi_get_sw() -> u32 { STATE.switch.load(Ordering::Relaxed) } -#[no_mangle] -pub extern "C" fn ffi_get_key() -> u32 { +#[unsafe(no_mangle)] +pub extern "C" fn ffi_get_btn() -> u32 { STATE.button.load(Ordering::Relaxed) } -#[no_mangle] -pub extern "C" fn ffi_set_outputs(led: u32, hex: u32) { - if STATE.led.swap(led, Ordering::Relaxed) != led{ - eprintln!("LED={}", STATE.led.load(Ordering::Relaxed)) - } - if STATE.hex.swap(hex, Ordering::Relaxed) != hex{ - eprintln!("HEX={}", STATE.hex.load(Ordering::Relaxed)) - } - std::thread::sleep(std::time::Duration::from_millis(1)); -} \ No newline at end of file +#[unsafe(no_mangle)] +pub extern "C" fn ffi_set_outputs(led: u32, seg0: u32, seg1: u32, seg2: u32, seg3: u32) { + let o_led = STATE.led.swap(led, Ordering::Relaxed) != led; + + let o_seg0 = STATE.segs[0].swap(seg0, Ordering::Relaxed) != seg0; + let o_seg1 = STATE.segs[1].swap(seg1, Ordering::Relaxed) != seg1; + let o_seg2 = STATE.segs[2].swap(seg2, Ordering::Relaxed) != seg2; + let o_seg3 = STATE.segs[3].swap(seg3, Ordering::Relaxed) != seg3; + + let to_set = (o_led as u8) << 0 + | (o_seg0 as u8) << 1 + | (o_seg1 as u8) << 2 + | (o_seg2 as u8) << 3 + | (o_seg3 as u8) << 4; + + STATE.updated.fetch_or(to_set, Ordering::Relaxed); + + std::thread::sleep(std::time::Duration::from_nanos(SLEEP_NANOS.load(Ordering::Relaxed))); +} + diff --git a/examples/cpu.vhdl b/examples/cpu.vhdl index 3b7639b..edf6c70 100644 --- a/examples/cpu.vhdl +++ b/examples/cpu.vhdl @@ -6,10 +6,13 @@ use ieee.numeric_std.all; entity circuit is port ( clk: in std_logic; - key: in std_logic_vector(31 downto 0); -- active low - sw: in std_logic_vector(31 downto 0); -- active high - led: out std_logic_vector(31 downto 0); -- active high - hex: out std_logic_vector(31 downto 0) + btn: in std_logic_vector(31 downto 0); + sw: in std_logic_vector(31 downto 0); + led: out std_logic_vector(31 downto 0); + seg0: out std_logic_vector(31 downto 0); + seg1: out std_logic_vector(31 downto 0); + seg2: out std_logic_vector(31 downto 0); + seg3: out std_logic_vector(31 downto 0) ); end circuit; @@ -181,11 +184,11 @@ architecture description of circuit is begin - hex(6 downto 0) <= not dec7seg(reg_out(7 downto 4)); - hex(14 downto 8) <= not dec7seg(reg_out(3 downto 0)); + seg0(6 downto 0) <= not dec7seg(reg_out(7 downto 4)); + seg0(14 downto 8) <= not dec7seg(reg_out(3 downto 0)); - hex(22 downto 16) <= not dec7seg(reg_pc(7 downto 4)); - hex(30 downto 24) <= not dec7seg(reg_pc(3 downto 0)); + seg0(22 downto 16) <= not dec7seg(reg_pc(7 downto 4)); + seg0(30 downto 24) <= not dec7seg(reg_pc(3 downto 0)); led(7 downto 0) <= std_logic_vector(reg_a); led(23 downto 16) <= std_logic_vector(reg_b); @@ -196,7 +199,7 @@ begin led(11) <= flag_gt; led(12) <= flag_carry; - clock <= clk when sw(9) = '1' else key(0); + clock <= clk when sw(9) = '1' else btn(0); ram_inst : entity work.inst_ram_8x256 port map( diff --git a/examples/example.vhdl b/examples/example.vhdl index 9f72241..4e0a3f3 100644 --- a/examples/example.vhdl +++ b/examples/example.vhdl @@ -6,10 +6,13 @@ use ieee.numeric_std.all; entity circuit is port ( clk: in std_logic; -- 500 Hz, period 2 ms - key: in std_logic_vector(31 downto 0); -- active high - sw: in std_logic_vector(31 downto 0); -- active high - led: out std_logic_vector(31 downto 0) := (others => '0'); -- active high - hex: out std_logic_vector(31 downto 0) := (others => '0') -- active high + btn: in std_logic_vector(31 downto 0); + sw: in std_logic_vector(31 downto 0); + led: out std_logic_vector(31 downto 0) := (others => '0'); + seg0: out std_logic_vector(31 downto 0); + seg1: out std_logic_vector(31 downto 0); + seg2: out std_logic_vector(31 downto 0); + seg3: out std_logic_vector(31 downto 0) ); end circuit; diff --git a/relay/src/main.rs b/relay/src/main.rs index 6844508..b30cd34 100644 --- a/relay/src/main.rs +++ b/relay/src/main.rs @@ -46,10 +46,11 @@ struct ClientInput{ #[serde(rename_all = "snake_case")] enum ServerMsg<'a> { Log { stream: &'a str, line: &'a str }, - /// bitfield of 32 leds Led(u32), - /// bitfield of 4 hex displays, 7 segment with decimal - Hex(u32) + Seg0(u32), + Seg1(u32), + Seg2(u32), + Seg3(u32) } async fn ws_handler(socket: WebSocket) { @@ -93,14 +94,13 @@ async fn ws_handler(socket: WebSocket) { Some(Ok(Message::Text(msg))) => { let input = serde_json::from_str::<'_, ClientInput>(&msg)?; use tokio::io::AsyncWriteExt; - process.stdin.write_all(format!("key={}\n", input.buttons).as_bytes()).await?; + process.stdin.write_all(format!("btn={}\n", input.buttons).as_bytes()).await?; process.stdin.write_all(format!("sw={}\n", input.switch).as_bytes()).await?; }, Some(Ok(_)) => {}, Some(Err(err)) => Err(err)?, _ => break, } - } out = sout.next_line() => { match out{ @@ -121,10 +121,16 @@ async fn ws_handler(socket: WebSocket) { err = serr.next_line() => { match err{ Ok(Some(line)) => { - let msg = if let Some(repr) = line.strip_prefix("LED="){ + let msg = if let Some(repr) = line.strip_prefix("led="){ ServerMsg::Led(repr.parse().unwrap_or(0)) - }else if let Some(repr) = line.strip_prefix("HEX="){ - ServerMsg::Hex(repr.parse().unwrap_or(0)) + }else if let Some(repr) = line.strip_prefix("seg0="){ + ServerMsg::Seg0(repr.parse().unwrap_or(0)) + }else if let Some(repr) = line.strip_prefix("seg1="){ + ServerMsg::Seg1(repr.parse().unwrap_or(0)) + }else if let Some(repr) = line.strip_prefix("seg2="){ + ServerMsg::Seg2(repr.parse().unwrap_or(0)) + }else if let Some(repr) = line.strip_prefix("seg3="){ + ServerMsg::Seg3(repr.parse().unwrap_or(0)) }else{ ServerMsg::Log { stream: "stderr", @@ -139,6 +145,10 @@ async fn ws_handler(socket: WebSocket) { } } } + _ = tokio::time::sleep(std::time::Duration::from_millis(30)) => { + use tokio::io::AsyncWriteExt; + process.stdin.write_all("\n".as_bytes()).await?; + } } } Ok(()) diff --git a/relay/src/run.rs b/relay/src/run.rs index a784787..37764d2 100644 --- a/relay/src/run.rs +++ b/relay/src/run.rs @@ -11,7 +11,8 @@ pub struct Process{ pub async fn run(artifact_dir: &Path) -> Result>{ let mut cmd = Command::new("ghdl"); - cmd.args(["-r", "--std=08", "tb", "--stop-delta=2147483647", "--unbuffered"]); + cmd.args(["-r", "--std=08", "tb", "--stop-delta=4294967296", "--unbuffered", "--"]); + cmd.args(std::env::args_os()); cmd.current_dir(artifact_dir); cmd.kill_on_drop(true); diff --git a/relay/ui/app.js b/relay/ui/app.js index dd0907f..6bbcc8d 100644 --- a/relay/ui/app.js +++ b/relay/ui/app.js @@ -69,7 +69,7 @@ function clearLogs() { function resetOutputsVisuals() { // reset LED/HEX visuals to 0 immediately setLeds(0); - setHex(0); + setSeg(0); } function u32BitGet(x, i) { @@ -196,7 +196,7 @@ function buildHex() { } } -function setHex(hexU32) { +function setSeg(hexU32, seg) { const digits = parseHexDigits(hexU32 >>> 0); for (let i = 0; i < 4; i++) { @@ -344,9 +344,27 @@ function connect() { return; } - if (parsed.hex !== undefined) { - const v = (parsed.hex ?? parsed.value ?? parsed[0] ?? parsed["0"] ?? 0) >>> 0; - setHex(v); + if (parsed.seg0 !== undefined) { + const v = (parsed.seg0 ?? parsed.value ?? parsed[0] ?? parsed["0"] ?? 0) >>> 0; + setSeg(v, 0); + return; + } + + if (parsed.seg1 !== undefined) { + const v = (parsed.seg1 ?? parsed.value ?? parsed[0] ?? parsed["0"] ?? 0) >>> 0; + setSeg(v, 1); + return; + } + + if (parsed.seg2 !== undefined) { + const v = (parsed.seg3 ?? parsed.value ?? parsed[0] ?? parsed["0"] ?? 0) >>> 0; + setSeg(v, 2); + return; + } + + if (parsed.seg3 !== undefined) { + const v = (parsed.seg3 ?? parsed.value ?? parsed[0] ?? parsed["0"] ?? 0) >>> 0; + setSeg(v, 3); return; } diff --git a/rtl/tb.vhdl b/rtl/tb.vhdl index 114633e..7be37eb 100644 --- a/rtl/tb.vhdl +++ b/rtl/tb.vhdl @@ -2,20 +2,22 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; + entity tb is + end entity; architecture sim of tb is signal clk : std_logic := '0'; - signal key : std_logic_vector(31 downto 0) := (others => '0'); + signal btn : std_logic_vector(31 downto 0) := (others => '0'); signal sw : std_logic_vector(31 downto 0) := (others => '0'); - signal led : std_logic_vector(31 downto 0) := (others => '0'); - signal hex : std_logic_vector(31 downto 0) := (others => '0'); + signal seg0 : std_logic_vector(31 downto 0) := (others => '0'); + signal seg1 : std_logic_vector(31 downto 0) := (others => '0'); + signal seg2 : std_logic_vector(31 downto 0) := (others => '0'); + signal seg3 : std_logic_vector(31 downto 0) := (others => '0'); - -- Foreign subprograms MUST be declared in the declarative region (here), - -- and MUST have a body (even dummy) to satisfy VHDL. procedure ffi_init is begin end procedure; @@ -29,13 +31,13 @@ architecture sim of tb is attribute foreign of ffi_get_sw : function is "VHPIDIRECT ffi_get_sw"; - function ffi_get_key return integer is + function ffi_get_btn return integer is begin return 0; end function; - attribute foreign of ffi_get_key : function is "VHPIDIRECT ffi_get_key"; + attribute foreign of ffi_get_btn : function is "VHPIDIRECT ffi_get_btn"; - procedure ffi_set_outputs(led_i : integer; hex_i : integer) is + procedure ffi_set_outputs(led_i: integer; seg0_i: integer; seg1_i: integer; seg2_i: integer; seg3_i: integer) is begin end procedure; attribute foreign of ffi_set_outputs : procedure is @@ -58,10 +60,13 @@ begin dut: entity work.circuit port map ( clk => clk, - key => key, + btn => btn, sw => sw, led => led, - hex => hex + seg0 => seg0, + seg1 => seg1, + seg2 => seg2, + seg3 => seg3 ); -- 500 Hz clock (2 ms period) @@ -69,7 +74,7 @@ begin process variable sw_i : integer; - variable key_i : integer; + variable btn_i : integer; begin ffi_init; wait for 0 ns; @@ -79,14 +84,17 @@ begin wait for 0 ns; sw_i := ffi_get_sw; - key_i := ffi_get_key; + btn_i := ffi_get_btn; sw <= std_logic_vector(to_signed(sw_i, 32)); - key <= std_logic_vector(to_signed(key_i, 32)); + btn <= std_logic_vector(to_signed(btn_i, 32)); ffi_set_outputs( to_integer(unsigned(clean_slv(led))), - to_integer(unsigned(clean_slv(hex))) + to_integer(unsigned(clean_slv(seg0))), + to_integer(unsigned(clean_slv(seg1))), + to_integer(unsigned(clean_slv(seg2))), + to_integer(unsigned(clean_slv(seg3))) ); end loop; end process; diff --git a/run_relay.sh b/run_relay.sh index 2846bf1..3d19296 100755 --- a/run_relay.sh +++ b/run_relay.sh @@ -7,5 +7,5 @@ popd >/dev/null pushd relay >/dev/null -cargo run --release -popd >/dev/null \ No newline at end of file +cargo run --release -- "$@" +popd >/dev/null