mirror of
https://github.com/ParkerTenBroeck/hdl_sim.git
synced 2026-06-07 05:28:45 -04:00
updated segment interface
This commit is contained in:
parent
4773ab5e9e
commit
5746846896
9 changed files with 183 additions and 195 deletions
|
|
@ -9,10 +9,8 @@ port (
|
||||||
btn: in std_logic_vector(31 downto 0);
|
btn: in std_logic_vector(31 downto 0);
|
||||||
sw: 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');
|
led: out std_logic_vector(31 downto 0) := (others => '0');
|
||||||
seg0: out std_logic_vector(31 downto 0);
|
segv: out std_logic_vector(31 downto 0);
|
||||||
seg1: out std_logic_vector(31 downto 0);
|
segs: 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;
|
end circuit;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
use std::{
|
use std::{
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
sync::atomic::{AtomicU8, AtomicU32, AtomicU64, Ordering},
|
sync::atomic::{AtomicU32, AtomicU64, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SimState{
|
pub struct SimState {
|
||||||
switch: AtomicU32,
|
switch: AtomicU32,
|
||||||
button: AtomicU32,
|
button: AtomicU32,
|
||||||
led: AtomicU32,
|
led: AtomicU32,
|
||||||
segs: [AtomicU32; 4],
|
/// represents 4 segments (each byte being one segment)
|
||||||
updated: AtomicU8,
|
segs: [AtomicU32; 8],
|
||||||
|
updated: AtomicU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
static STATE: SimState = SimState{
|
static STATE: SimState = SimState {
|
||||||
switch: AtomicU32::new(0),
|
switch: AtomicU32::new(0),
|
||||||
button: AtomicU32::new(0),
|
button: AtomicU32::new(0),
|
||||||
led: AtomicU32::new(0),
|
led: AtomicU32::new(0),
|
||||||
segs: [const{AtomicU32::new(0)}; 4],
|
segs: [const { AtomicU32::new(0) }; 8],
|
||||||
updated: AtomicU8::new(0)
|
updated: AtomicU32::new(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
/// default 1 ms
|
/// default 1 ms
|
||||||
|
|
@ -34,13 +35,15 @@ fn client() {
|
||||||
if let Ok(n) = v.parse::<u32>() {
|
if let Ok(n) = v.parse::<u32>() {
|
||||||
STATE.button.store(n, Ordering::Relaxed);
|
STATE.button.store(n, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
} else if let value = STATE.updated.swap(0, Ordering::Relaxed) && value!=0 {
|
} else if let value = STATE.updated.swap(0, Ordering::Relaxed)
|
||||||
if (value >> 0) & 1 == 1{
|
&& value != 0
|
||||||
|
{
|
||||||
|
if (value >> 0) & 1 == 1 {
|
||||||
eprintln!("led={}", STATE.led.load(Ordering::Relaxed));
|
eprintln!("led={}", STATE.led.load(Ordering::Relaxed));
|
||||||
}
|
}
|
||||||
for i in 0..4{
|
for i in 0..STATE.segs.len() {
|
||||||
if (value >> (i+1)) & 1 == 1{
|
if (value >> (i + 1)) & 1 == 1 {
|
||||||
eprintln!("seg{i}={}", STATE.segs[i].load(Ordering::Relaxed));
|
eprintln!("seg={};{i}", STATE.segs[i].load(Ordering::Relaxed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,12 +52,15 @@ fn client() {
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn ffi_init() {
|
pub extern "C" fn ffi_init() {
|
||||||
std::thread::Builder::new().name("client".into()).spawn(client).expect("Failed to spawn client thread");
|
std::thread::Builder::new()
|
||||||
for arg in std::env::args(){
|
.name("client".into())
|
||||||
if let Some(arg) = arg.strip_prefix("--cycle_sleep").or(arg.strip_prefix("-c")){
|
.spawn(client)
|
||||||
if let Ok(nanos) = arg.trim().parse::<u64>(){
|
.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);
|
SLEEP_NANOS.store(nanos, Ordering::Relaxed);
|
||||||
}else{
|
} else {
|
||||||
eprintln!("cycle sleep(ns) failed to parse");
|
eprintln!("cycle sleep(ns) failed to parse");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,22 +79,19 @@ pub extern "C" fn ffi_get_btn() -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn ffi_set_outputs(led: u32, seg0: u32, seg1: u32, seg2: u32, seg3: u32) {
|
pub extern "C" fn ffi_set_outputs(led: u32, segv: u32, segs: u32) {
|
||||||
let o_led = STATE.led.swap(led, Ordering::Relaxed) != led;
|
let o_led = STATE.led.swap(led, Ordering::Relaxed) != led;
|
||||||
|
|
||||||
let o_seg0 = STATE.segs[0].swap(seg0, Ordering::Relaxed) != seg0;
|
let mut to_set = o_led as u32;
|
||||||
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
|
if let Some(place) = STATE.segs.get(segs as usize) {
|
||||||
| (o_seg0 as u8) << 1
|
let seg = place.swap(segv, Ordering::Relaxed) != segv;
|
||||||
| (o_seg1 as u8) << 2
|
to_set |= (seg as u32 + 1) << segs
|
||||||
| (o_seg2 as u8) << 3
|
}
|
||||||
| (o_seg3 as u8) << 4;
|
|
||||||
|
|
||||||
STATE.updated.fetch_or(to_set, Ordering::Relaxed);
|
STATE.updated.fetch_or(to_set, Ordering::Relaxed);
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_nanos(SLEEP_NANOS.load(Ordering::Relaxed)));
|
std::thread::sleep(std::time::Duration::from_nanos(
|
||||||
|
SLEEP_NANOS.load(Ordering::Relaxed),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap, ffi::OsStr, ops::Deref, path::{Path, PathBuf}
|
collections::HashMap,
|
||||||
|
ffi::OsStr,
|
||||||
|
ops::Deref,
|
||||||
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use tokio::process::{Child, Command};
|
use tokio::process::{Child, Command};
|
||||||
|
|
||||||
|
|
@ -46,9 +49,7 @@ impl AsRef<Path> for TempDir {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn copy_and_build(
|
pub async fn copy_and_build(files: HashMap<String, String>) -> HResult<TempDir> {
|
||||||
files: HashMap<String, String>,
|
|
||||||
) -> HResult<TempDir> {
|
|
||||||
use std::hash::*;
|
use std::hash::*;
|
||||||
let mut hasher = std::hash::DefaultHasher::default();
|
let mut hasher = std::hash::DefaultHasher::default();
|
||||||
for (key, value) in &files {
|
for (key, value) in &files {
|
||||||
|
|
@ -73,8 +74,7 @@ pub async fn copy_and_build(
|
||||||
Ok(work_dir)
|
Ok(work_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn build(build: &Path, src: &Path) -> HResult<()> {
|
||||||
pub async fn build(build: &Path, src: &Path) -> HResult<()>{
|
|
||||||
std::fs::create_dir_all(build)?;
|
std::fs::create_dir_all(build)?;
|
||||||
let embedded_lib_path = build.join("libvhdl_conn.a");
|
let embedded_lib_path = build.join("libvhdl_conn.a");
|
||||||
let embedded_tb_path = build.join("tb.vhdl");
|
let embedded_tb_path = build.join("tb.vhdl");
|
||||||
|
|
@ -85,7 +85,7 @@ pub async fn build(build: &Path, src: &Path) -> HResult<()>{
|
||||||
cmd.kill_on_drop(true);
|
cmd.kill_on_drop(true);
|
||||||
cmd.args(["-i", "-g", "--std=08"]);
|
cmd.args(["-i", "-g", "--std=08"]);
|
||||||
|
|
||||||
for file in src.read_dir().unwrap().flatten(){
|
for file in src.read_dir().unwrap().flatten() {
|
||||||
if Path::new(&file.file_name()).extension() == Some(OsStr::new("vhdl")) {
|
if Path::new(&file.file_name()).extension() == Some(OsStr::new("vhdl")) {
|
||||||
cmd.arg(file.path().canonicalize()?);
|
cmd.arg(file.path().canonicalize()?);
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +99,6 @@ pub async fn build(build: &Path, src: &Path) -> HResult<()>{
|
||||||
cmd.current_dir(build);
|
cmd.current_dir(build);
|
||||||
ensure_ok(cmd.spawn()?).await?;
|
ensure_ok(cmd.spawn()?).await?;
|
||||||
|
|
||||||
|
|
||||||
let mut cmd = Command::new("ghdl");
|
let mut cmd = Command::new("ghdl");
|
||||||
cmd.kill_on_drop(true);
|
cmd.kill_on_drop(true);
|
||||||
cmd.args(["-m", "--std=08"]);
|
cmd.args(["-m", "--std=08"]);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,10 @@ async fn serve_styles() -> impl IntoResponse {
|
||||||
|
|
||||||
async fn serve_app_js() -> impl IntoResponse {
|
async fn serve_app_js() -> impl IntoResponse {
|
||||||
(
|
(
|
||||||
[(header::CONTENT_TYPE, "application/javascript; charset=utf-8")],
|
[(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
"application/javascript; charset=utf-8",
|
||||||
|
)],
|
||||||
UI_APP_JS,
|
UI_APP_JS,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +94,8 @@ fn parse_config_from_args() -> Result<Config, String> {
|
||||||
}
|
}
|
||||||
"--help" | "-h" => {
|
"--help" | "-h" => {
|
||||||
return Err(
|
return Err(
|
||||||
"usage: relay [--ip <ip>] [--port <port>] [--update-ms <ms>] [--workspace]".into(),
|
"usage: relay [--ip <ip>] [--port <port>] [--update-ms <ms>] [--workspace]"
|
||||||
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -116,18 +120,12 @@ async fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let update_interval = Duration::from_millis(cfg.update_ms);
|
let update_interval = Duration::from_millis(cfg.update_ms);
|
||||||
let mut app = Router::new()
|
let mut app =
|
||||||
.route(
|
Router::new()
|
||||||
"/",
|
.route("/", get(move || async move { serve_index().await }))
|
||||||
get(move || {
|
|
||||||
async move { serve_index().await }
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.route(
|
.route(
|
||||||
"/index.html",
|
"/index.html",
|
||||||
get(move || {
|
get(move || async move { serve_index().await }),
|
||||||
async move { serve_index().await }
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.route("/styles.css", get(serve_styles))
|
.route("/styles.css", get(serve_styles))
|
||||||
.route("/app.js", get(serve_app_js))
|
.route("/app.js", get(serve_app_js))
|
||||||
|
|
@ -183,10 +181,7 @@ pub enum ServerMsg<'a> {
|
||||||
Start,
|
Start,
|
||||||
Stop,
|
Stop,
|
||||||
Led(u32),
|
Led(u32),
|
||||||
Seg0(u32),
|
Seg { value: u32, index: u32 },
|
||||||
Seg1(u32),
|
|
||||||
Seg2(u32),
|
|
||||||
Seg3(u32),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type HResult<T> = Result<T, Box<dyn std::error::Error + Sync + Send>>;
|
pub type HResult<T> = Result<T, Box<dyn std::error::Error + Sync + Send>>;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
use axum::{
|
use axum::extract::ws::{Message, WebSocket};
|
||||||
extract::ws::{Message, WebSocket},
|
use futures_util::{SinkExt, StreamExt};
|
||||||
};
|
|
||||||
use futures_util::{
|
|
||||||
SinkExt, StreamExt,
|
|
||||||
};
|
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncBufReadExt, BufReader}, time::Instant,
|
io::{AsyncBufReadExt, BufReader},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ClientMsg, HResult, ServerMsg, build, run};
|
use crate::{ClientMsg, HResult, ServerMsg, build, run};
|
||||||
|
|
@ -22,21 +19,24 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let artifact_dir = match build::copy_and_build(files).await {
|
||||||
let artifact_dir = match build::copy_and_build(files).await{
|
|
||||||
Ok(dir) => dir,
|
Ok(dir) => dir,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
_ = sender.send(Message::Text(format!("Failed to build: {err}").into())).await;
|
_ = sender
|
||||||
|
.send(Message::Text(format!("Failed to build: {err}").into()))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut process = match run::run(&artifact_dir).await{
|
let mut process = match run::run(&artifact_dir).await {
|
||||||
Ok(process) => process,
|
Ok(process) => process,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
_ = sender.send(Message::Text(format!("Failed to run: {err}").into())).await;
|
_ = sender
|
||||||
|
.send(Message::Text(format!("Failed to run: {err}").into()))
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
let mut sout = BufReader::new(process.stdout).lines();
|
let mut sout = BufReader::new(process.stdout).lines();
|
||||||
let mut serr = BufReader::new(process.stderr).lines();
|
let mut serr = BufReader::new(process.stderr).lines();
|
||||||
|
|
@ -89,14 +89,12 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
||||||
Ok(Some(line)) => {
|
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))
|
ServerMsg::Led(repr.parse().unwrap_or(0))
|
||||||
}else if let Some(repr) = line.strip_prefix("seg0="){
|
}else if let Some(repr) = line.strip_prefix("seg=")
|
||||||
ServerMsg::Seg0(repr.parse().unwrap_or(0))
|
&& let Some((val, idx)) = repr.split_once(";") {
|
||||||
}else if let Some(repr) = line.strip_prefix("seg1="){
|
ServerMsg::Seg{
|
||||||
ServerMsg::Seg1(repr.parse().unwrap_or(0))
|
value: val.parse().unwrap_or(0),
|
||||||
}else if let Some(repr) = line.strip_prefix("seg2="){
|
index: idx.parse().unwrap_or(0)
|
||||||
ServerMsg::Seg2(repr.parse().unwrap_or(0))
|
}
|
||||||
}else if let Some(repr) = line.strip_prefix("seg3="){
|
|
||||||
ServerMsg::Seg3(repr.parse().unwrap_or(0))
|
|
||||||
}else{
|
}else{
|
||||||
ServerMsg::Log {
|
ServerMsg::Log {
|
||||||
stream: "stderr",
|
stream: "stderr",
|
||||||
|
|
@ -121,10 +119,10 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
||||||
Ok(())
|
Ok(())
|
||||||
}.await;
|
}.await;
|
||||||
|
|
||||||
match result{
|
match result {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
_ = sender.send(Message::Text(format!("{err}").into())).await;
|
_ = sender.send(Message::Text(format!("{err}").into())).await;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
Error, extract::ws::{Message, WebSocket}
|
Error,
|
||||||
|
extract::ws::{Message, WebSocket},
|
||||||
};
|
};
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
SinkExt, StreamExt,
|
SinkExt, StreamExt,
|
||||||
|
|
@ -8,12 +9,12 @@ use futures_util::{
|
||||||
use std::{path::PathBuf, time::Duration};
|
use std::{path::PathBuf, time::Duration};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncBufReadExt, BufReader, Lines},
|
io::{AsyncBufReadExt, BufReader, Lines},
|
||||||
process::{Child, ChildStderr, ChildStdin, ChildStdout}, time::Instant,
|
process::{Child, ChildStderr, ChildStdin, ChildStdout},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ClientMsg, ServerMsg, build, run};
|
use crate::{ClientMsg, ServerMsg, build, run};
|
||||||
|
|
||||||
|
|
||||||
struct Process {
|
struct Process {
|
||||||
process: Child,
|
process: Child,
|
||||||
|
|
||||||
|
|
@ -22,7 +23,6 @@ struct Process {
|
||||||
stdin: ChildStdin,
|
stdin: ChildStdin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Handler {
|
struct Handler {
|
||||||
sender: SplitSink<WebSocket, Message>,
|
sender: SplitSink<WebSocket, Message>,
|
||||||
receiver: SplitStream<WebSocket>,
|
receiver: SplitStream<WebSocket>,
|
||||||
|
|
@ -36,7 +36,6 @@ struct Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
|
|
||||||
fn workspace(socket: WebSocket, build: PathBuf, src: PathBuf, refresh_time: Duration) -> Self {
|
fn workspace(socket: WebSocket, build: PathBuf, src: PathBuf, refresh_time: Duration) -> Self {
|
||||||
let (sender, receiver) = socket.split();
|
let (sender, receiver) = socket.split();
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -55,7 +54,12 @@ impl Handler {
|
||||||
stream: "stdout",
|
stream: "stdout",
|
||||||
line: msg.as_ref(),
|
line: msg.as_ref(),
|
||||||
};
|
};
|
||||||
_ = self.sender.send(Message::Text(serde_json::to_string(&msg).unwrap_or_default().into())).await;
|
_ = self
|
||||||
|
.sender
|
||||||
|
.send(Message::Text(
|
||||||
|
serde_json::to_string(&msg).unwrap_or_default().into(),
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn eprint(&mut self, msg: impl AsRef<str>) {
|
pub async fn eprint(&mut self, msg: impl AsRef<str>) {
|
||||||
|
|
@ -64,36 +68,51 @@ impl Handler {
|
||||||
stream: "stderr",
|
stream: "stderr",
|
||||||
line: msg.as_ref(),
|
line: msg.as_ref(),
|
||||||
};
|
};
|
||||||
_ = self.sender.send(Message::Text(serde_json::to_string(&msg).unwrap_or_default().into())).await;
|
_ = self
|
||||||
|
.sender
|
||||||
|
.send(Message::Text(
|
||||||
|
serde_json::to_string(&msg).unwrap_or_default().into(),
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stop_process(&mut self) {
|
async fn stop_process(&mut self) {
|
||||||
self.process = None;
|
self.process = None;
|
||||||
_ = self.sender.send(Message::Text(serde_json::to_string(&ServerMsg::Stop).unwrap_or_default().into())).await;
|
_ = self
|
||||||
|
.sender
|
||||||
|
.send(Message::Text(
|
||||||
|
serde_json::to_string(&ServerMsg::Stop)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_websocket_msg(&mut self, msg: ClientMsg) {
|
async fn handle_websocket_msg(&mut self, msg: ClientMsg) {
|
||||||
match msg{
|
match msg {
|
||||||
ClientMsg::Start => self.run_program().await,
|
ClientMsg::Start => self.run_program().await,
|
||||||
ClientMsg::Stop => self.stop_process().await,
|
ClientMsg::Stop => self.stop_process().await,
|
||||||
ClientMsg::Input { switch, buttons } => {
|
ClientMsg::Input { switch, buttons } => {
|
||||||
if let Some(process) = &mut self.process{
|
if let Some(process) = &mut self.process {
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
_ = process.stdin.write_all(format!("btn={}\n", buttons).as_bytes()).await;
|
_ = process
|
||||||
_ = process.stdin.write_all(format!("sw={}\n", switch).as_bytes()).await;
|
.stdin
|
||||||
|
.write_all(format!("btn={}\n", buttons).as_bytes())
|
||||||
|
.await;
|
||||||
|
_ = process
|
||||||
|
.stdin
|
||||||
|
.write_all(format!("sw={}\n", switch).as_bytes())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_websocket_receive(
|
async fn handle_websocket_receive(&mut self, msg: Option<Result<Message, Error>>) -> bool {
|
||||||
&mut self,
|
|
||||||
msg: Option<Result<Message, Error>>,
|
|
||||||
) -> bool {
|
|
||||||
match msg {
|
match msg {
|
||||||
Some(Ok(Message::Close(_))) => true,
|
Some(Ok(Message::Close(_))) => true,
|
||||||
Some(Ok(Message::Text(msg))) => {
|
Some(Ok(Message::Text(msg))) => {
|
||||||
let msg = match serde_json::from_str(msg.as_str()){
|
let msg = match serde_json::from_str(msg.as_str()) {
|
||||||
Ok(msg) => msg,
|
Ok(msg) => msg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.eprint(format!("Client message error {err}")).await;
|
self.eprint(format!("Client message error {err}")).await;
|
||||||
|
|
@ -107,7 +126,7 @@ impl Handler {
|
||||||
Some(Err(err)) => {
|
Some(Err(err)) => {
|
||||||
self.eprint(format!("Client websocket error {err}")).await;
|
self.eprint(format!("Client websocket error {err}")).await;
|
||||||
true
|
true
|
||||||
},
|
}
|
||||||
None => true,
|
None => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +134,7 @@ impl Handler {
|
||||||
async fn run(&mut self) {
|
async fn run(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
if let Some(process) = &mut self.process {
|
if let Some(process) = &mut self.process {
|
||||||
if let Ok(Some(_)) = process.process.try_wait(){
|
if let Ok(Some(_)) = process.process.try_wait() {
|
||||||
self.stop_process().await;
|
self.stop_process().await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -143,14 +162,12 @@ impl Handler {
|
||||||
Ok(Some(line)) => {
|
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))
|
ServerMsg::Led(repr.parse().unwrap_or(0))
|
||||||
}else if let Some(repr) = line.strip_prefix("seg0="){
|
}else if let Some(repr) = line.strip_prefix("seg=")
|
||||||
ServerMsg::Seg0(repr.parse().unwrap_or(0))
|
&& let Some((val, idx)) = repr.split_once(";") {
|
||||||
}else if let Some(repr) = line.strip_prefix("seg1="){
|
ServerMsg::Seg{
|
||||||
ServerMsg::Seg1(repr.parse().unwrap_or(0))
|
value: val.parse().unwrap_or(0),
|
||||||
}else if let Some(repr) = line.strip_prefix("seg2="){
|
index: idx.parse().unwrap_or(0)
|
||||||
ServerMsg::Seg2(repr.parse().unwrap_or(0))
|
}
|
||||||
}else if let Some(repr) = line.strip_prefix("seg3="){
|
|
||||||
ServerMsg::Seg3(repr.parse().unwrap_or(0))
|
|
||||||
}else{
|
}else{
|
||||||
self.eprint(line).await;
|
self.eprint(line).await;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -170,9 +187,9 @@ impl Handler {
|
||||||
_ = process.stdin.write_all("\n".as_bytes()).await;
|
_ = process.stdin.write_all("\n".as_bytes()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
let res = self.receiver.next().await;
|
let res = self.receiver.next().await;
|
||||||
if self.handle_websocket_receive(res).await{
|
if self.handle_websocket_receive(res).await {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -180,9 +197,8 @@ impl Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_program(&mut self) {
|
async fn run_program(&mut self) {
|
||||||
|
|
||||||
match build::build(&self.build_dir, &self.src_dir).await {
|
match build::build(&self.build_dir, &self.src_dir).await {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
_ = self.eprint(format!("Failed to build: {err}")).await;
|
_ = self.eprint(format!("Failed to build: {err}")).await;
|
||||||
return;
|
return;
|
||||||
|
|
@ -200,13 +216,25 @@ impl Handler {
|
||||||
let stderr = BufReader::new(process.stderr).lines();
|
let stderr = BufReader::new(process.stderr).lines();
|
||||||
let stdin = process.stdin;
|
let stdin = process.stdin;
|
||||||
|
|
||||||
_ = self.sender.send(Message::Text(serde_json::to_string(&ServerMsg::Start).unwrap_or_default().into())).await;
|
_ = self
|
||||||
self.process = Some(
|
.sender
|
||||||
Process { process: process.child, stderr, stdout, stdin }
|
.send(Message::Text(
|
||||||
)
|
serde_json::to_string(&ServerMsg::Start)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
self.process = Some(Process {
|
||||||
|
process: process.child,
|
||||||
|
stderr,
|
||||||
|
stdout,
|
||||||
|
stdin,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
||||||
Handler::workspace(socket, "./target".into(), "./src".into(), refresh_time).run().await;
|
Handler::workspace(socket, "./target".into(), "./src".into(), refresh_time)
|
||||||
|
.run()
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,8 @@ port (
|
||||||
btn: in std_logic_vector(31 downto 0);
|
btn: in std_logic_vector(31 downto 0);
|
||||||
sw: 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');
|
led: out std_logic_vector(31 downto 0) := (others => '0');
|
||||||
seg0: out std_logic_vector(31 downto 0);
|
segv: out std_logic_vector(31 downto 0);
|
||||||
seg1: out std_logic_vector(31 downto 0);
|
segs: 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;
|
end circuit;
|
||||||
|
|
||||||
|
|
@ -250,6 +248,8 @@ class OutputController {
|
||||||
constructor({ ledRow, hexRow }) {
|
constructor({ ledRow, hexRow }) {
|
||||||
this.ledRow = ledRow;
|
this.ledRow = ledRow;
|
||||||
this.hexRow = hexRow;
|
this.hexRow = hexRow;
|
||||||
|
// Each backend segN value encodes 4 seven-segment displays, one byte each.
|
||||||
|
this.segDisplaysPerGroup = 4;
|
||||||
|
|
||||||
this.ledEls = Array.from(this.ledRow.querySelectorAll(".led[data-bit]"));
|
this.ledEls = Array.from(this.ledRow.querySelectorAll(".led[data-bit]"));
|
||||||
this.segDisplays = Array.from(this.hexRow.querySelectorAll(".sevenSeg[data-digit]"));
|
this.segDisplays = Array.from(this.hexRow.querySelectorAll(".sevenSeg[data-digit]"));
|
||||||
|
|
@ -275,37 +275,12 @@ class OutputController {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let handledSegment = false;
|
if (parsed.seg !== undefined){
|
||||||
|
const groupBytes = parseSegRowBytes(parsed.seg.value);
|
||||||
// Row mapping:
|
this.setSegGroup(parsed.seg.index, groupBytes);
|
||||||
// seg0 -> displays 0..7
|
|
||||||
// seg1 -> displays 8..15
|
|
||||||
// seg2 -> displays 16..23
|
|
||||||
// seg3 -> displays 24..31
|
|
||||||
for (let row = 0; row < 4; row += 1) {
|
|
||||||
const key = `seg${row}`;
|
|
||||||
if (parsed[key] === undefined) continue;
|
|
||||||
|
|
||||||
const rowBytes = parseSegRowBytes(parsed[key]);
|
|
||||||
if (!rowBytes) continue;
|
|
||||||
|
|
||||||
this.setSegRow(row, rowBytes);
|
|
||||||
handledSegment = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backward-compat path for a single 32-bit value (fills first 4 displays).
|
|
||||||
if (!handledSegment && parsed.seg !== undefined) {
|
|
||||||
const bytes = parseHexDigits(Number(parsed.seg) >>> 0);
|
|
||||||
this.setSegRow(0, bytes);
|
|
||||||
handledSegment = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handledSegment) {
|
|
||||||
this.renderAllSegments();
|
this.renderAllSegments();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setLeds(bitsU32) {
|
setLeds(bitsU32) {
|
||||||
|
|
@ -329,9 +304,9 @@ class OutputController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSegRow(rowIndex, bytes) {
|
setSegGroup(groupIndex, bytes) {
|
||||||
const base = rowIndex * 8;
|
const base = groupIndex * this.segDisplaysPerGroup;
|
||||||
for (let i = 0; i < 8; i += 1) {
|
for (let i = 0; i < this.segDisplaysPerGroup; i += 1) {
|
||||||
const dst = base + i;
|
const dst = base + i;
|
||||||
if (dst >= this.segBytes.length) break;
|
if (dst >= this.segBytes.length) break;
|
||||||
this.segBytes[dst] = bytes[i] & 0xff;
|
this.segBytes[dst] = bytes[i] & 0xff;
|
||||||
|
|
|
||||||
20
rtl/tb.vhdl
20
rtl/tb.vhdl
|
|
@ -12,10 +12,8 @@ architecture sim of tb is
|
||||||
signal btn : 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 sw : std_logic_vector(31 downto 0) := (others => '0');
|
||||||
signal led : std_logic_vector(31 downto 0) := (others => '0');
|
signal led : std_logic_vector(31 downto 0) := (others => '0');
|
||||||
signal seg0 : std_logic_vector(31 downto 0) := (others => '0');
|
signal segv : std_logic_vector(31 downto 0) := (others => '0');
|
||||||
signal seg1 : std_logic_vector(31 downto 0) := (others => '0');
|
signal segs : 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');
|
|
||||||
|
|
||||||
|
|
||||||
procedure ffi_init is
|
procedure ffi_init is
|
||||||
|
|
@ -37,7 +35,7 @@ architecture sim of tb is
|
||||||
end function;
|
end function;
|
||||||
attribute foreign of ffi_get_btn : function is "VHPIDIRECT ffi_get_btn";
|
attribute foreign of ffi_get_btn : function is "VHPIDIRECT ffi_get_btn";
|
||||||
|
|
||||||
procedure ffi_set_outputs(led_i: integer; seg0_i: integer; seg1_i: integer; seg2_i: integer; seg3_i: integer) is
|
procedure ffi_set_outputs(led_i: integer; segv_i: integer; segs_i: integer) is
|
||||||
begin
|
begin
|
||||||
end procedure;
|
end procedure;
|
||||||
attribute foreign of ffi_set_outputs : procedure is
|
attribute foreign of ffi_set_outputs : procedure is
|
||||||
|
|
@ -63,10 +61,8 @@ begin
|
||||||
btn => btn,
|
btn => btn,
|
||||||
sw => sw,
|
sw => sw,
|
||||||
led => led,
|
led => led,
|
||||||
seg0 => seg0,
|
segv => segv,
|
||||||
seg1 => seg1,
|
segs => segs
|
||||||
seg2 => seg2,
|
|
||||||
seg3 => seg3
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 500 Hz clock (2 ms period)
|
-- 500 Hz clock (2 ms period)
|
||||||
|
|
@ -91,10 +87,8 @@ begin
|
||||||
|
|
||||||
ffi_set_outputs(
|
ffi_set_outputs(
|
||||||
to_integer(signed(clean_slv(led))),
|
to_integer(signed(clean_slv(led))),
|
||||||
to_integer(signed(clean_slv(seg0))),
|
to_integer(signed(clean_slv(segv))),
|
||||||
to_integer(signed(clean_slv(seg1))),
|
to_integer(signed(clean_slv(segs)))
|
||||||
to_integer(signed(clean_slv(seg2))),
|
|
||||||
to_integer(signed(clean_slv(seg3)))
|
|
||||||
);
|
);
|
||||||
end loop;
|
end loop;
|
||||||
end process;
|
end process;
|
||||||
|
|
|
||||||
14
src/cpu.vhdl
14
src/cpu.vhdl
|
|
@ -9,10 +9,8 @@ port (
|
||||||
btn: in std_logic_vector(31 downto 0);
|
btn: in std_logic_vector(31 downto 0);
|
||||||
sw: in std_logic_vector(31 downto 0);
|
sw: in std_logic_vector(31 downto 0);
|
||||||
led: out std_logic_vector(31 downto 0);
|
led: out std_logic_vector(31 downto 0);
|
||||||
seg0: out std_logic_vector(31 downto 0);
|
segv: out std_logic_vector(31 downto 0);
|
||||||
seg1: out std_logic_vector(31 downto 0);
|
segs: 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;
|
end circuit;
|
||||||
|
|
||||||
|
|
@ -71,11 +69,11 @@ architecture description of circuit is
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|
||||||
seg0(6 downto 0) <= dec7seg(reg_out(7 downto 4));
|
segs(6 downto 0) <= dec7seg(reg_out(7 downto 4));
|
||||||
seg0(14 downto 8) <= dec7seg(reg_out(3 downto 0));
|
segs(14 downto 8) <= dec7seg(reg_out(3 downto 0));
|
||||||
|
|
||||||
seg0(22 downto 16) <= dec7seg(reg_pc(7 downto 4));
|
segs(22 downto 16) <= dec7seg(reg_pc(7 downto 4));
|
||||||
seg0(30 downto 24) <= dec7seg(reg_pc(3 downto 0));
|
segs(30 downto 24) <= dec7seg(reg_pc(3 downto 0));
|
||||||
|
|
||||||
led(7 downto 0) <= std_logic_vector(reg_a);
|
led(7 downto 0) <= std_logic_vector(reg_a);
|
||||||
led(23 downto 16) <= std_logic_vector(reg_b);
|
led(23 downto 16) <= std_logic_vector(reg_b);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue