mirror of
https://github.com/ParkerTenBroeck/hdl_sim.git
synced 2026-06-07 05:28:45 -04:00
updated
This commit is contained in:
parent
b4841e6f40
commit
e6c5947949
9 changed files with 156 additions and 63 deletions
|
|
@ -1,7 +1,11 @@
|
|||
[package]
|
||||
name = "vhdl_ui"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
|
||||
[lints.clippy]
|
||||
identity_op = "allow"
|
||||
|
|
@ -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::<u32>() {
|
||||
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::<u32>() {
|
||||
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::<u64>(){
|
||||
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));
|
||||
#[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)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ pub struct Process{
|
|||
|
||||
pub async fn run(artifact_dir: &Path) -> Result<Process, Box<dyn std::error::Error + Send + Sync>>{
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
36
rtl/tb.vhdl
36
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;
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ popd >/dev/null
|
|||
|
||||
|
||||
pushd relay >/dev/null
|
||||
cargo run --release
|
||||
cargo run --release -- "$@"
|
||||
popd >/dev/null
|
||||
Loading…
Add table
Add a link
Reference in a new issue