mirror of
https://github.com/ParkerTenBroeck/hdl_sim.git
synced 2026-06-07 05:28:45 -04:00
added webUI
This commit is contained in:
parent
a266096f32
commit
0201990df8
16 changed files with 2590 additions and 341 deletions
|
|
@ -11,7 +11,7 @@ pub struct SimState{
|
||||||
}
|
}
|
||||||
|
|
||||||
static STATE: SimState = SimState{
|
static STATE: SimState = SimState{
|
||||||
switch: AtomicU32::new(0),
|
switch: AtomicU32::new(512),
|
||||||
button: AtomicU32::new(0),
|
button: AtomicU32::new(0),
|
||||||
led: AtomicU32::new(0),
|
led: AtomicU32::new(0),
|
||||||
hex: AtomicU32::new(0),
|
hex: AtomicU32::new(0),
|
||||||
|
|
@ -52,9 +52,10 @@ pub extern "C" fn ffi_get_key() -> u32 {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn ffi_set_outputs(led: u32, hex: u32) {
|
pub extern "C" fn ffi_set_outputs(led: u32, hex: u32) {
|
||||||
if STATE.led.swap(led, Ordering::Relaxed) != led{
|
if STATE.led.swap(led, Ordering::Relaxed) != led{
|
||||||
println!("LED={:#x?}", STATE.led.load(Ordering::Relaxed))
|
eprintln!("LED={}", STATE.led.load(Ordering::Relaxed))
|
||||||
}
|
}
|
||||||
if STATE.hex.swap(hex, Ordering::Relaxed) != hex{
|
if STATE.hex.swap(hex, Ordering::Relaxed) != hex{
|
||||||
println!("HEX={:#x?}", STATE.hex.load(Ordering::Relaxed))
|
eprintln!("HEX={}", STATE.hex.load(Ordering::Relaxed))
|
||||||
}
|
}
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||||
}
|
}
|
||||||
1
relay/.gitignore
vendored
Normal file
1
relay/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
928
relay/Cargo.lock
generated
Normal file
928
relay/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,928 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||||
|
dependencies = [
|
||||||
|
"axum-core",
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"form_urlencoded",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"itoa",
|
||||||
|
"matchit",
|
||||||
|
"memchr",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"serde_core",
|
||||||
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sha1",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tokio-tungstenite",
|
||||||
|
"tower",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-core"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"mime",
|
||||||
|
"pin-project-lite",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body-util"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-range-header"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httparse"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpdate"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
|
"itoa",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-util"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.182"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchit"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "relay"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"axum",
|
||||||
|
"futures-util",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.149"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_path_to_error"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_urlencoded"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sync_wrapper"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.50.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-tungstenite"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"log",
|
||||||
|
"tokio",
|
||||||
|
"tungstenite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-http"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"http-range-header",
|
||||||
|
"httpdate",
|
||||||
|
"mime",
|
||||||
|
"mime_guess",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-layer"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-service"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tungstenite"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"data-encoding",
|
||||||
|
"http",
|
||||||
|
"httparse",
|
||||||
|
"log",
|
||||||
|
"rand",
|
||||||
|
"sha1",
|
||||||
|
"thiserror",
|
||||||
|
"utf-8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf-8"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.2+wasi-0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.53.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
12
relay/Cargo.toml
Normal file
12
relay/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "relay"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = { version = "0.8", features = ["ws"] }
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread", "macros", "process", "io-util", "sync"] }
|
||||||
|
tower-http = { version = "0.6", features = ["fs", "trace"] }
|
||||||
|
futures-util = "0.3"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
92
relay/src/build.rs
Normal file
92
relay/src/build.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
use std::{collections::HashMap, ops::Deref, path::{Path, PathBuf}};
|
||||||
|
use tokio::{
|
||||||
|
process::{Child, Command},
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn ensure_ok(child: Child) -> Result<(), Box<dyn std::error::Error + Send + Sync>>{
|
||||||
|
let result = child.wait_with_output().await?;
|
||||||
|
if !result.status.success(){
|
||||||
|
return Err(format!("{}\n{}", String::from_utf8_lossy(&result.stdout), String::from_utf8_lossy(&result.stderr)))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TempDir(PathBuf);
|
||||||
|
impl Drop for TempDir{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
_ = std::fs::remove_dir_all(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TempDir{
|
||||||
|
type Target = PathBuf;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<PathBuf> for TempDir{
|
||||||
|
fn as_ref(&self) -> &PathBuf {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsRef<Path> for TempDir{
|
||||||
|
fn as_ref(&self) -> &Path {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn build(files: HashMap<String, String>) -> Result<TempDir, Box<dyn std::error::Error + Send + Sync>>{
|
||||||
|
use std::hash::*;
|
||||||
|
let mut hasher = std::hash::DefaultHasher::default();
|
||||||
|
for (key, value) in &files{
|
||||||
|
key.hash(&mut hasher);
|
||||||
|
value.hash(&mut hasher);
|
||||||
|
}
|
||||||
|
let hash = hasher.finish();
|
||||||
|
|
||||||
|
let mut work_dir = std::env::temp_dir();
|
||||||
|
work_dir.push(format!("ghdl-relay-{hash:x?}"));
|
||||||
|
_ = std::fs::create_dir(&work_dir);
|
||||||
|
let work_dir = TempDir(work_dir);
|
||||||
|
|
||||||
|
|
||||||
|
for (name, contents) in &files{
|
||||||
|
let mut path = work_dir.clone();
|
||||||
|
path.push(name);
|
||||||
|
std::fs::write(path, contents)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let mut cmd = Command::new("ghdl");
|
||||||
|
cmd.kill_on_drop(true);
|
||||||
|
cmd.args(["-a", "-g", "--std=08"]);
|
||||||
|
for name in files.keys(){
|
||||||
|
let mut path = work_dir.clone();
|
||||||
|
path.push(name);
|
||||||
|
cmd.arg(path);
|
||||||
|
}
|
||||||
|
cmd.arg(std::fs::canonicalize("../rtl/tb.vhdl")?);
|
||||||
|
|
||||||
|
cmd.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped());
|
||||||
|
|
||||||
|
cmd.current_dir(&work_dir);
|
||||||
|
ensure_ok(cmd.spawn()?).await?;
|
||||||
|
|
||||||
|
let mut cmd = Command::new("ghdl");
|
||||||
|
cmd.kill_on_drop(true);
|
||||||
|
cmd.args(["-e", "--std=08"]);
|
||||||
|
cmd.arg(format!("-Wl,{}", std::fs::canonicalize("../conn/target/release/libvhdl_ui.a")?.display()));
|
||||||
|
cmd.arg("tb");
|
||||||
|
cmd.current_dir(&work_dir);
|
||||||
|
cmd.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped());
|
||||||
|
ensure_ok(cmd.spawn()?).await?;
|
||||||
|
|
||||||
|
Ok(work_dir)
|
||||||
|
}
|
||||||
153
relay/src/main.rs
Normal file
153
relay/src/main.rs
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
use axum::{
|
||||||
|
Router,
|
||||||
|
extract::ws::{Message, WebSocket, WebSocketUpgrade},
|
||||||
|
routing::get,
|
||||||
|
};
|
||||||
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::HashMap, net::SocketAddr};
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncBufReadExt, BufReader},
|
||||||
|
};
|
||||||
|
use tower_http::services::ServeDir;
|
||||||
|
|
||||||
|
pub mod build;
|
||||||
|
pub mod run;
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let app = Router::new()
|
||||||
|
.route(
|
||||||
|
"/ws",
|
||||||
|
get(|ws: WebSocketUpgrade| async move { ws.on_upgrade(ws_handler) }),
|
||||||
|
)
|
||||||
|
.fallback_service(ServeDir::new("ui"));
|
||||||
|
|
||||||
|
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||||
|
println!("Open UI: http://{}/", addr);
|
||||||
|
|
||||||
|
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
struct ClientInput{
|
||||||
|
/// bitfield of 32 switches
|
||||||
|
switch: u32,
|
||||||
|
/// bitfield of 32 buttons
|
||||||
|
buttons: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ws_handler(socket: WebSocket) {
|
||||||
|
let (mut sender, mut receiver) = socket.split();
|
||||||
|
|
||||||
|
let files = if let Some(Ok(Message::Text(msg))) = receiver.next().await
|
||||||
|
&& let Ok(files) = serde_json::from_str::<'_, HashMap<String, String>>(&msg)
|
||||||
|
{
|
||||||
|
files
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let artifact_dir = match build::build(files).await{
|
||||||
|
Ok(dir) => dir,
|
||||||
|
Err(err) => {
|
||||||
|
_ = sender.send(Message::Text(format!("Failed to build: {err}").into())).await;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_process, mut sin, sout, serr) = match run::run(&artifact_dir).await{
|
||||||
|
Ok(process) => process,
|
||||||
|
Err(err) => {
|
||||||
|
_ = sender.send(Message::Text(format!("Failed to run: {err}").into())).await;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let mut sout = BufReader::new(sout).lines();
|
||||||
|
let mut serr = BufReader::new(serr).lines();
|
||||||
|
|
||||||
|
let artifact_prefix = artifact_dir.to_str().unwrap_or("\0\0NOPE");
|
||||||
|
|
||||||
|
let result: Result<(), Box<dyn std::error::Error + Sync + Send>> = async {
|
||||||
|
loop{
|
||||||
|
tokio::select! {
|
||||||
|
receive = receiver.next() => {
|
||||||
|
match receive{
|
||||||
|
Some(Ok(Message::Close(_))) => break,
|
||||||
|
Some(Ok(Message::Text(msg))) => {
|
||||||
|
let input = serde_json::from_str::<'_, ClientInput>(&msg)?;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
sin.write_all(format!("key={}\n", input.buttons).as_bytes()).await?;
|
||||||
|
sin.write_all(format!("sw={}\n", input.switch).as_bytes()).await?;
|
||||||
|
},
|
||||||
|
Some(Ok(_)) => {},
|
||||||
|
Some(Err(err)) => Err(err)?,
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
out = sout.next_line() => {
|
||||||
|
match out{
|
||||||
|
Ok(Some(line)) => {
|
||||||
|
|
||||||
|
let msg = ServerMsg::Log {
|
||||||
|
stream: "stdout",
|
||||||
|
line: line.strip_prefix(artifact_prefix).unwrap_or(&line),
|
||||||
|
};
|
||||||
|
sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?;
|
||||||
|
},
|
||||||
|
Ok(None) => break,
|
||||||
|
Err(err) => {
|
||||||
|
Err(format!("Failed to read proccess sout: {err}"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = serr.next_line() => {
|
||||||
|
match err{
|
||||||
|
Ok(Some(line)) => {
|
||||||
|
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{
|
||||||
|
ServerMsg::Log {
|
||||||
|
stream: "stderr",
|
||||||
|
line: line.strip_prefix(artifact_prefix).unwrap_or(&line),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?;
|
||||||
|
},
|
||||||
|
Ok(None) => break,
|
||||||
|
Err(err) => {
|
||||||
|
Err(format!("Failed to read proccess serr: {err}"))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}.await;
|
||||||
|
|
||||||
|
match result{
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(err) => {
|
||||||
|
_ = sender.send(Message::Text(format!("{err}").into())).await;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
31
relay/src/run.rs
Normal file
31
relay/src/run.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::{path::Path};
|
||||||
|
|
||||||
|
use tokio::process::{Child, ChildStdin, ChildStdout, ChildStderr, Command};
|
||||||
|
|
||||||
|
pub struct Process{
|
||||||
|
child: Child,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Process{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
_ = self.child.start_kill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(artifact_dir: &Path) -> Result<(Process, ChildStdin, ChildStdout, ChildStderr), Box<dyn std::error::Error + Send + Sync>>{
|
||||||
|
let mut cmd = Command::new("ghdl");
|
||||||
|
cmd.args(["-r", "--std=08", "tb", "--stop-delta=2147483647", "--unbuffered"]);
|
||||||
|
cmd.current_dir(artifact_dir);
|
||||||
|
|
||||||
|
cmd.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped());
|
||||||
|
|
||||||
|
let mut child = cmd.spawn()?;
|
||||||
|
|
||||||
|
let stdin = child.stdin.take().ok_or("no stdin")?;
|
||||||
|
let stdout = child.stdout.take().ok_or("no stdout")?;
|
||||||
|
let stderr = child.stderr.take().ok_or("no stderr")?;
|
||||||
|
|
||||||
|
Ok((Process { child }, stdin, stdout, stderr))
|
||||||
|
}
|
||||||
468
relay/ui/app.js
Normal file
468
relay/ui/app.js
Normal file
|
|
@ -0,0 +1,468 @@
|
||||||
|
// ----- LocalStorage keys -----
|
||||||
|
const LS_KEY_VHDL = "circuit_ui:circuit.vhdl";
|
||||||
|
|
||||||
|
// ----- State -----
|
||||||
|
let ws = null;
|
||||||
|
let isConnected = false;
|
||||||
|
|
||||||
|
let switches = 0 >>> 0; // u32
|
||||||
|
let buttons = 0 >>> 0; // u32
|
||||||
|
|
||||||
|
let autoscroll = true;
|
||||||
|
|
||||||
|
// ----- DOM -----
|
||||||
|
const wsPill = document.getElementById("wsPill");
|
||||||
|
const wsUrlText = document.getElementById("wsUrlText");
|
||||||
|
|
||||||
|
const connectToggleBtn = document.getElementById("connectToggleBtn");
|
||||||
|
const sendInputsBtn = document.getElementById("sendInputsBtn");
|
||||||
|
|
||||||
|
const vhdlEditor = document.getElementById("vhdlEditor");
|
||||||
|
const lineGutter = document.getElementById("lineGutter");
|
||||||
|
const loadExampleBtn = document.getElementById("loadExampleBtn");
|
||||||
|
|
||||||
|
const ledRow = document.getElementById("ledRow");
|
||||||
|
const ledLabels = document.getElementById("ledLabels");
|
||||||
|
const hexRow = document.getElementById("hexRow");
|
||||||
|
|
||||||
|
const switchGrid = document.getElementById("switchGrid");
|
||||||
|
const buttonGrid = document.getElementById("buttonGrid");
|
||||||
|
|
||||||
|
const logView = document.getElementById("logView");
|
||||||
|
const clearLogBtn = document.getElementById("clearLogBtn");
|
||||||
|
const autoscrollBtn = document.getElementById("autoscrollBtn");
|
||||||
|
|
||||||
|
const allSwOffBtn = document.getElementById("allSwOffBtn");
|
||||||
|
const allSwOnBtn = document.getElementById("allSwOnBtn");
|
||||||
|
|
||||||
|
// ----- Helpers -----
|
||||||
|
function wsUrl() {
|
||||||
|
const proto = (location.protocol === "https:") ? "wss" : "ws";
|
||||||
|
return `${proto}://${location.host}/ws`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatus(connected) {
|
||||||
|
isConnected = connected;
|
||||||
|
wsPill.textContent = connected ? "CONNECTED" : "DISCONNECTED";
|
||||||
|
wsPill.style.borderColor = connected ? "rgba(34,197,94,.6)" : "rgba(239,68,68,.6)";
|
||||||
|
wsPill.style.background = connected ? "rgba(34,197,94,.14)" : "rgba(239,68,68,.10)";
|
||||||
|
|
||||||
|
sendInputsBtn.disabled = !connected;
|
||||||
|
|
||||||
|
// single button label
|
||||||
|
connectToggleBtn.textContent = connected ? "Disconnect" : "Connect";
|
||||||
|
connectToggleBtn.classList.toggle("secondary", connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendLog(stream, line) {
|
||||||
|
const prefix = stream === "stderr" ? "[stderr]" : "[stdout]";
|
||||||
|
logView.textContent += `${prefix} ${line}\n`;
|
||||||
|
if (autoscroll) {
|
||||||
|
logView.scrollTop = logView.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearLogs() {
|
||||||
|
logView.textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetOutputsVisuals() {
|
||||||
|
// reset LED/HEX visuals to 0 immediately
|
||||||
|
setLeds(0);
|
||||||
|
setHex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function u32BitGet(x, i) {
|
||||||
|
return ((x >>> i) & 1) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function u32BitSet(x, i, on) {
|
||||||
|
if (on) return (x | (1 << i)) >>> 0;
|
||||||
|
return (x & ~(1 << i)) >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendClientInput() {
|
||||||
|
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
||||||
|
|
||||||
|
const msg = {
|
||||||
|
type: "client_input",
|
||||||
|
switch: switches >>> 0,
|
||||||
|
buttons: buttons >>> 0,
|
||||||
|
};
|
||||||
|
ws.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHexDigits(hexU32) {
|
||||||
|
const d0 = (hexU32 >>> 0) & 0xFF;
|
||||||
|
const d1 = (hexU32 >>> 8) & 0xFF;
|
||||||
|
const d2 = (hexU32 >>> 16) & 0xFF;
|
||||||
|
const d3 = (hexU32 >>> 24) & 0xFF;
|
||||||
|
return [d0, d1, d2, d3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- Line numbers -----
|
||||||
|
function updateLineNumbers() {
|
||||||
|
const text = vhdlEditor.value || "";
|
||||||
|
// count lines: number of '\n' + 1 (even empty text -> 1 line)
|
||||||
|
const lines = text.length ? (text.split("\n").length) : 1;
|
||||||
|
|
||||||
|
// Build as one string for performance
|
||||||
|
let out = "";
|
||||||
|
for (let i = 1; i <= lines; i++) out += i + "\n";
|
||||||
|
lineGutter.textContent = out;
|
||||||
|
|
||||||
|
syncGutterScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncGutterScroll() {
|
||||||
|
// keep gutter aligned to editor scroll
|
||||||
|
lineGutter.scrollTop = vhdlEditor.scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- LocalStorage save (debounced) -----
|
||||||
|
let saveTimer = null;
|
||||||
|
function saveEditorToLocalStorageDebounced() {
|
||||||
|
if (saveTimer) clearTimeout(saveTimer);
|
||||||
|
saveTimer = setTimeout(() => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(LS_KEY_VHDL, vhdlEditor.value ?? "");
|
||||||
|
} catch (e) {
|
||||||
|
// ignore storage failures (private mode, quota)
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadEditorFromLocalStorage() {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem(LS_KEY_VHDL);
|
||||||
|
if (saved !== null) return saved;
|
||||||
|
} catch {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- UI Builders -----
|
||||||
|
function buildLeds() {
|
||||||
|
ledRow.innerHTML = "";
|
||||||
|
ledLabels.innerHTML = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < 32; i++) {
|
||||||
|
const el = document.createElement("div");
|
||||||
|
el.className = "led";
|
||||||
|
el.title = `LED[${i}]`;
|
||||||
|
el.dataset.bit = String(i);
|
||||||
|
ledRow.appendChild(el);
|
||||||
|
|
||||||
|
const lab = document.createElement("span");
|
||||||
|
lab.textContent = String(i);
|
||||||
|
ledLabels.appendChild(lab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLeds(bitsU32) {
|
||||||
|
for (const el of ledRow.children) {
|
||||||
|
const i = Number(el.dataset.bit);
|
||||||
|
el.classList.toggle("on", u32BitGet(bitsU32 >>> 0, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSevenSeg(digitIndex) {
|
||||||
|
const wrap = document.createElement("div");
|
||||||
|
|
||||||
|
const disp = document.createElement("div");
|
||||||
|
disp.className = "sevenSeg";
|
||||||
|
disp.dataset.digit = String(digitIndex);
|
||||||
|
|
||||||
|
const segs = ["a","b","c","d","e","f","g","dp"];
|
||||||
|
for (const s of segs) {
|
||||||
|
const seg = document.createElement("div");
|
||||||
|
seg.className = `seg ${s}`;
|
||||||
|
seg.dataset.seg = s;
|
||||||
|
disp.appendChild(seg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = document.createElement("div");
|
||||||
|
label.className = "digitLabel";
|
||||||
|
label.textContent = `HEX[${digitIndex}]`;
|
||||||
|
|
||||||
|
wrap.appendChild(disp);
|
||||||
|
wrap.appendChild(label);
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildHex() {
|
||||||
|
hexRow.innerHTML = "";
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
hexRow.appendChild(makeSevenSeg(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHex(hexU32) {
|
||||||
|
const digits = parseHexDigits(hexU32 >>> 0);
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const byte = digits[i] & 0xFF;
|
||||||
|
const disp = hexRow.querySelector(`.sevenSeg[data-digit="${i}"]`);
|
||||||
|
if (!disp) continue;
|
||||||
|
|
||||||
|
const map = { a:0, b:1, c:2, d:3, e:4, f:5, g:6, dp:7 };
|
||||||
|
|
||||||
|
for (const segEl of disp.querySelectorAll(".seg")) {
|
||||||
|
const name = segEl.dataset.seg;
|
||||||
|
const bit = map[name];
|
||||||
|
const on = ((byte >>> bit) & 1) === 1;
|
||||||
|
segEl.classList.toggle("on", on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSwitches() {
|
||||||
|
switchGrid.innerHTML = "";
|
||||||
|
for (let i = 0; i < 32; i++) {
|
||||||
|
const cell = document.createElement("div");
|
||||||
|
cell.className = "ioCell";
|
||||||
|
|
||||||
|
const label = document.createElement("label");
|
||||||
|
label.textContent = `SW[${i}]`;
|
||||||
|
|
||||||
|
const toggle = document.createElement("div");
|
||||||
|
toggle.className = "toggle";
|
||||||
|
toggle.dataset.bit = String(i);
|
||||||
|
toggle.title = `Toggle switch ${i}`;
|
||||||
|
|
||||||
|
toggle.addEventListener("click", () => {
|
||||||
|
const bit = Number(toggle.dataset.bit);
|
||||||
|
const now = !u32BitGet(switches, bit);
|
||||||
|
switches = u32BitSet(switches, bit, now);
|
||||||
|
toggle.classList.toggle("on", now);
|
||||||
|
sendClientInput();
|
||||||
|
});
|
||||||
|
|
||||||
|
cell.appendChild(toggle);
|
||||||
|
cell.appendChild(label);
|
||||||
|
switchGrid.appendChild(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncSwitchesUI() {
|
||||||
|
for (const toggle of switchGrid.querySelectorAll(".toggle")) {
|
||||||
|
const i = Number(toggle.dataset.bit);
|
||||||
|
toggle.classList.toggle("on", u32BitGet(switches, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildButtons() {
|
||||||
|
buttonGrid.innerHTML = "";
|
||||||
|
for (let i = 0; i < 32; i++) {
|
||||||
|
const cell = document.createElement("div");
|
||||||
|
cell.className = "ioCell";
|
||||||
|
|
||||||
|
const label = document.createElement("label");
|
||||||
|
label.textContent = `KEY[${i}]`;
|
||||||
|
|
||||||
|
const btn = document.createElement("button");
|
||||||
|
btn.className = "momentary";
|
||||||
|
btn.type = "button";
|
||||||
|
btn.textContent = "press";
|
||||||
|
btn.dataset.bit = String(i);
|
||||||
|
btn.title = `Momentary button ${i}`;
|
||||||
|
|
||||||
|
const press = () => {
|
||||||
|
const bit = Number(btn.dataset.bit);
|
||||||
|
buttons = u32BitSet(buttons, bit, true);
|
||||||
|
btn.classList.add("down");
|
||||||
|
sendClientInput();
|
||||||
|
};
|
||||||
|
|
||||||
|
const release = () => {
|
||||||
|
const bit = Number(btn.dataset.bit);
|
||||||
|
buttons = u32BitSet(buttons, bit, false);
|
||||||
|
btn.classList.remove("down");
|
||||||
|
sendClientInput();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mouse
|
||||||
|
btn.addEventListener("mousedown", (e) => { e.preventDefault(); press(); });
|
||||||
|
btn.addEventListener("mouseup", (e) => { e.preventDefault(); release(); });
|
||||||
|
btn.addEventListener("mouseleave",(e) => { e.preventDefault(); release(); });
|
||||||
|
|
||||||
|
// Touch
|
||||||
|
btn.addEventListener("touchstart",(e) => { e.preventDefault(); press(); }, {passive:false});
|
||||||
|
btn.addEventListener("touchend", (e) => { e.preventDefault(); release(); }, {passive:false});
|
||||||
|
btn.addEventListener("touchcancel",(e)=> { e.preventDefault(); release(); }, {passive:false});
|
||||||
|
|
||||||
|
cell.appendChild(btn);
|
||||||
|
cell.appendChild(label);
|
||||||
|
buttonGrid.appendChild(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- WebSocket -----
|
||||||
|
function connect() {
|
||||||
|
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return;
|
||||||
|
|
||||||
|
// Requirement (3): reset logs + outputs when connect is pressed
|
||||||
|
clearLogs();
|
||||||
|
resetOutputsVisuals();
|
||||||
|
|
||||||
|
const url = wsUrl();
|
||||||
|
wsUrlText.textContent = url;
|
||||||
|
|
||||||
|
ws = new WebSocket(url);
|
||||||
|
|
||||||
|
ws.addEventListener("open", () => {
|
||||||
|
setStatus(true);
|
||||||
|
appendLog("stdout", "WebSocket connected.");
|
||||||
|
|
||||||
|
// First message MUST be the file map
|
||||||
|
const files = {
|
||||||
|
"circuit.vhdl": vhdlEditor.value ?? ""
|
||||||
|
};
|
||||||
|
ws.send(JSON.stringify(files));
|
||||||
|
|
||||||
|
// Push initial input state
|
||||||
|
sendClientInput();
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.addEventListener("message", (ev) => {
|
||||||
|
let parsed = null;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(ev.data);
|
||||||
|
} catch {
|
||||||
|
appendLog("stderr", String(ev.data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (parsed.log !== undefined) {
|
||||||
|
appendLog(parsed.log.stream ?? "stdout", parsed.log.line ?? "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.led !== undefined) {
|
||||||
|
const v = (parsed.led ?? parsed.value ?? parsed[0] ?? parsed["0"] ?? 0) >>> 0;
|
||||||
|
setLeds(v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.hex !== undefined) {
|
||||||
|
const v = (parsed.hex ?? parsed.value ?? parsed[0] ?? parsed["0"] ?? 0) >>> 0;
|
||||||
|
setHex(v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog("stderr", `Unknown msg: ${ev.data}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.addEventListener("close", () => {
|
||||||
|
appendLog("stderr", "WebSocket closed.");
|
||||||
|
setStatus(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.addEventListener("error", () => {
|
||||||
|
appendLog("stderr", "WebSocket error.");
|
||||||
|
setStatus(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnect() {
|
||||||
|
if (ws) ws.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleConnect() {
|
||||||
|
if (isConnected) disconnect();
|
||||||
|
else connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- Wire up controls -----
|
||||||
|
connectToggleBtn.addEventListener("click", toggleConnect);
|
||||||
|
|
||||||
|
sendInputsBtn.addEventListener("click", () => {
|
||||||
|
appendLog("stdout", `Manual send: sw=${switches >>> 0} key=${buttons >>> 0}`);
|
||||||
|
sendClientInput();
|
||||||
|
});
|
||||||
|
|
||||||
|
clearLogBtn.addEventListener("click", () => { clearLogs(); });
|
||||||
|
|
||||||
|
autoscrollBtn.addEventListener("click", () => {
|
||||||
|
autoscroll = !autoscroll;
|
||||||
|
autoscrollBtn.textContent = `Autoscroll: ${autoscroll ? "on" : "off"}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
allSwOffBtn.addEventListener("click", () => {
|
||||||
|
switches = 0 >>> 0;
|
||||||
|
syncSwitchesUI();
|
||||||
|
sendClientInput();
|
||||||
|
});
|
||||||
|
|
||||||
|
allSwOnBtn.addEventListener("click", () => {
|
||||||
|
switches = 0xFFFF_FFFF >>> 0;
|
||||||
|
syncSwitchesUI();
|
||||||
|
sendClientInput();
|
||||||
|
});
|
||||||
|
|
||||||
|
loadExampleBtn.addEventListener("click", () => {
|
||||||
|
vhdlEditor.value =
|
||||||
|
`library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
-- Do not modify the following entity block
|
||||||
|
entity circuit is
|
||||||
|
port (
|
||||||
|
clk: in std_logic; -- 500 Hz, period 2 ms
|
||||||
|
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) := (others => '0'); -- active high
|
||||||
|
hex: out std_logic_vector(31 downto 0) := (others => '0') -- active low
|
||||||
|
);
|
||||||
|
end circuit;
|
||||||
|
|
||||||
|
|
||||||
|
architecture description of circuit is
|
||||||
|
signal counter: unsigned(31 downto 0) := x"00000000";
|
||||||
|
begin
|
||||||
|
led <= std_logic_vector(counter);
|
||||||
|
process(clk)
|
||||||
|
begin
|
||||||
|
counter <= counter+1;
|
||||||
|
end process;
|
||||||
|
end description;`;
|
||||||
|
|
||||||
|
saveEditorToLocalStorageDebounced();
|
||||||
|
updateLineNumbers();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Editor events: save + line numbers + gutter sync
|
||||||
|
vhdlEditor.addEventListener("input", () => {
|
||||||
|
saveEditorToLocalStorageDebounced();
|
||||||
|
updateLineNumbers();
|
||||||
|
});
|
||||||
|
|
||||||
|
vhdlEditor.addEventListener("scroll", () => {
|
||||||
|
syncGutterScroll();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----- Init -----
|
||||||
|
(function init() {
|
||||||
|
wsUrlText.textContent = wsUrl();
|
||||||
|
|
||||||
|
buildLeds();
|
||||||
|
buildHex();
|
||||||
|
buildSwitches();
|
||||||
|
buildButtons();
|
||||||
|
|
||||||
|
resetOutputsVisuals();
|
||||||
|
setStatus(false);
|
||||||
|
|
||||||
|
// Load from localStorage if present
|
||||||
|
const saved = loadEditorFromLocalStorage();
|
||||||
|
if (saved !== null) {
|
||||||
|
vhdlEditor.value = saved;
|
||||||
|
} else {
|
||||||
|
vhdlEditor.value =
|
||||||
|
`-- circuit.vhdl
|
||||||
|
-- Paste your circuit here. The UI will send it on Connect.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLineNumbers();
|
||||||
|
})();
|
||||||
119
relay/ui/index.html
Normal file
119
relay/ui/index.html
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<title>VHDL Circuit UI</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="topbar">
|
||||||
|
<div class="brand">
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div>
|
||||||
|
<div class="title">Circuit Web UI</div>
|
||||||
|
<div class="subtitle">LEDs • HEX • Switches • Buttons • circuit.vhdl</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<div class="status">
|
||||||
|
<span class="pill" id="wsPill">DISCONNECTED</span>
|
||||||
|
<span class="muted" id="wsUrlText"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- single toggle button now -->
|
||||||
|
<button id="connectToggleBtn">Connect</button>
|
||||||
|
|
||||||
|
<button id="sendInputsBtn" class="secondary" disabled>Send Inputs</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="grid">
|
||||||
|
<!-- Left: Editor -->
|
||||||
|
<section class="card editor">
|
||||||
|
<div class="cardHeader">
|
||||||
|
<div class="cardTitle">circuit.vhdl</div>
|
||||||
|
<div class="cardActions">
|
||||||
|
<button id="loadExampleBtn" class="secondary">Load example</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NEW: editor with line-number gutter -->
|
||||||
|
<div class="editorWrap">
|
||||||
|
<pre id="lineGutter" class="lineGutter" aria-hidden="true"></pre>
|
||||||
|
<textarea id="vhdlEditor" spellcheck="false"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hint">
|
||||||
|
Saved locally as you type. On <b>Connect</b>, the UI sends <code>{"circuit.vhdl": "..."}</code> as the first WebSocket message.
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Right: IO -->
|
||||||
|
<section class="card io">
|
||||||
|
<div class="cardHeader">
|
||||||
|
<div class="cardTitle">Outputs</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="outputs">
|
||||||
|
<div class="block">
|
||||||
|
<div class="blockTitle">LEDs (32)</div>
|
||||||
|
<div id="ledRow" class="ledRow"></div>
|
||||||
|
<div class="bitLabelRow" id="ledLabels"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<div class="blockTitle">HEX (4 digits, 7-seg + dp)</div>
|
||||||
|
<div id="hexRow" class="hexRow"></div>
|
||||||
|
<div class="hint small">
|
||||||
|
Assumes each digit is 8 bits: <code>bit0=a</code>, <code>1=b</code>, <code>2=c</code>, <code>3=d</code>, <code>4=e</code>, <code>5=f</code>, <code>6=g</code>, <code>7=dp</code>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Bottom-left: Inputs -->
|
||||||
|
<section class="card inputs">
|
||||||
|
<div class="cardHeader">
|
||||||
|
<div class="cardTitle">Inputs</div>
|
||||||
|
<div class="cardActions">
|
||||||
|
<button id="allSwOffBtn" class="secondary">All switches off</button>
|
||||||
|
<button id="allSwOnBtn" class="secondary">All switches on</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="twoCols">
|
||||||
|
<div class="block">
|
||||||
|
<div class="blockTitle">Switches (32, latched)</div>
|
||||||
|
<div id="switchGrid" class="ioGrid"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<div class="blockTitle">Buttons (32, momentary)</div>
|
||||||
|
<div id="buttonGrid" class="ioGrid"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hint">
|
||||||
|
Switches toggle a bit. Buttons set the bit while pressed (mouse/touch) and clear on release.
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Bottom-right: Logs -->
|
||||||
|
<section class="card logs">
|
||||||
|
<div class="cardHeader">
|
||||||
|
<div class="cardTitle">Logs</div>
|
||||||
|
<div class="cardActions">
|
||||||
|
<button id="clearLogBtn" class="secondary">Clear</button>
|
||||||
|
<button id="autoscrollBtn" class="secondary">Autoscroll: on</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre id="logView" class="logView"></pre>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
396
relay/ui/styles.css
Normal file
396
relay/ui/styles.css
Normal file
|
|
@ -0,0 +1,396 @@
|
||||||
|
:root{
|
||||||
|
--bg: #0b0f17;
|
||||||
|
--card: #121a2a;
|
||||||
|
--card2:#0f1726;
|
||||||
|
--text: #e6eefc;
|
||||||
|
--muted:#9fb0d0;
|
||||||
|
--accent:#58a6ff;
|
||||||
|
--ok:#22c55e;
|
||||||
|
--bad:#ef4444;
|
||||||
|
--warn:#f59e0b;
|
||||||
|
--line:#1f2a44;
|
||||||
|
--shadow: 0 12px 30px rgba(0,0,0,.35);
|
||||||
|
--radius: 14px;
|
||||||
|
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
|
||||||
|
}
|
||||||
|
|
||||||
|
*{ box-sizing:border-box; }
|
||||||
|
html,body{ height:100%; }
|
||||||
|
body{
|
||||||
|
margin:0;
|
||||||
|
background: radial-gradient(1000px 600px at 20% -10%, rgba(88,166,255,.20), transparent 60%),
|
||||||
|
radial-gradient(900px 500px at 90% 0%, rgba(34,197,94,.12), transparent 60%),
|
||||||
|
var(--bg);
|
||||||
|
color:var(--text);
|
||||||
|
font-family: var(--sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
.topbar{
|
||||||
|
position: sticky;
|
||||||
|
top:0;
|
||||||
|
z-index: 10;
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
align-items:center;
|
||||||
|
padding:14px 16px;
|
||||||
|
border-bottom:1px solid rgba(255,255,255,.06);
|
||||||
|
background: rgba(11,15,23,.8);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand{
|
||||||
|
display:flex;
|
||||||
|
gap:12px;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
.brand .dot{
|
||||||
|
width:12px;height:12px;border-radius:50%;
|
||||||
|
background: var(--accent);
|
||||||
|
box-shadow: 0 0 18px rgba(88,166,255,.7);
|
||||||
|
}
|
||||||
|
.title{ font-weight:700; letter-spacing:.2px; }
|
||||||
|
.subtitle{ font-size:12px; color:var(--muted); margin-top:2px; }
|
||||||
|
|
||||||
|
.controls{
|
||||||
|
display:flex;
|
||||||
|
gap:10px;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
.status{
|
||||||
|
display:flex;
|
||||||
|
gap:8px;
|
||||||
|
align-items:center;
|
||||||
|
margin-right:6px;
|
||||||
|
}
|
||||||
|
.pill{
|
||||||
|
font-size:12px;
|
||||||
|
padding:6px 10px;
|
||||||
|
border-radius:999px;
|
||||||
|
border:1px solid rgba(255,255,255,.12);
|
||||||
|
background: rgba(255,255,255,.04);
|
||||||
|
}
|
||||||
|
.muted{ color:var(--muted); font-size:12px; }
|
||||||
|
|
||||||
|
button{
|
||||||
|
border:1px solid rgba(255,255,255,.14);
|
||||||
|
background: rgba(88,166,255,.14);
|
||||||
|
color:var(--text);
|
||||||
|
padding:8px 12px;
|
||||||
|
border-radius:10px;
|
||||||
|
cursor:pointer;
|
||||||
|
transition: transform .05s ease, background .2s ease;
|
||||||
|
}
|
||||||
|
button:hover{ background: rgba(88,166,255,.20); }
|
||||||
|
button:active{ transform: translateY(1px); }
|
||||||
|
button.secondary{
|
||||||
|
background: rgba(255,255,255,.06);
|
||||||
|
}
|
||||||
|
button.secondary:hover{ background: rgba(255,255,255,.10); }
|
||||||
|
button:disabled{
|
||||||
|
opacity:.55;
|
||||||
|
cursor:not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns: 1.2fr 1fr;
|
||||||
|
grid-template-rows: 420px 1fr;
|
||||||
|
gap:14px;
|
||||||
|
padding:14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card{
|
||||||
|
background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.03));
|
||||||
|
border:1px solid rgba(255,255,255,.08);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
overflow:hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardHeader{
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
align-items:center;
|
||||||
|
padding:12px 12px;
|
||||||
|
border-bottom:1px solid rgba(255,255,255,.06);
|
||||||
|
background: rgba(0,0,0,.15);
|
||||||
|
}
|
||||||
|
.cardTitle{ font-weight:700; }
|
||||||
|
.cardActions{ display:flex; gap:8px; }
|
||||||
|
|
||||||
|
.editor{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
}
|
||||||
|
textarea{
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
padding:12px;
|
||||||
|
background: rgba(0,0,0,.22);
|
||||||
|
color:var(--text);
|
||||||
|
border:0;
|
||||||
|
outline:none;
|
||||||
|
resize:none;
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
.hint{
|
||||||
|
padding:10px 12px;
|
||||||
|
border-top:1px solid rgba(255,255,255,.06);
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:12px;
|
||||||
|
background: rgba(0,0,0,.12);
|
||||||
|
}
|
||||||
|
.hint.small{ font-size:11px; }
|
||||||
|
|
||||||
|
.io .outputs{
|
||||||
|
padding:12px;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:14px;
|
||||||
|
}
|
||||||
|
.blockTitle{
|
||||||
|
font-size:12px;
|
||||||
|
color:var(--muted);
|
||||||
|
margin-bottom:8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ledRow{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns: repeat(16, 1fr);
|
||||||
|
gap:6px;
|
||||||
|
}
|
||||||
|
.led{
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
border-radius: 999px;
|
||||||
|
border:1px solid rgba(255,255,255,.14);
|
||||||
|
background: rgba(255,255,255,.08);
|
||||||
|
box-shadow: inset 0 0 0 2px rgba(0,0,0,.18);
|
||||||
|
}
|
||||||
|
.led.on{
|
||||||
|
background: rgba(34,197,94,.70);
|
||||||
|
border-color: rgba(34,197,94,.9);
|
||||||
|
box-shadow: 0 0 14px rgba(34,197,94,.45);
|
||||||
|
}
|
||||||
|
.bitLabelRow{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns: repeat(16, 1fr);
|
||||||
|
gap:6px;
|
||||||
|
margin-top:6px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 10px;
|
||||||
|
font-family: var(--mono);
|
||||||
|
opacity: .85;
|
||||||
|
}
|
||||||
|
.bitLabelRow span{
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hexRow{
|
||||||
|
display:flex;
|
||||||
|
gap:14px;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7-seg display */
|
||||||
|
.sevenSeg{
|
||||||
|
width: 90px;
|
||||||
|
height: 140px;
|
||||||
|
position: relative;
|
||||||
|
background: rgba(0,0,0,.18);
|
||||||
|
border:1px solid rgba(255,255,255,.10);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding:10px;
|
||||||
|
}
|
||||||
|
.seg{
|
||||||
|
position:absolute;
|
||||||
|
background: rgba(255,255,255,.10);
|
||||||
|
border-radius: 8px;
|
||||||
|
filter: drop-shadow(0 0 0 rgba(0,0,0,0));
|
||||||
|
}
|
||||||
|
.seg.on{
|
||||||
|
background: rgba(88,166,255,.75);
|
||||||
|
filter: drop-shadow(0 0 8px rgba(88,166,255,.35));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* segment positions */
|
||||||
|
.seg.a{ top:10px; left:18px; width:54px; height:12px; }
|
||||||
|
.seg.d{ bottom:10px; left:18px; width:54px; height:12px; }
|
||||||
|
.seg.g{ top:64px; left:18px; width:54px; height:12px; }
|
||||||
|
|
||||||
|
.seg.f{ top:18px; left:10px; width:12px; height:54px; }
|
||||||
|
.seg.b{ top:18px; right:10px; width:12px; height:54px; }
|
||||||
|
|
||||||
|
.seg.e{ bottom:18px; left:10px; width:12px; height:54px; }
|
||||||
|
.seg.c{ bottom:18px; right:10px; width:12px; height:54px; }
|
||||||
|
|
||||||
|
.seg.dp{
|
||||||
|
width:12px; height:12px;
|
||||||
|
bottom:10px; right:10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.digitLabel{
|
||||||
|
margin-top:8px;
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--muted);
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs{
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.twoCols{
|
||||||
|
padding:12px;
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap:12px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.ioGrid{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns: repeat(8, 1fr);
|
||||||
|
gap:8px;
|
||||||
|
}
|
||||||
|
.ioCell{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:6px;
|
||||||
|
align-items:center;
|
||||||
|
padding:8px 6px;
|
||||||
|
background: rgba(0,0,0,.16);
|
||||||
|
border:1px solid rgba(255,255,255,.08);
|
||||||
|
border-radius:12px;
|
||||||
|
}
|
||||||
|
.ioCell label{
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle{
|
||||||
|
width: 38px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 999px;
|
||||||
|
position: relative;
|
||||||
|
background: rgba(255,255,255,.10);
|
||||||
|
border:1px solid rgba(255,255,255,.14);
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.toggle::after{
|
||||||
|
content:"";
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
position:absolute;
|
||||||
|
top:2px;
|
||||||
|
left:2px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,.45);
|
||||||
|
transition: left .12s ease, background .12s ease;
|
||||||
|
}
|
||||||
|
.toggle.on{
|
||||||
|
background: rgba(34,197,94,.24);
|
||||||
|
border-color: rgba(34,197,94,.55);
|
||||||
|
}
|
||||||
|
.toggle.on::after{
|
||||||
|
left: 20px;
|
||||||
|
background: rgba(34,197,94,.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.momentary{
|
||||||
|
width: 40px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border:1px solid rgba(255,255,255,.14);
|
||||||
|
background: rgba(255,255,255,.08);
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.momentary.down{
|
||||||
|
background: rgba(245,158,11,.25);
|
||||||
|
border-color: rgba(245,158,11,.55);
|
||||||
|
box-shadow: 0 0 12px rgba(245,158,11,.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs{
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.logView{
|
||||||
|
margin:0;
|
||||||
|
padding:12px;
|
||||||
|
height:100%;
|
||||||
|
overflow:auto;
|
||||||
|
background: rgba(0,0,0,.22);
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.35;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 1100px){
|
||||||
|
.grid{
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
}
|
||||||
|
.inputs, .logs{
|
||||||
|
grid-column: auto;
|
||||||
|
}
|
||||||
|
.twoCols{
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor w/ line numbers */
|
||||||
|
.editorWrap{
|
||||||
|
display:flex;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
min-height: 0;
|
||||||
|
background: rgba(0,0,0,.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineGutter{
|
||||||
|
margin:0;
|
||||||
|
padding:12px 10px 12px 12px;
|
||||||
|
width: 54px; /* gutter width */
|
||||||
|
overflow:hidden;
|
||||||
|
user-select:none;
|
||||||
|
text-align:right;
|
||||||
|
color: rgba(159,176,208,.85);
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.35;
|
||||||
|
border-right: 1px solid rgba(255,255,255,.06);
|
||||||
|
background: rgba(0,0,0,.10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineGutter .ln { display:block; }
|
||||||
|
|
||||||
|
textarea#vhdlEditor{
|
||||||
|
overscroll-behavior-y: none;
|
||||||
|
flex:1;
|
||||||
|
height:100%;
|
||||||
|
padding:12px;
|
||||||
|
background: transparent; /* uses editorWrap background */
|
||||||
|
color:var(--text);
|
||||||
|
border:0;
|
||||||
|
outline:none;
|
||||||
|
resize:none;
|
||||||
|
font-family: var(--mono);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.35;
|
||||||
|
overflow:auto;
|
||||||
|
}
|
||||||
352
rtl/circuit copy._vhdl
Normal file
352
rtl/circuit copy._vhdl
Normal file
|
|
@ -0,0 +1,352 @@
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
-- Do not modify the following entity block
|
||||||
|
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) -- active low
|
||||||
|
);
|
||||||
|
end circuit;
|
||||||
|
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
entity alu is
|
||||||
|
Port (
|
||||||
|
func: in unsigned(3 downto 0);
|
||||||
|
a: in unsigned(7 downto 0);
|
||||||
|
b: in unsigned(7 downto 0);
|
||||||
|
carry_in: in std_logic;
|
||||||
|
o: out unsigned(7 downto 0);
|
||||||
|
carry_out: out std_logic;
|
||||||
|
zero: out std_logic;
|
||||||
|
gt: out std_logic;
|
||||||
|
lt: out std_logic;
|
||||||
|
eq: out std_logic
|
||||||
|
);
|
||||||
|
end alu;
|
||||||
|
|
||||||
|
architecture Behavioral of alu is
|
||||||
|
signal tmp: unsigned(8 downto 0) := "000000000";
|
||||||
|
begin
|
||||||
|
with func select
|
||||||
|
tmp <= ("0"&a) + ("0"&b) when x"0",
|
||||||
|
("0"&a) + ("0"&b) + (x"00"&carry_in) when x"1",
|
||||||
|
("0"&a) - ("0"&b) when x"2",
|
||||||
|
("0"&a) - ("0"&b) - (x"00"&carry_in) when x"3",
|
||||||
|
("0"&a) and ("0"&b) when x"4",
|
||||||
|
("0"&a) or ("0"&b) when x"5",
|
||||||
|
("0"&a) xor ("0"&b) when x"6",
|
||||||
|
"0"&x"00" when others;
|
||||||
|
|
||||||
|
zero <= '1' when tmp = 0 else '0';
|
||||||
|
eq <= '1' when a = b else '0';
|
||||||
|
lt <= '1' when a < b else '0';
|
||||||
|
gt <= '1' when a > b else '0';
|
||||||
|
carry_out <= tmp(8);
|
||||||
|
o <= tmp(7 downto 0);
|
||||||
|
|
||||||
|
end Behavioral ; -- Behavioral
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
entity ram_8x256 is
|
||||||
|
Port (
|
||||||
|
clk : in std_logic;
|
||||||
|
we : in std_logic; -- write enable
|
||||||
|
addr : in unsigned(7 downto 0); -- 8-bit address
|
||||||
|
din : in unsigned(7 downto 0); -- data input
|
||||||
|
dout : out unsigned(7 downto 0) -- data output
|
||||||
|
);
|
||||||
|
end ram_8x256;
|
||||||
|
|
||||||
|
architecture Behavioral of ram_8x256 is
|
||||||
|
type ram_type is array (0 to 255) of unsigned(7 downto 0);
|
||||||
|
signal ram : ram_type := (others => x"AB");
|
||||||
|
begin
|
||||||
|
process(clk)
|
||||||
|
begin
|
||||||
|
if rising_edge(clk) then
|
||||||
|
if we = '1' then
|
||||||
|
ram(to_integer(unsigned(addr))) <= din;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
dout <= ram(to_integer(unsigned(addr)));
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
end Behavioral;
|
||||||
|
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
entity inst_ram_8x256 is
|
||||||
|
Port (
|
||||||
|
clk : in std_logic;
|
||||||
|
addr : in unsigned(7 downto 0); -- 8-bit address
|
||||||
|
dout : out unsigned(7 downto 0) -- data output
|
||||||
|
);
|
||||||
|
end inst_ram_8x256;
|
||||||
|
|
||||||
|
architecture Behavioral of inst_ram_8x256 is
|
||||||
|
type ram_type is array (0 to 255) of unsigned(7 downto 0);
|
||||||
|
signal ram : ram_type := (
|
||||||
|
0 => x"A0", -- 0 => a
|
||||||
|
2 => x"B1", -- 1 => b
|
||||||
|
3 => x"10", -- a+b => out
|
||||||
|
4 => x"FE", -- out
|
||||||
|
5 => x"AE", -- out => a
|
||||||
|
6 => x"10", -- a+b => out
|
||||||
|
7 => x"FE", -- out
|
||||||
|
8 => x"BE", -- out => b
|
||||||
|
9 => x"D0", -- jump to 3
|
||||||
|
10 => x"03",
|
||||||
|
others => (others => '0')
|
||||||
|
);
|
||||||
|
begin
|
||||||
|
process(clk)
|
||||||
|
begin
|
||||||
|
if rising_edge(clk) or falling_edge(clk) then
|
||||||
|
dout <= ram(to_integer(unsigned(addr)));
|
||||||
|
end if;
|
||||||
|
end process;
|
||||||
|
end Behavioral;
|
||||||
|
|
||||||
|
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
|
||||||
|
architecture description of circuit is
|
||||||
|
signal clock: std_logic;
|
||||||
|
|
||||||
|
signal reg_pc: unsigned(7 downto 0) := "00000000";
|
||||||
|
signal reg_a: unsigned(7 downto 0) := "00000000";
|
||||||
|
signal reg_b: unsigned(7 downto 0) := "00000000";
|
||||||
|
signal reg_out: unsigned(7 downto 0) := "00000000";
|
||||||
|
|
||||||
|
signal inst_reg: unsigned(7 downto 0);
|
||||||
|
signal inst_bus: unsigned(7 downto 0);
|
||||||
|
|
||||||
|
signal data_read: unsigned(7 downto 0);
|
||||||
|
signal data_write: unsigned(7 downto 0) := "00000000";
|
||||||
|
|
||||||
|
signal data_addr: unsigned(7 downto 0) := "00000000";
|
||||||
|
signal data_write_e: std_logic := '0';
|
||||||
|
|
||||||
|
signal flag_carry: std_logic := '0';
|
||||||
|
signal flag_lt: std_logic := '0';
|
||||||
|
signal flag_gt: std_logic := '0';
|
||||||
|
signal flag_eq: std_logic := '0';
|
||||||
|
signal flag_zero: std_logic := '0';
|
||||||
|
|
||||||
|
signal alu_a, alu_b, alu_o : unsigned(7 downto 0) := "00000000";
|
||||||
|
signal alu_func : unsigned(3 downto 0) := "0000";
|
||||||
|
signal alu_carry, alu_zero, alu_gt, alu_lt, alu_eq : std_logic := '0';
|
||||||
|
signal alu_tmp: unsigned(8 downto 0) := "000000000";
|
||||||
|
|
||||||
|
function dec7seg(val: unsigned(3 downto 0)) return std_logic_vector is
|
||||||
|
begin
|
||||||
|
case val is
|
||||||
|
when "0000"=> return "1000000"; --0
|
||||||
|
when "0001"=> return "1111001"; --1
|
||||||
|
when "0010"=> return "0100100"; --2
|
||||||
|
when "0011"=> return "0110000"; --3
|
||||||
|
when "0100"=> return "0011001"; --4
|
||||||
|
when "0101"=> return "0010010"; --5
|
||||||
|
when "0110"=> return "0000010"; --6
|
||||||
|
when "0111"=> return "1111000"; --7
|
||||||
|
when "1000"=> return "0000000"; --8
|
||||||
|
when "1001"=> return "0011000"; --9
|
||||||
|
when "1010"=> return "0001000"; --A
|
||||||
|
when "1011"=> return "0000011"; --B
|
||||||
|
when "1100"=> return "1000110"; --C
|
||||||
|
when "1101"=> return "0100001"; --D
|
||||||
|
when "1110"=> return "0000110"; --E
|
||||||
|
when "1111"=> return "0001110"; --F
|
||||||
|
when others=> return "1111111"; ---
|
||||||
|
end case;
|
||||||
|
end function;
|
||||||
|
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
-- hex(7 downto 4) <= dec7seg(reg_out(7 downto 4));
|
||||||
|
-- hex(3 downto 0) <= dec7seg(reg_out(3 downto 0));
|
||||||
|
|
||||||
|
clock <= clk when sw(9) = '1' else sw(8);
|
||||||
|
|
||||||
|
ram_inst : entity work.inst_ram_8x256
|
||||||
|
port map(
|
||||||
|
clk => clk,
|
||||||
|
addr => reg_pc,
|
||||||
|
dout => inst_bus
|
||||||
|
);
|
||||||
|
|
||||||
|
ram_data : entity work.ram_8x256
|
||||||
|
port map(
|
||||||
|
clk => clk,
|
||||||
|
we => data_write_e,
|
||||||
|
addr => data_addr,
|
||||||
|
din => data_write,
|
||||||
|
dout => data_read
|
||||||
|
);
|
||||||
|
|
||||||
|
with alu_func select
|
||||||
|
alu_tmp <= ("0"&alu_a) + ("0"&alu_b) when x"0",
|
||||||
|
("0"&alu_a) + ("0"&alu_b) + (x"00"&flag_carry) when x"1",
|
||||||
|
("0"&alu_a) - ("0"&alu_b) when x"2",
|
||||||
|
("0"&alu_a) - ("0"&alu_b) - (x"00"&flag_carry) when x"3",
|
||||||
|
("0"&alu_a) and ("0"&alu_b) when x"4",
|
||||||
|
("0"&alu_a) or ("0"&alu_b) when x"5",
|
||||||
|
("0"&alu_a) xor ("0"&alu_b) when x"6",
|
||||||
|
"0"&x"00" when others;
|
||||||
|
|
||||||
|
alu_zero <= '1' when alu_tmp = 0 else '0';
|
||||||
|
alu_eq <= '1' when alu_a = alu_b else '0';
|
||||||
|
alu_lt <= '1' when alu_a < alu_b else '0';
|
||||||
|
alu_gt <= '1' when alu_a > alu_b else '0';
|
||||||
|
alu_carry <= alu_tmp(8);
|
||||||
|
alu_o <= alu_tmp(7 downto 0);
|
||||||
|
|
||||||
|
-- alu : entity work.alu
|
||||||
|
-- port map(
|
||||||
|
-- func => alu_func,
|
||||||
|
-- a => alu_a,
|
||||||
|
-- b => alu_b,
|
||||||
|
-- carry_in => flag_carry,
|
||||||
|
-- o => alu_o,
|
||||||
|
-- carry_out => alu_carry,
|
||||||
|
-- zero => alu_zero,
|
||||||
|
-- gt => alu_gt,
|
||||||
|
-- lt => alu_lt,
|
||||||
|
-- eq => alu_eq
|
||||||
|
-- );
|
||||||
|
|
||||||
|
process(clock)
|
||||||
|
variable out_extended : unsigned(8 downto 0);
|
||||||
|
begin
|
||||||
|
|
||||||
|
if rising_edge(clock) then
|
||||||
|
inst_reg <= inst_bus;
|
||||||
|
data_write_e <= '0';
|
||||||
|
|
||||||
|
-- report "begin reg_a = " & integer'image(to_integer(unsigned(reg_a)))
|
||||||
|
-- & " reg_b = " & integer'image(to_integer(unsigned(reg_b)))
|
||||||
|
-- & " reg_out = " & integer'image(to_integer(unsigned(reg_out)))
|
||||||
|
-- & " reg_pc = " & integer'image(to_integer(unsigned(reg_pc)))
|
||||||
|
-- & " inst_bus = " & integer'image(to_integer(unsigned(inst_bus)));
|
||||||
|
|
||||||
|
-- alu operations a,b
|
||||||
|
if inst_bus(7 downto 4) = x"1" then
|
||||||
|
alu_func <= inst_bus(3 downto 0);
|
||||||
|
alu_a <= reg_a;
|
||||||
|
alu_b <= reg_b;
|
||||||
|
reg_out <= alu_o;
|
||||||
|
end if;
|
||||||
|
-- alu operations a,imm
|
||||||
|
if inst_bus(7 downto 4) = x"2" then
|
||||||
|
alu_func <= inst_bus(3 downto 0);
|
||||||
|
alu_a <= reg_a;
|
||||||
|
alu_b <= x"00";
|
||||||
|
reg_pc <= reg_pc+1;
|
||||||
|
end if;
|
||||||
|
-- alu operations imm,b
|
||||||
|
if inst_bus(7 downto 4) = x"3" then
|
||||||
|
alu_func <= inst_bus(3 downto 0);
|
||||||
|
alu_a <= x"00";
|
||||||
|
alu_b <= reg_b;
|
||||||
|
reg_pc <= reg_pc+1;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
case inst_bus is
|
||||||
|
-- nop
|
||||||
|
when x"00" => null;
|
||||||
|
|
||||||
|
-- 0 => a
|
||||||
|
when x"A0" => reg_a <= x"00";
|
||||||
|
-- 1 => a
|
||||||
|
when x"A1" => reg_a <= x"01";
|
||||||
|
-- mem[reg b] => a
|
||||||
|
when x"AC" =>
|
||||||
|
data_addr <= reg_b;
|
||||||
|
-- out => a
|
||||||
|
when x"AE" => reg_a <= reg_out;
|
||||||
|
-- immediate => a
|
||||||
|
when x"AF" => reg_pc <= reg_pc+1;
|
||||||
|
|
||||||
|
-- 0 => b
|
||||||
|
when x"B0" => reg_b <= x"00";
|
||||||
|
-- 1 => b
|
||||||
|
when x"B1" => reg_b <= x"01";
|
||||||
|
-- mem[reg a] => b
|
||||||
|
when x"BC" =>
|
||||||
|
data_addr <= reg_b;
|
||||||
|
-- out => b
|
||||||
|
when x"BE" => reg_b <= reg_out;
|
||||||
|
-- immediate => b
|
||||||
|
when x"BF" => reg_pc <= reg_pc+1;
|
||||||
|
|
||||||
|
-- conditional
|
||||||
|
|
||||||
|
-- jump imm addr abs
|
||||||
|
when x"D0" => reg_pc <= reg_pc+1;
|
||||||
|
-- jump imm addr rel
|
||||||
|
when x"D1" => reg_pc <= reg_pc+1;
|
||||||
|
-- jump addr reg a
|
||||||
|
when x"DA" => reg_pc <= reg_a-1;
|
||||||
|
-- jump addr reg b
|
||||||
|
when x"DB" => reg_pc <= reg_b-1;
|
||||||
|
|
||||||
|
-- out
|
||||||
|
when x"FE" => report " out = " & integer'image(to_integer(unsigned(reg_out)));
|
||||||
|
-- halt
|
||||||
|
when x"FF" => reg_pc <= reg_pc-1;
|
||||||
|
|
||||||
|
when others => null;
|
||||||
|
end case;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if falling_edge(clock) then
|
||||||
|
case inst_reg is
|
||||||
|
when x"AC" => reg_a <= data_read;
|
||||||
|
when x"AF" => reg_a <= inst_bus;
|
||||||
|
|
||||||
|
when x"BC" => reg_b <= data_read;
|
||||||
|
when x"BF" => reg_b <= inst_bus;
|
||||||
|
|
||||||
|
when others => null;
|
||||||
|
end case;
|
||||||
|
|
||||||
|
|
||||||
|
case inst_reg is
|
||||||
|
-- jump imm addr abs
|
||||||
|
when x"D0" => reg_pc <= inst_bus;
|
||||||
|
-- jump imm addr rel
|
||||||
|
when x"D1" => reg_pc <= reg_pc+inst_bus;
|
||||||
|
|
||||||
|
when others => reg_pc <= reg_pc+1;
|
||||||
|
end case;
|
||||||
|
|
||||||
|
-- report "end reg_a = " & integer'image(to_integer(unsigned(reg_a)))
|
||||||
|
-- & " reg_b = " & integer'image(to_integer(unsigned(reg_b)))
|
||||||
|
-- & " reg_out = " & integer'image(to_integer(unsigned(reg_out)))
|
||||||
|
-- & " reg_pc = " & integer'image(to_integer(unsigned(reg_pc)))
|
||||||
|
-- & " inst_bus = " & integer'image(to_integer(unsigned(inst_bus)));
|
||||||
|
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
end process;
|
||||||
|
end description;
|
||||||
|
|
@ -6,19 +6,18 @@ use ieee.numeric_std.all;
|
||||||
entity circuit is
|
entity circuit is
|
||||||
port (
|
port (
|
||||||
clk: in std_logic; -- 500 Hz, period 2 ms
|
clk: in std_logic; -- 500 Hz, period 2 ms
|
||||||
key: in std_logic_vector(3 downto 0); -- active low
|
key: in std_logic_vector(31 downto 0); -- active low
|
||||||
sw: in std_logic_vector(9 downto 0); -- active high
|
sw: in std_logic_vector(31 downto 0); -- active high
|
||||||
led: out std_logic_vector(9 downto 0) := (others => '0'); -- active high
|
led: out std_logic_vector(31 downto 0) := (others => '0'); -- active high
|
||||||
hex0: out std_logic_vector(6 downto 0) := (others => '0'); -- active low
|
hex: out std_logic_vector(31 downto 0) := (others => '0') -- active low
|
||||||
hex1: out std_logic_vector(6 downto 0) := (others => '0') -- active low
|
|
||||||
);
|
);
|
||||||
end circuit;
|
end circuit;
|
||||||
|
|
||||||
|
|
||||||
architecture description of circuit is
|
architecture description of circuit is
|
||||||
signal counter: unsigned(9 downto 0) := "0000000000";
|
signal counter: unsigned(31 downto 0) := x"00000000";
|
||||||
begin
|
begin
|
||||||
led <= std_logic_vector(counter(9 downto 0));
|
led <= std_logic_vector(counter);
|
||||||
process(clk)
|
process(clk)
|
||||||
begin
|
begin
|
||||||
counter <= counter+1;
|
counter <= counter+1;
|
||||||
|
|
|
||||||
320
rtl/circuit.vhdl
320
rtl/circuit.vhdl
|
|
@ -5,325 +5,23 @@ use ieee.numeric_std.all;
|
||||||
-- Do not modify the following entity block
|
-- Do not modify the following entity block
|
||||||
entity circuit is
|
entity circuit is
|
||||||
port (
|
port (
|
||||||
clk: in std_logic;
|
clk: in std_logic; -- 500 Hz, period 2 ms
|
||||||
key: in std_logic_vector(31 downto 0); -- active low
|
key: in std_logic_vector(31 downto 0); -- active low
|
||||||
sw: 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); -- active high
|
led: out std_logic_vector(31 downto 0) := (others => '0'); -- active high
|
||||||
hex: out std_logic_vector(31 downto 0) -- active low
|
hex: out std_logic_vector(31 downto 0) := (others => '0') -- active low
|
||||||
);
|
);
|
||||||
end circuit;
|
end circuit;
|
||||||
|
|
||||||
|
|
||||||
library ieee;
|
|
||||||
use ieee.std_logic_1164.all;
|
|
||||||
use ieee.numeric_std.all;
|
|
||||||
|
|
||||||
entity alu is
|
|
||||||
Port (
|
|
||||||
func: in unsigned(3 downto 0);
|
|
||||||
a: in unsigned(7 downto 0);
|
|
||||||
b: in unsigned(7 downto 0);
|
|
||||||
carry_in: in std_logic;
|
|
||||||
o: out unsigned(7 downto 0);
|
|
||||||
carry_out: out std_logic;
|
|
||||||
zero: out std_logic;
|
|
||||||
gt: out std_logic;
|
|
||||||
lt: out std_logic;
|
|
||||||
eq: out std_logic
|
|
||||||
);
|
|
||||||
end alu;
|
|
||||||
|
|
||||||
architecture Behavioral of alu is
|
|
||||||
signal tmp: unsigned(8 downto 0);
|
|
||||||
begin
|
|
||||||
with func select
|
|
||||||
tmp <= ("0"&a) + ("0"&b) when x"0",
|
|
||||||
("0"&a) + ("0"&b) + (x"00"&carry_in) when x"1",
|
|
||||||
("0"&a) - ("0"&b) when x"2",
|
|
||||||
("0"&a) - ("0"&b) - (x"00"&carry_in) when x"3",
|
|
||||||
("0"&a) and ("0"&b) when x"4",
|
|
||||||
("0"&a) or ("0"&b) when x"5",
|
|
||||||
("0"&a) xor ("0"&b) when x"6",
|
|
||||||
"0"&x"00" when others;
|
|
||||||
|
|
||||||
zero <= '1' when tmp = 0 else '0';
|
|
||||||
eq <= '1' when a = b else '0';
|
|
||||||
lt <= '1' when a < b else '0';
|
|
||||||
gt <= '1' when a > b else '0';
|
|
||||||
carry_out <= tmp(8);
|
|
||||||
o <= tmp(7 downto 0);
|
|
||||||
|
|
||||||
end Behavioral ; -- Behavioral
|
|
||||||
|
|
||||||
library ieee;
|
|
||||||
use ieee.std_logic_1164.all;
|
|
||||||
use ieee.numeric_std.all;
|
|
||||||
|
|
||||||
entity ram_8x256 is
|
|
||||||
Port (
|
|
||||||
clk : in std_logic;
|
|
||||||
we : in std_logic; -- write enable
|
|
||||||
addr : in unsigned(7 downto 0); -- 8-bit address
|
|
||||||
din : in unsigned(7 downto 0); -- data input
|
|
||||||
dout : out unsigned(7 downto 0) -- data output
|
|
||||||
);
|
|
||||||
end ram_8x256;
|
|
||||||
|
|
||||||
architecture Behavioral of ram_8x256 is
|
|
||||||
type ram_type is array (0 to 255) of unsigned(7 downto 0);
|
|
||||||
signal ram : ram_type := (others => x"AB");
|
|
||||||
begin
|
|
||||||
process(clk)
|
|
||||||
begin
|
|
||||||
if rising_edge(clk) then
|
|
||||||
if we = '1' then
|
|
||||||
ram(to_integer(unsigned(addr))) <= din;
|
|
||||||
end if;
|
|
||||||
|
|
||||||
dout <= ram(to_integer(unsigned(addr)));
|
|
||||||
end if;
|
|
||||||
end process;
|
|
||||||
end Behavioral;
|
|
||||||
|
|
||||||
|
|
||||||
library ieee;
|
|
||||||
use ieee.std_logic_1164.all;
|
|
||||||
use ieee.numeric_std.all;
|
|
||||||
|
|
||||||
entity inst_ram_8x256 is
|
|
||||||
Port (
|
|
||||||
clk : in std_logic;
|
|
||||||
addr : in unsigned(7 downto 0); -- 8-bit address
|
|
||||||
dout : out unsigned(7 downto 0) -- data output
|
|
||||||
);
|
|
||||||
end inst_ram_8x256;
|
|
||||||
|
|
||||||
architecture Behavioral of inst_ram_8x256 is
|
|
||||||
type ram_type is array (0 to 255) of unsigned(7 downto 0);
|
|
||||||
signal ram : ram_type := (
|
|
||||||
0 => x"A0", -- 0 => a
|
|
||||||
2 => x"B1", -- 1 => b
|
|
||||||
3 => x"10", -- a+b => out
|
|
||||||
4 => x"AE", -- out => a
|
|
||||||
5 => x"10", -- a+b => out
|
|
||||||
6 => x"BE", -- out => b
|
|
||||||
7 => x"D0", -- jump to 3
|
|
||||||
8 => x"03",
|
|
||||||
others => (others => '0')
|
|
||||||
);
|
|
||||||
begin
|
|
||||||
process(clk)
|
|
||||||
begin
|
|
||||||
if rising_edge(clk) or falling_edge(clk) then
|
|
||||||
dout <= ram(to_integer(unsigned(addr)));
|
|
||||||
end if;
|
|
||||||
end process;
|
|
||||||
end Behavioral;
|
|
||||||
|
|
||||||
|
|
||||||
library ieee;
|
|
||||||
use ieee.std_logic_1164.all;
|
|
||||||
use ieee.numeric_std.all;
|
|
||||||
|
|
||||||
|
|
||||||
architecture description of circuit is
|
architecture description of circuit is
|
||||||
signal clock: std_logic;
|
signal counter: unsigned(9 downto 0) := "0000000000";
|
||||||
|
|
||||||
signal reg_pc: unsigned(7 downto 0) := "00000000";
|
|
||||||
signal reg_a: unsigned(7 downto 0) := "00000000";
|
|
||||||
signal reg_b: unsigned(7 downto 0) := "00000000";
|
|
||||||
signal reg_out: unsigned(7 downto 0) := "00000000";
|
|
||||||
|
|
||||||
signal inst_reg: unsigned(7 downto 0);
|
|
||||||
signal inst_bus: unsigned(7 downto 0);
|
|
||||||
|
|
||||||
signal data_read: unsigned(7 downto 0);
|
|
||||||
signal data_write: unsigned(7 downto 0);
|
|
||||||
|
|
||||||
signal data_addr: unsigned(7 downto 0) := "00000000";
|
|
||||||
signal data_write_e: std_logic := '0';
|
|
||||||
|
|
||||||
signal flag_carry: std_logic := '0';
|
|
||||||
signal flag_lt: std_logic := '0';
|
|
||||||
signal flag_gt: std_logic := '0';
|
|
||||||
signal flag_eq: std_logic := '0';
|
|
||||||
signal flag_zero: std_logic := '0';
|
|
||||||
|
|
||||||
signal alu_a, alu_b, alu_o : unsigned(7 downto 0);
|
|
||||||
signal alu_func : unsigned(3 downto 0);
|
|
||||||
signal alu_carry, alu_zero, alu_gt, alu_lt, alu_eq : std_logic;
|
|
||||||
|
|
||||||
function dec7seg(val: unsigned(3 downto 0)) return std_logic_vector is
|
|
||||||
begin
|
begin
|
||||||
case val is
|
led(9 downto 0) <= std_logic_vector(counter);
|
||||||
when "0000"=> return "1000000"; --0
|
-- led(10) <= clk;
|
||||||
when "0001"=> return "1111001"; --1
|
process(sw(0))
|
||||||
when "0010"=> return "0100100"; --2
|
|
||||||
when "0011"=> return "0110000"; --3
|
|
||||||
when "0100"=> return "0011001"; --4
|
|
||||||
when "0101"=> return "0010010"; --5
|
|
||||||
when "0110"=> return "0000010"; --6
|
|
||||||
when "0111"=> return "1111000"; --7
|
|
||||||
when "1000"=> return "0000000"; --8
|
|
||||||
when "1001"=> return "0011000"; --9
|
|
||||||
when "1010"=> return "0001000"; --A
|
|
||||||
when "1011"=> return "0000011"; --B
|
|
||||||
when "1100"=> return "1000110"; --C
|
|
||||||
when "1101"=> return "0100001"; --D
|
|
||||||
when "1110"=> return "0000110"; --E
|
|
||||||
when "1111"=> return "0001110"; --F
|
|
||||||
when others=> return "1111111"; ---
|
|
||||||
end case;
|
|
||||||
end function;
|
|
||||||
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
counter <= counter+1;
|
||||||
-- hex(7 downto 4) <= dec7seg(reg_out(7 downto 4));
|
report "meow";
|
||||||
-- hex(3 downto 0) <= dec7seg(reg_out(3 downto 0));
|
|
||||||
|
|
||||||
clock <= clk when sw(9) = '1' else sw(8);
|
|
||||||
|
|
||||||
ram_inst : entity work.inst_ram_8x256
|
|
||||||
port map(
|
|
||||||
clk => clk,
|
|
||||||
addr => reg_pc,
|
|
||||||
dout => inst_bus
|
|
||||||
);
|
|
||||||
|
|
||||||
ram_data : entity work.ram_8x256
|
|
||||||
port map(
|
|
||||||
clk => clk,
|
|
||||||
we => data_write_e,
|
|
||||||
addr => data_addr,
|
|
||||||
din => data_write,
|
|
||||||
dout => data_read
|
|
||||||
);
|
|
||||||
|
|
||||||
alu : entity work.alu
|
|
||||||
port map(
|
|
||||||
func => alu_func,
|
|
||||||
a => alu_a,
|
|
||||||
b => alu_b,
|
|
||||||
carry_in => flag_carry,
|
|
||||||
o => alu_o,
|
|
||||||
carry_out => alu_carry,
|
|
||||||
zero => alu_zero,
|
|
||||||
gt => alu_gt,
|
|
||||||
lt => alu_lt,
|
|
||||||
eq => alu_eq
|
|
||||||
);
|
|
||||||
|
|
||||||
process(clock)
|
|
||||||
variable out_extended : unsigned(8 downto 0);
|
|
||||||
begin
|
|
||||||
|
|
||||||
if rising_edge(clock) then
|
|
||||||
inst_reg <= inst_bus;
|
|
||||||
data_write_e <= '0';
|
|
||||||
|
|
||||||
report "begin reg_a = " & integer'image(to_integer(unsigned(reg_a)))
|
|
||||||
& " reg_b = " & integer'image(to_integer(unsigned(reg_b)))
|
|
||||||
& " reg_out = " & integer'image(to_integer(unsigned(reg_out)))
|
|
||||||
& " reg_pc = " & integer'image(to_integer(unsigned(reg_pc)))
|
|
||||||
& " inst_bus = " & integer'image(to_integer(unsigned(inst_bus)));
|
|
||||||
|
|
||||||
-- alu operations a,b
|
|
||||||
if inst_reg(7 downto 4) = x"1" then
|
|
||||||
alu_func <= inst_reg(3 downto 0);
|
|
||||||
alu_a <= reg_a;
|
|
||||||
alu_b <= reg_b;
|
|
||||||
reg_out <= alu_o;
|
|
||||||
end if;
|
|
||||||
-- alu operations a,imm
|
|
||||||
if inst_reg(7 downto 4) = x"2" then
|
|
||||||
alu_func <= inst_reg(3 downto 0);
|
|
||||||
alu_a <= reg_a;
|
|
||||||
reg_pc <= reg_pc+1;
|
|
||||||
end if;
|
|
||||||
-- alu operations imm,b
|
|
||||||
if inst_reg(7 downto 4) = x"3" then
|
|
||||||
alu_func <= inst_reg(3 downto 0);
|
|
||||||
alu_b <= reg_b;
|
|
||||||
reg_pc <= reg_pc+1;
|
|
||||||
end if;
|
|
||||||
|
|
||||||
case inst_bus is
|
|
||||||
-- nop
|
|
||||||
when x"00" => null;
|
|
||||||
|
|
||||||
|
|
||||||
-- 0 => a
|
|
||||||
when x"A0" => reg_a <= x"00";
|
|
||||||
-- 1 => a
|
|
||||||
when x"A1" => reg_a <= x"01";
|
|
||||||
-- mem[reg b] => a
|
|
||||||
when x"AC" =>
|
|
||||||
data_addr <= reg_b;
|
|
||||||
-- out => a
|
|
||||||
when x"AE" => reg_a <= reg_out;
|
|
||||||
-- immediate => a
|
|
||||||
when x"AF" => reg_pc <= reg_pc+1;
|
|
||||||
|
|
||||||
-- 0 => b
|
|
||||||
when x"B0" => reg_b <= x"00";
|
|
||||||
-- 1 => b
|
|
||||||
when x"B1" => reg_b <= x"01";
|
|
||||||
-- mem[reg a] => b
|
|
||||||
when x"BC" =>
|
|
||||||
data_addr <= reg_b;
|
|
||||||
-- out => b
|
|
||||||
when x"BE" => reg_b <= reg_out;
|
|
||||||
-- immediate => b
|
|
||||||
when x"BF" => reg_pc <= reg_pc+1;
|
|
||||||
|
|
||||||
-- conditional
|
|
||||||
|
|
||||||
-- jump imm addr abs
|
|
||||||
when x"D0" => reg_pc <= reg_pc+1;
|
|
||||||
-- jump imm addr rel
|
|
||||||
when x"D1" => reg_pc <= reg_pc+1;
|
|
||||||
-- jump addr reg a
|
|
||||||
when x"DA" => reg_pc <= reg_a-1;
|
|
||||||
-- jump addr reg b
|
|
||||||
when x"DB" => reg_pc <= reg_b-1;
|
|
||||||
|
|
||||||
-- halt
|
|
||||||
when x"FF" => reg_pc <= reg_pc-1;
|
|
||||||
|
|
||||||
when others => null;
|
|
||||||
end case;
|
|
||||||
end if;
|
|
||||||
|
|
||||||
if falling_edge(clock) then
|
|
||||||
case inst_reg is
|
|
||||||
when x"AC" => reg_a <= data_read;
|
|
||||||
when x"AF" => reg_a <= inst_bus;
|
|
||||||
|
|
||||||
when x"BC" => reg_b <= data_read;
|
|
||||||
when x"BF" => reg_b <= inst_bus;
|
|
||||||
|
|
||||||
when others => null;
|
|
||||||
end case;
|
|
||||||
|
|
||||||
|
|
||||||
case inst_reg is
|
|
||||||
-- jump imm addr abs
|
|
||||||
when x"D0" => reg_pc <= inst_bus;
|
|
||||||
-- jump imm addr rel
|
|
||||||
when x"D1" => reg_pc <= reg_pc+inst_bus;
|
|
||||||
|
|
||||||
when others => reg_pc <= reg_pc+1;
|
|
||||||
end case;
|
|
||||||
|
|
||||||
report "end reg_a = " & integer'image(to_integer(unsigned(reg_a)))
|
|
||||||
& " reg_b = " & integer'image(to_integer(unsigned(reg_b)))
|
|
||||||
& " reg_out = " & integer'image(to_integer(unsigned(reg_out)))
|
|
||||||
& " reg_pc = " & integer'image(to_integer(unsigned(reg_pc)))
|
|
||||||
& " inst_bus = " & integer'image(to_integer(unsigned(inst_bus)));
|
|
||||||
|
|
||||||
|
|
||||||
end if;
|
|
||||||
|
|
||||||
end process;
|
end process;
|
||||||
end description;
|
end description;
|
||||||
|
|
@ -71,18 +71,18 @@ begin
|
||||||
variable sw_i : integer;
|
variable sw_i : integer;
|
||||||
variable key_i : integer;
|
variable key_i : integer;
|
||||||
begin
|
begin
|
||||||
ffi_init; -- starts Rust listener thread
|
ffi_init;
|
||||||
wait for 0 ns;
|
wait for 0 ns;
|
||||||
|
|
||||||
|
|
||||||
while true loop
|
while true loop
|
||||||
wait until rising_edge(clk) or falling_edge(clk);
|
wait until rising_edge(clk) or falling_edge(clk);
|
||||||
|
wait for 0 ns;
|
||||||
|
|
||||||
sw_i := ffi_get_sw;
|
sw_i := ffi_get_sw;
|
||||||
key_i := ffi_get_key;
|
key_i := ffi_get_key;
|
||||||
|
|
||||||
sw <= std_logic_vector(to_unsigned(sw_i, 32));
|
sw <= std_logic_vector(to_signed(sw_i, 32));
|
||||||
key <= std_logic_vector(to_unsigned(key_i, 32));
|
key <= std_logic_vector(to_signed(key_i, 32));
|
||||||
|
|
||||||
ffi_set_outputs(
|
ffi_set_outputs(
|
||||||
to_integer(unsigned(clean_slv(led))),
|
to_integer(unsigned(clean_slv(led))),
|
||||||
|
|
|
||||||
18
run.sh
18
run.sh
|
|
@ -3,7 +3,6 @@ set -euo pipefail
|
||||||
|
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
|
|
||||||
# Build Rust shared library
|
|
||||||
pushd conn >/dev/null
|
pushd conn >/dev/null
|
||||||
cargo build --release
|
cargo build --release
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
@ -14,21 +13,10 @@ LIBSRC="../conn/target/release/libvhdl_ui.a"
|
||||||
LIBDIR="../conn/target/release"
|
LIBDIR="../conn/target/release"
|
||||||
|
|
||||||
|
|
||||||
ghdl -a --std=08 ../rtl/*.vhdl
|
ghdl -a -g --std=08 ../rtl/*.vhdl
|
||||||
|
|
||||||
ghdl -e --std=08 \
|
ghdl -e --std=08 \
|
||||||
-Wl,"$LIBSRC" \
|
-Wl,$LIBSRC \
|
||||||
-Wl,-Wl,-rpath -Wl,-Wl,$LIBDIR \
|
|
||||||
tb
|
tb
|
||||||
|
|
||||||
|
ghdl -r --std=08 tb --stop-delta=2147483647
|
||||||
echo "=== Running sim ==="
|
|
||||||
echo "Connect and stream inputs using:"
|
|
||||||
echo " nc 127.0.0.1 5555"
|
|
||||||
echo "Then type lines like:"
|
|
||||||
echo " sw=1"
|
|
||||||
echo " key=15"
|
|
||||||
echo " key=7 (press KEY3 if bit3 becomes 0, etc; active-low)"
|
|
||||||
echo
|
|
||||||
|
|
||||||
ghdl -r --std=08 tb
|
|
||||||
11
run_relay.sh
Executable file
11
run_relay.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
pushd conn >/dev/null
|
||||||
|
cargo build --release
|
||||||
|
popd >/dev/null
|
||||||
|
|
||||||
|
|
||||||
|
pushd relay >/dev/null
|
||||||
|
cargo run --release
|
||||||
|
popd >/dev/null
|
||||||
Loading…
Add table
Add a link
Reference in a new issue