From 26856b58ce2b8afe744fa2a5669cb142b115f2c2 Mon Sep 17 00:00:00 2001 From: Martijn Bastiaan Date: Wed, 4 Dec 2024 13:59:21 +0100 Subject: [PATCH 1/2] Squashed 'clash-vexriscv/' changes from 0ac4a4f7..2eced423 2eced423 Add optional VCD tracing (#41) 652280ad Merge pull request #39 from clash-lang/rs/jtagChainTest 6f1c2a20 Add JTAG chaining test. 72904a38 Expose JTAG output port, and move debug config into a standalone file. git-subtree-dir: clash-vexriscv git-subtree-split: 2eced42396cc46cbabc0376ebc4e3ad2335b8f9c --- .cargo/config.toml | 3 +- .envrc | 4 +- .git-blame-ignore-revs | 5 - .gitattributes | 6 - .github/cabal.project | 20 +- .github/docker/Dockerfile | 140 +- .../docker/build-and-publish-docker-image.sh | 0 .github/docker/build-and-publish.sh | 30 - .github/scripts/all_check.py | 35 - .github/scripts/cabal-gild.sh | 9 - .github/scripts/cache.py | 376 -- .github/scripts/cache_server_setup.sh | 36 - .github/scripts/check_eol_whitespace.sh | 17 - .github/scripts/check_missing_eof_newline.sh | 19 - .github/scripts/fix_spdx_header.py | 56 - .github/scripts/force_expensive_checks.sh | 22 - .github/scripts/fourmolu.sh | 17 - .github/scripts/generate_checks.sh | 19 - .github/scripts/get_formal_checks.sh | 22 - .github/scripts/register_runners.sh | 116 - .github/scripts/run_riscv_formal_check.sh | 18 - .github/scripts/self_hosted_docker_check.py | 38 - .github/scripts/with_vivado.sh | 16 - .github/synthesis/all.json | 28 - .github/synthesis/staging.json | 9 - .github/systemd/README.md | 29 - .github/systemd/vivado-hw-server.service | 17 - .github/systemd/vivado-hw-server.service.conf | 2 - .github/workflows/ci.yml | 742 +--- .gitignore | 16 +- .reuse/dep5 | 53 +- .vscode/schemas/hitl-test-schema.json | 79 - .vscode/settings.json | 29 +- clash-vexriscv/Cargo.lock => Cargo.lock | 0 .../Cargo.lock.license => Cargo.lock.license | 0 clash-vexriscv/Cargo.toml => Cargo.toml | 0 bittide-instances/LICENSE => LICENSE | 1 - LICENSES/CC-BY-4.0.txt | 156 - Makefile | 44 - README.md | 66 +- bittide-experiments/bittide-experiments.cabal | 157 - .../bittide-experiments.cabal.license | 3 - .../src/Bittide/Github/Artifacts.hs | 200 -- bittide-experiments/src/Bittide/Hitl.hs | 334 -- bittide-experiments/src/Bittide/Plot.hs | 237 -- .../src/Bittide/Report/ClockControl.hs | 352 -- .../src/Bittide/Simulate/Config.hs | 110 - bittide-experiments/src/Bittide/Topology.hs | 773 ---- bittide-experiments/tests/doctests.hs | 14 - bittide-experiments/tests/unittests.hs | 17 - bittide-extra/LICENSE | 202 -- bittide-extra/bittide-extra.cabal | 128 - bittide-extra/bittide-extra.cabal.license | 3 - bittide-extra/src/Bittide/Extra/Maybe.hs | 48 - bittide-extra/src/Bittide/Extra/Wishbone.hs | 120 - .../src/Clash/Explicit/Signal/Extra.hs | 19 - bittide-extra/src/Clash/Sized/Vector/Extra.hs | 3171 ----------------- bittide-extra/src/Numeric/Extra.hs | 30 - bittide-extra/tests/doctests/Main.hs | 14 - bittide-extra/tests/unittests/Main.hs | 30 - .../tests/unittests/Tests/Numeric/Extra.hs | 91 - bittide-instances/README.md | 25 - bittide-instances/bittide-instances.cabal | 284 -- .../bittide-instances.cabal.license | 3 - .../data/constraints/boardTestExtended.xdc | 18 - .../data/constraints/boardTestSimple.xdc | 18 - .../data/constraints/extraProbesTest.xdc | 14 - .../data/constraints/fincFdecTests.xdc | 52 - .../data/constraints/fullMeshHwCcTest.xdc | 44 - .../constraints/fullMeshHwCcWithRiscvTest.xdc | 1 - .../data/constraints/fullMeshSwCcTest.xdc | 1 - .../data/constraints/hwCcTopologyTest.xdc | 1 - .../data/constraints/jtag_config.xdc | 249 -- .../data/constraints/jtag_pmod0.xdc | 13 - .../data/constraints/jtag_pmod1.xdc | 18 - .../constraints/linkConfigurationTest.xdc | 44 - bittide-instances/data/constraints/sgmii.xdc | 10 - .../data/constraints/syncInSyncOut.xdc | 11 - .../data/constraints/temperatureMonitor.xdc | 17 - .../data/constraints/transceiversUpTest.xdc | 44 - .../data/constraints/vexRiscEthernetTop.xdc | 8 - .../data/constraints/vexRiscvTcpTest.xdc | 8 - .../data/constraints/vexRiscvTest.xdc | 17 - .../data/gdb/smoltcp-hitl-prog.gdb | 9 - bittide-instances/data/gdb/test-gdb-prog.gdb | 29 - bittide-instances/data/openocd/ports.tcl | 7 - bittide-instances/data/openocd/sipeed.tcl | 35 - bittide-instances/data/openocd/start.sh | 6 - .../data/openocd/vexriscv_init.tcl | 59 - bittide-instances/data/picocom/start.sh | 11 - bittide-instances/data/tcpspray/start.sh | 7 - .../data/test_configs/extraProbesTest.yml | 43 - bittide-instances/exe/clash/Main.hs | 10 - .../exe/post-board-test-extended/Main.hs | 32 - .../exe/post-fullMeshSwCcTest/Main.hs | 132 - .../exe/post-vex-riscv-tcp-test/Main.hs | 130 - .../exe/post-vex-riscv-test/Main.hs | 88 - bittide-instances/imgs/reducePins.drawio | 187 - bittide-instances/imgs/reducePins.svg | 1 - .../src/Bittide/Instances/Domains.hs | 66 - .../src/Bittide/Instances/Hacks.hs | 78 - .../src/Bittide/Instances/Hitl/BoardTest.hs | 189 - .../src/Bittide/Instances/Hitl/Ethernet.hs | 94 - .../src/Bittide/Instances/Hitl/FincFdec.hs | 226 -- .../Bittide/Instances/Hitl/FullMeshHwCc.hs | 533 --- .../Bittide/Instances/Hitl/FullMeshSwCc.hs | 697 ---- .../Bittide/Instances/Hitl/HwCcTopologies.hs | 924 ----- .../src/Bittide/Instances/Hitl/IlaPlot.hs | 731 ---- .../Instances/Hitl/LinkConfiguration.hs | 291 -- .../Instances/Hitl/Post/BoardTestExtended.hs | 74 - .../Instances/Hitl/Post/PostProcess.hs | 95 - .../src/Bittide/Instances/Hitl/README.md | 104 - .../src/Bittide/Instances/Hitl/Setup.hs | 120 - .../Bittide/Instances/Hitl/SyncInSyncOut.hs | 174 - .../Instances/Hitl/TemperatureMonitor.hs | 117 - .../src/Bittide/Instances/Hitl/Tests.hs | 51 - .../Bittide/Instances/Hitl/Transceivers.hs | 250 -- .../src/Bittide/Instances/Hitl/VexRiscv.hs | 166 - .../src/Bittide/Instances/Pnr/Calendar.hs | 45 - .../src/Bittide/Instances/Pnr/ClockControl.hs | 28 - .../src/Bittide/Instances/Pnr/Counter.hs | 27 - .../Bittide/Instances/Pnr/ElasticBuffer.hs | 30 - .../src/Bittide/Instances/Pnr/Ethernet.hs | 188 - .../Instances/Pnr/ProcessingElement.hs | 87 - .../Bittide/Instances/Pnr/ScatterGather.hs | 118 - .../src/Bittide/Instances/Pnr/Si539xSpi.hs | 34 - .../Bittide/Instances/Pnr/StabilityChecker.hs | 22 - .../src/Bittide/Instances/Pnr/Synchronizer.hs | 21 - .../src/Paths/Bittide/Instances.hs | 9 - bittide-instances/src/Project/FilePath.hs | 77 - bittide-instances/src/Project/Handle.hs | 54 - bittide-instances/src/Project/Programs.hs | 55 - .../tests/Tests/OverflowResistantDiff.hs | 134 - bittide-instances/tests/Wishbone/Axi.hs | 160 - bittide-instances/tests/Wishbone/DnaPortE2.hs | 105 - bittide-instances/tests/Wishbone/Time.hs | 131 - bittide-instances/tests/doctests.hs | 14 - bittide-instances/tests/unittests.hs | 27 - bittide-shake/LICENSE | 202 -- bittide-shake/README.md | 99 - bittide-shake/bittide-shake.cabal | 90 - bittide-shake/bittide-shake.cabal.license | 3 - bittide-shake/data/scripts/get_watch_files.py | 96 - bittide-shake/data/scripts/shorten_names.py | 118 - bittide-shake/exe/Main.hs | 584 --- bittide-shake/src/Clash/Shake/Extra.hs | 94 - bittide-shake/src/Clash/Shake/Flags.hs | 32 - bittide-shake/src/Clash/Shake/Vivado.hs | 955 ----- bittide-shake/src/Development/Shake/Extra.hs | 20 - bittide-shake/src/Paths/Bittide/Shake.hs | 9 - bittide-shake/tests/doctests.hs | 14 - bittide-tools/LICENSE | 202 -- bittide-tools/bittide-tools.cabal | 126 - bittide-tools/bittide-tools.cabal.license | 3 - bittide-tools/clockcontrol/plot/Main.hs | 774 ---- bittide-tools/program/stream/Main.hs | 28 - bittide/LICENSE | 202 -- bittide/bittide.cabal | 244 -- bittide/bittide.cabal.license | 3 - bittide/data/clock_configs/README.md | 2 - .../Si5395J-200MHz-100ppb-Registers.csv | 622 ---- .../Si5395J-200MHz-100ppb.slabtimeproj | Bin 18376 -> 0 bytes .../Si5395J-200MHz-10ppb-Registers.csv | 622 ---- .../Si5395J-200MHz-10ppb-and-out1.csv | 616 ---- ...Si5395J-200MHz-10ppb-and-out1.slabtimeproj | Bin 12368 -> 0 bytes .../Si5395J-200MHz-10ppb.slabtimeproj | Bin 18376 -> 0 bytes .../Si5395J-200MHz-1ppb-Registers.csv | 622 ---- .../Si5395J-200MHz-1ppb.slabtimeproj | Bin 18392 -> 0 bytes .../Si5395J-200MHz-1ppm-Registers.csv | 622 ---- .../Si5395J-200MHz-1ppm.slabtimeproj | Bin 18392 -> 0 bytes .../Si5395J-200MHz-500ppb-Registers.csv | 622 ---- .../Si5395J-200MHz-500ppb.slabtimeproj | Bin 18392 -> 0 bytes bittide/linuwial-wrap-types.css | 13 - bittide/proofs/TypeNatProofs.agda | 267 -- bittide/src/Bittide/Arithmetic/PartsPer.hs | 110 - bittide/src/Bittide/Arithmetic/Time.hs | 153 - bittide/src/Bittide/Axi4.hs | 786 ---- bittide/src/Bittide/Axi4/Internal.hs | 258 -- bittide/src/Bittide/Calendar.hs | 524 --- bittide/src/Bittide/ClockControl.hs | 199 -- bittide/src/Bittide/ClockControl/Callisto.hs | 246 -- .../Bittide/ClockControl/Callisto/Types.hs | 85 - .../src/Bittide/ClockControl/Callisto/Util.hs | 111 - .../Bittide/ClockControl/ParseRegisters.hs | 91 - bittide/src/Bittide/ClockControl/Registers.hs | 84 - bittide/src/Bittide/ClockControl/Si5391A.hs | 889 ----- bittide/src/Bittide/ClockControl/Si5395J.hs | 52 - bittide/src/Bittide/ClockControl/Si539xSpi.hs | 438 --- .../Bittide/ClockControl/StabilityChecker.hs | 61 - bittide/src/Bittide/Counter.hs | 194 - bittide/src/Bittide/DoubleBufferedRam.hs | 617 ---- bittide/src/Bittide/ElasticBuffer.hs | 172 - bittide/src/Bittide/Ethernet/Mac.hs | 552 --- bittide/src/Bittide/Node.hs | 193 - bittide/src/Bittide/ProcessingElement.hs | 158 - .../ProcessingElement/DeviceTreeCompiler.hs | 59 - .../ProcessingElement/ProgramStream.hs | 93 - .../src/Bittide/ProcessingElement/ReadElf.hs | 57 - bittide/src/Bittide/ProcessingElement/Util.hs | 163 - bittide/src/Bittide/ScatterGather.hs | 275 -- bittide/src/Bittide/SharedTypes.hs | 202 -- bittide/src/Bittide/Switch.hs | 103 - bittide/src/Bittide/Transceiver.hs | 627 ---- bittide/src/Bittide/Transceiver/Cdc.hs | 64 - bittide/src/Bittide/Transceiver/Comma.hs | 49 - bittide/src/Bittide/Transceiver/Prbs.hs | 149 - .../src/Bittide/Transceiver/ResetManager.hs | 183 - bittide/src/Bittide/Transceiver/WordAlign.hs | 196 - bittide/src/Bittide/Wishbone.hs | 554 --- bittide/src/Clash/Cores/Extra.hs | 236 -- bittide/src/Clash/Cores/UART/Extra.hs | 87 - bittide/src/Clash/Cores/Xilinx/Extra.hs | 99 - bittide/src/Clash/Cores/Xilinx/GTH.hs | 7 - .../src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs | 279 -- .../src/Clash/Cores/Xilinx/GTH/Internal.hs | 112 - .../src/Clash/Cores/Xilinx/SystemMonitor.hs | 266 -- .../Cores/Xilinx/Xpm/Cdc/Handshake/Extra.hs | 73 - bittide/src/Clash/Explicit/Reset/Extra.hs | 51 - bittide/src/Clash/Functor/Extra.hs | 17 - bittide/src/Clash/Sized/Extra.hs | 33 - bittide/src/Data/Constraint/Nat/Extra.hs | 146 - bittide/src/System/IO/Temp/Extra.hs | 35 - bittide/tests/Tests/Axi4.hs | 345 -- bittide/tests/Tests/Axi4/Generators.hs | 141 - bittide/tests/Tests/Axi4/Properties.hs | 237 -- bittide/tests/Tests/Axi4/Types.hs | 119 - bittide/tests/Tests/Calendar.hs | 553 --- bittide/tests/Tests/ClockControl/Si539xSpi.hs | 84 - bittide/tests/Tests/Counter.hs | 80 - bittide/tests/Tests/DoubleBufferedRam.hs | 1085 ------ bittide/tests/Tests/ElasticBuffer.hs | 204 -- bittide/tests/Tests/Haxioms.hs | 27 - .../tests/Tests/ProcessingElement/ReadElf.hs | 295 -- bittide/tests/Tests/ScatterGather.hs | 398 --- bittide/tests/Tests/Shared.hs | 248 -- bittide/tests/Tests/StabilityChecker.hs | 81 - bittide/tests/Tests/Switch.hs | 112 - bittide/tests/Tests/Transceiver.hs | 461 --- bittide/tests/Tests/Transceiver/Prbs.hs | 134 - bittide/tests/Tests/Transceiver/WordAlign.hs | 235 -- bittide/tests/Tests/Wishbone.hs | 373 -- bittide/tests/UnitTests.hs | 71 - bittide/tests/doctests.hs | 14 - cabal.project | 163 +- cabal.project.freeze | 329 -- cargo.sh | 57 - .../.gitignore | 0 .../LICENSE | 0 .../README.md | 0 .../app/HdlTest.hs | 2 +- .../app/VexRiscvChainSimulation.hs | 307 ++ .../app/VexRiscvSimulation.hs | 52 +- .../bundle_test_binaries.sh | 0 .../clash-vexriscv-sim.cabal | 28 +- .../clash-vexriscv-sim.cabal.license | 0 .../data/vexriscv_chain_gdba.cfg | 0 .../data/vexriscv_chain_gdbb.cfg | 45 + .../data/vexriscv_chain_sim.cfg | 47 + clash-vexriscv-sim/data/vexriscv_gdb.cfg | 45 + .../data/vexriscv_sim.cfg | 0 .../src/Utils/Cpu.hs | 34 +- clash-vexriscv-sim/src/Utils/DebugConfig.hs | 34 + clash-vexriscv-sim/src/Utils/FilePath.hs | 38 + .../src/Utils/Instance.hs | 2 +- .../src/Utils/Interconnect.hs | 0 .../src/Utils/ProgramLoad.hs | 0 .../src/Utils/ReadElf.hs | 0 .../src/Utils/Storage.hs | 0 .../test-programs/Cargo.lock | 0 .../test-programs/Cargo.lock.license | 0 .../test-programs/Cargo.toml | 0 .../test-programs/build.rs | 0 .../test-programs/memory.x | 0 .../src/bin/dyn_dispatch.expected | 0 .../test-programs/src/bin/dyn_dispatch.rs | 0 .../src/bin/ebreak_exception.expected | 0 .../test-programs/src/bin/ebreak_exception.rs | 0 .../test-programs/src/bin/fpu_test.expected | 0 .../test-programs/src/bin/fpu_test.rs | 0 .../src/bin/hello_world.expected | 0 .../test-programs/src/bin/hello_world.rs | 0 .../src/bin/long_string.expected | 0 .../test-programs/src/bin/long_string.rs | 0 .../test-programs/src/bin/loop_write.expected | 0 .../test-programs/src/bin/loop_write.rs | 0 .../test-programs/src/bin/many_calls.expected | 0 .../test-programs/src/bin/many_calls.rs | 0 .../test-programs/src/bin/print_a.expected | 0 .../test-programs/src/bin/print_a.rs | 0 .../test-programs/src/bin/print_b.expected | 0 .../test-programs/src/bin/print_b.rs | 0 .../src/bin/print_numbers.expected | 0 .../test-programs/src/bin/print_numbers.rs | 0 .../src/bin/single_write.expected | 0 .../test-programs/src/bin/single_write.rs | 0 .../tests/Tests/Jtag.hs | 0 clash-vexriscv-sim/tests/Tests/JtagChain.hs | 173 + .../tests/tests.hs | 12 +- clash-vexriscv/.cargo/config.toml | 7 - clash-vexriscv/.envrc | 4 - clash-vexriscv/.github/cabal.project | 8 - clash-vexriscv/.github/docker/Dockerfile | 82 - clash-vexriscv/.github/workflows/ci.yml | 180 - clash-vexriscv/.gitignore | 71 +- clash-vexriscv/.reuse/dep5 | 16 - clash-vexriscv/.vscode/settings.json | 26 - clash-vexriscv/LICENSE | 1 + clash-vexriscv/LICENSES/Apache-2.0.txt | 73 - clash-vexriscv/LICENSES/CC0-1.0.txt | 121 - clash-vexriscv/LICENSES/MIT.txt | 9 - clash-vexriscv/{clash-vexriscv => }/Makefile | 0 clash-vexriscv/README.md | 36 +- clash-vexriscv/{clash-vexriscv => }/Setup.hs | 0 clash-vexriscv/cabal.project | 60 - clash-vexriscv/clash-vexriscv-sim/LICENSE | 202 -- .../{clash-vexriscv => }/clash-vexriscv.cabal | 0 .../clash-vexriscv.cabal.license | 0 clash-vexriscv/clash-vexriscv/.gitignore | 6 - clash-vexriscv/clash-vexriscv/LICENSE | 202 -- clash-vexriscv/clash-vexriscv/README.md | 29 - .../example-cpu/.gitignore | 0 .../example-cpu/ExampleCpu.yaml | 0 .../example-cpu/ExampleCpu.yaml.license | 0 .../example-cpu/VexRiscv.v | 4 +- .../example-cpu/VexRiscv.v.license | 0 .../example-cpu/build.sbt | 0 .../example-cpu/lib/update-vexriscv.py | 0 ...7ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar | Bin ...c8183f0ba7c51f7f0301d05eb8ced1.jar.license | 0 .../example-cpu/project/Dependencies.scala | 0 .../example-cpu/project/build.properties | 0 .../src/main/scala/example/ExampleCpu.scala | 4 +- clash-vexriscv/nix/nixpkgs.nix | 33 - clash-vexriscv/nix/openocd-vexriscv.nix | 41 - clash-vexriscv/nix/sources.json | 26 - clash-vexriscv/nix/sources.nix | 194 - clash-vexriscv/rust-toolchain.toml | 18 - clash-vexriscv/shell.nix | 52 - .../{clash-vexriscv => }/src/VexRiscv.hs | 55 +- .../src/VexRiscv/ClockTicks.hs | 0 .../{clash-vexriscv => }/src/VexRiscv/FFI.hsc | 12 +- .../src/VexRiscv/JtagTcpBridge.hs | 0 .../src/VexRiscv/Random.hs | 0 .../{clash-vexriscv => }/src/VexRiscv/TH.hs | 0 .../src/VexRiscv/VecToTuple.hs | 2 +- .../{clash-vexriscv => }/src/ffi/impl.cpp | 35 +- .../{clash-vexriscv => }/src/ffi/interface.h | 0 .../tests/unittests/Tests/Extra.hs | 0 .../unittests/Tests/VexRiscv/ClockTicks.hs | 0 .../tests/unittests/Tests/VexRiscv/Random.hs | 0 .../tests/unittests/main.hs | 0 default.nix | 8 - docs/code-of-conduct.md | 117 - docs/contributing.md | 35 - firmware-binaries/.cargo/config.toml | 11 - firmware-binaries/Cargo.lock | 452 --- firmware-binaries/Cargo.lock.license | 3 - firmware-binaries/Cargo.toml | 28 - .../clock-control-reg-cpy/Cargo.toml | 17 - .../clock-control-reg-cpy/build.rs | 23 - .../clock-control-reg-cpy/memory.x | 18 - .../clock-control-reg-cpy/src/main.rs | 36 - .../clock-control/Cargo.lock.license | 3 - firmware-binaries/clock-control/Cargo.toml | 17 - firmware-binaries/clock-control/build.rs | 23 - firmware-binaries/clock-control/memory.x | 18 - firmware-binaries/clock-control/src/main.rs | 49 - firmware-binaries/examples/.cargo/config.toml | 10 - firmware-binaries/examples/hello/Cargo.toml | 17 - firmware-binaries/examples/hello/build.rs | 23 - firmware-binaries/examples/hello/memory.x | 18 - firmware-binaries/examples/hello/src/main.rs | 55 - .../examples/smoltcp_echo/Cargo.toml | 21 - .../examples/smoltcp_echo/build.rs | 23 - .../examples/smoltcp_echo/memory.x | 18 - .../examples/smoltcp_echo/src/main.rs | 147 - .../Cargo.lock.license | 3 - .../processing-element-test/Cargo.toml | 15 - .../processing-element-test/build.rs | 23 - .../processing-element-test/memory.x | 18 - .../processing-element-test/src/main.rs | 57 - .../axi_stream_self_test/Cargo.toml | 17 - .../test-cases/axi_stream_self_test/build.rs | 23 - .../test-cases/axi_stream_self_test/memory.x | 18 - .../axi_stream_self_test/rust-toolchain.toml | 7 - .../axi_stream_self_test/src/main.rs | 37 - .../test-cases/dna_port_e2_test/Cargo.toml | 17 - .../test-cases/dna_port_e2_test/build.rs | 23 - .../test-cases/dna_port_e2_test/memory.x | 18 - .../test-cases/dna_port_e2_test/src/main.rs | 32 - .../test-cases/time_self_test/Cargo.toml | 17 - .../test-cases/time_self_test/build.rs | 23 - .../test-cases/time_self_test/memory.x | 18 - .../test-cases/time_self_test/src/main.rs | 39 - firmware-support/.cargo/config.toml | 6 - firmware-support/Cargo.lock | 707 ---- firmware-support/Cargo.lock.license | 3 - firmware-support/Cargo.toml | 9 - firmware-support/bittide-sys/Cargo.toml | 33 - firmware-support/bittide-sys/src/axi.rs | 176 - .../bittide-sys/src/axi/self_test.rs | 312 -- firmware-support/bittide-sys/src/callisto.rs | 147 - .../bittide-sys/src/clock_control.rs | 112 - .../bittide-sys/src/dna_port_e2.rs | 14 - .../bittide-sys/src/gather_unit.rs | 93 - firmware-support/bittide-sys/src/lib.rs | 105 - firmware-support/bittide-sys/src/mac.rs | 47 - .../bittide-sys/src/program_stream.rs | 126 - .../bittide-sys/src/scatter_unit.rs | 93 - firmware-support/bittide-sys/src/smoltcp.rs | 34 - .../bittide-sys/src/smoltcp/axi.rs | 132 - firmware-support/bittide-sys/src/time.rs | 417 --- .../bittide-sys/src/time/self_test.rs | 258 -- firmware-support/bittide-sys/src/uart.rs | 119 - firmware-support/bittide-sys/src/uart/log.rs | 67 - firmware-support/bittide-sys/src/utils.rs | 13 - .../bittide-sys/tests/elf_common.rs | 349 -- .../bittide-sys/tests/program_stream.rs | 164 - fourmolu.yaml | 5 - hie.yaml | 6 - hitl-setup/README.md | 12 - hitl-setup/dhcpd.conf | 10 - hitl-setup/fpganet | 7 - hitl-setup/fpganet.service | 27 - hitl-setup/setup.sh | 48 - host-tools/.cargo/config.toml | 6 - host-tools/Cargo.lock | 179 - host-tools/Cargo.lock.license | 3 - host-tools/Cargo.toml | 10 - host-tools/callisto-lib/Cargo.toml | 16 - host-tools/callisto-lib/src/lib.rs | 340 -- host-tools/dummy/Cargo.toml | 13 - host-tools/dummy/src/main.rs | 7 - nix/bin/cache | 3 - nix/bin/format | 14 - nix/bin/install-openocd-vexriscv-udev | 46 - nix/bin/shake | 16 - nix/mc.nix | 18 - nix/nixpkgs.nix | 6 +- nix/openocd-vexriscv.nix | 3 + nix/sources.json | 14 +- .../nix => nix}/sources.json.license | 0 .../nix => nix}/sources.nix.license | 0 nix/verilog-ethernet.nix | 28 - run_test.sh | 14 - rust-toolchain.toml | 19 +- scripts/diff-clash-vexriscv-subtree.py | 51 - scripts/update-clash-vexriscv-subtree.sh | 17 - shell.nix | 55 +- vivado-hs/LICENSE | 202 -- vivado-hs/src/Vivado.hs | 49 - vivado-hs/src/Vivado/Internal.hs | 266 -- vivado-hs/src/Vivado/Tcl.hs | 346 -- vivado-hs/tests/Tests/Vivado.hs | 78 - vivado-hs/tests/unittests.hs | 19 - vivado-hs/vivado-hs.cabal | 93 - vivado-hs/vivado-hs.cabal.license | 3 - 457 files changed, 1130 insertions(+), 48798 deletions(-) delete mode 100644 .git-blame-ignore-revs delete mode 100644 .gitattributes rename {clash-vexriscv/.github => .github}/docker/build-and-publish-docker-image.sh (100%) delete mode 100755 .github/docker/build-and-publish.sh delete mode 100755 .github/scripts/all_check.py delete mode 100755 .github/scripts/cabal-gild.sh delete mode 100755 .github/scripts/cache.py delete mode 100755 .github/scripts/cache_server_setup.sh delete mode 100755 .github/scripts/check_eol_whitespace.sh delete mode 100755 .github/scripts/check_missing_eof_newline.sh delete mode 100644 .github/scripts/fix_spdx_header.py delete mode 100755 .github/scripts/force_expensive_checks.sh delete mode 100755 .github/scripts/fourmolu.sh delete mode 100755 .github/scripts/generate_checks.sh delete mode 100755 .github/scripts/get_formal_checks.sh delete mode 100755 .github/scripts/register_runners.sh delete mode 100755 .github/scripts/run_riscv_formal_check.sh delete mode 100755 .github/scripts/self_hosted_docker_check.py delete mode 100755 .github/scripts/with_vivado.sh delete mode 100644 .github/synthesis/all.json delete mode 100644 .github/synthesis/staging.json delete mode 100644 .github/systemd/README.md delete mode 100644 .github/systemd/vivado-hw-server.service delete mode 100644 .github/systemd/vivado-hw-server.service.conf delete mode 100644 .vscode/schemas/hitl-test-schema.json rename clash-vexriscv/Cargo.lock => Cargo.lock (100%) rename clash-vexriscv/Cargo.lock.license => Cargo.lock.license (100%) rename clash-vexriscv/Cargo.toml => Cargo.toml (100%) rename bittide-instances/LICENSE => LICENSE (99%) delete mode 100644 LICENSES/CC-BY-4.0.txt delete mode 100644 Makefile delete mode 100644 bittide-experiments/bittide-experiments.cabal delete mode 100644 bittide-experiments/bittide-experiments.cabal.license delete mode 100644 bittide-experiments/src/Bittide/Github/Artifacts.hs delete mode 100644 bittide-experiments/src/Bittide/Hitl.hs delete mode 100644 bittide-experiments/src/Bittide/Plot.hs delete mode 100644 bittide-experiments/src/Bittide/Report/ClockControl.hs delete mode 100644 bittide-experiments/src/Bittide/Simulate/Config.hs delete mode 100644 bittide-experiments/src/Bittide/Topology.hs delete mode 100644 bittide-experiments/tests/doctests.hs delete mode 100644 bittide-experiments/tests/unittests.hs delete mode 100644 bittide-extra/LICENSE delete mode 100644 bittide-extra/bittide-extra.cabal delete mode 100644 bittide-extra/bittide-extra.cabal.license delete mode 100644 bittide-extra/src/Bittide/Extra/Maybe.hs delete mode 100644 bittide-extra/src/Bittide/Extra/Wishbone.hs delete mode 100644 bittide-extra/src/Clash/Explicit/Signal/Extra.hs delete mode 100644 bittide-extra/src/Clash/Sized/Vector/Extra.hs delete mode 100644 bittide-extra/src/Numeric/Extra.hs delete mode 100644 bittide-extra/tests/doctests/Main.hs delete mode 100644 bittide-extra/tests/unittests/Main.hs delete mode 100644 bittide-extra/tests/unittests/Tests/Numeric/Extra.hs delete mode 100644 bittide-instances/README.md delete mode 100644 bittide-instances/bittide-instances.cabal delete mode 100644 bittide-instances/bittide-instances.cabal.license delete mode 100644 bittide-instances/data/constraints/boardTestExtended.xdc delete mode 100644 bittide-instances/data/constraints/boardTestSimple.xdc delete mode 100644 bittide-instances/data/constraints/extraProbesTest.xdc delete mode 100644 bittide-instances/data/constraints/fincFdecTests.xdc delete mode 100644 bittide-instances/data/constraints/fullMeshHwCcTest.xdc delete mode 120000 bittide-instances/data/constraints/fullMeshHwCcWithRiscvTest.xdc delete mode 120000 bittide-instances/data/constraints/fullMeshSwCcTest.xdc delete mode 120000 bittide-instances/data/constraints/hwCcTopologyTest.xdc delete mode 100644 bittide-instances/data/constraints/jtag_config.xdc delete mode 100644 bittide-instances/data/constraints/jtag_pmod0.xdc delete mode 100644 bittide-instances/data/constraints/jtag_pmod1.xdc delete mode 100644 bittide-instances/data/constraints/linkConfigurationTest.xdc delete mode 100644 bittide-instances/data/constraints/sgmii.xdc delete mode 100644 bittide-instances/data/constraints/syncInSyncOut.xdc delete mode 100644 bittide-instances/data/constraints/temperatureMonitor.xdc delete mode 100644 bittide-instances/data/constraints/transceiversUpTest.xdc delete mode 100644 bittide-instances/data/constraints/vexRiscEthernetTop.xdc delete mode 100644 bittide-instances/data/constraints/vexRiscvTcpTest.xdc delete mode 100644 bittide-instances/data/constraints/vexRiscvTest.xdc delete mode 100644 bittide-instances/data/gdb/smoltcp-hitl-prog.gdb delete mode 100644 bittide-instances/data/gdb/test-gdb-prog.gdb delete mode 100644 bittide-instances/data/openocd/ports.tcl delete mode 100644 bittide-instances/data/openocd/sipeed.tcl delete mode 100755 bittide-instances/data/openocd/start.sh delete mode 100644 bittide-instances/data/openocd/vexriscv_init.tcl delete mode 100755 bittide-instances/data/picocom/start.sh delete mode 100755 bittide-instances/data/tcpspray/start.sh delete mode 100644 bittide-instances/data/test_configs/extraProbesTest.yml delete mode 100644 bittide-instances/exe/clash/Main.hs delete mode 100644 bittide-instances/exe/post-board-test-extended/Main.hs delete mode 100644 bittide-instances/exe/post-fullMeshSwCcTest/Main.hs delete mode 100644 bittide-instances/exe/post-vex-riscv-tcp-test/Main.hs delete mode 100644 bittide-instances/exe/post-vex-riscv-test/Main.hs delete mode 100644 bittide-instances/imgs/reducePins.drawio delete mode 100644 bittide-instances/imgs/reducePins.svg delete mode 100644 bittide-instances/src/Bittide/Instances/Domains.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hacks.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Ethernet.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/IlaPlot.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Post/BoardTestExtended.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Post/PostProcess.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/README.md delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Setup.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Tests.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/Calendar.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/ClockControl.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/Counter.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/ElasticBuffer.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/ScatterGather.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/Si539xSpi.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/StabilityChecker.hs delete mode 100644 bittide-instances/src/Bittide/Instances/Pnr/Synchronizer.hs delete mode 100644 bittide-instances/src/Paths/Bittide/Instances.hs delete mode 100644 bittide-instances/src/Project/FilePath.hs delete mode 100644 bittide-instances/src/Project/Handle.hs delete mode 100644 bittide-instances/src/Project/Programs.hs delete mode 100644 bittide-instances/tests/Tests/OverflowResistantDiff.hs delete mode 100644 bittide-instances/tests/Wishbone/Axi.hs delete mode 100644 bittide-instances/tests/Wishbone/DnaPortE2.hs delete mode 100644 bittide-instances/tests/Wishbone/Time.hs delete mode 100644 bittide-instances/tests/doctests.hs delete mode 100644 bittide-instances/tests/unittests.hs delete mode 100644 bittide-shake/LICENSE delete mode 100644 bittide-shake/README.md delete mode 100644 bittide-shake/bittide-shake.cabal delete mode 100644 bittide-shake/bittide-shake.cabal.license delete mode 100755 bittide-shake/data/scripts/get_watch_files.py delete mode 100644 bittide-shake/data/scripts/shorten_names.py delete mode 100644 bittide-shake/exe/Main.hs delete mode 100644 bittide-shake/src/Clash/Shake/Extra.hs delete mode 100644 bittide-shake/src/Clash/Shake/Flags.hs delete mode 100644 bittide-shake/src/Clash/Shake/Vivado.hs delete mode 100644 bittide-shake/src/Development/Shake/Extra.hs delete mode 100644 bittide-shake/src/Paths/Bittide/Shake.hs delete mode 100644 bittide-shake/tests/doctests.hs delete mode 100644 bittide-tools/LICENSE delete mode 100644 bittide-tools/bittide-tools.cabal delete mode 100644 bittide-tools/bittide-tools.cabal.license delete mode 100644 bittide-tools/clockcontrol/plot/Main.hs delete mode 100644 bittide-tools/program/stream/Main.hs delete mode 100644 bittide/LICENSE delete mode 100644 bittide/bittide.cabal delete mode 100644 bittide/bittide.cabal.license delete mode 100644 bittide/data/clock_configs/README.md delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-100ppb-Registers.csv delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-100ppb.slabtimeproj delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-10ppb-Registers.csv delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-10ppb-and-out1.csv delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-10ppb-and-out1.slabtimeproj delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-10ppb.slabtimeproj delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-1ppb-Registers.csv delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-1ppb.slabtimeproj delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-1ppm-Registers.csv delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-1ppm.slabtimeproj delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-500ppb-Registers.csv delete mode 100644 bittide/data/clock_configs/Si5395J-200MHz-500ppb.slabtimeproj delete mode 100644 bittide/linuwial-wrap-types.css delete mode 100644 bittide/proofs/TypeNatProofs.agda delete mode 100644 bittide/src/Bittide/Arithmetic/PartsPer.hs delete mode 100644 bittide/src/Bittide/Arithmetic/Time.hs delete mode 100644 bittide/src/Bittide/Axi4.hs delete mode 100644 bittide/src/Bittide/Axi4/Internal.hs delete mode 100644 bittide/src/Bittide/Calendar.hs delete mode 100644 bittide/src/Bittide/ClockControl.hs delete mode 100644 bittide/src/Bittide/ClockControl/Callisto.hs delete mode 100644 bittide/src/Bittide/ClockControl/Callisto/Types.hs delete mode 100644 bittide/src/Bittide/ClockControl/Callisto/Util.hs delete mode 100644 bittide/src/Bittide/ClockControl/ParseRegisters.hs delete mode 100644 bittide/src/Bittide/ClockControl/Registers.hs delete mode 100644 bittide/src/Bittide/ClockControl/Si5391A.hs delete mode 100644 bittide/src/Bittide/ClockControl/Si5395J.hs delete mode 100644 bittide/src/Bittide/ClockControl/Si539xSpi.hs delete mode 100644 bittide/src/Bittide/ClockControl/StabilityChecker.hs delete mode 100644 bittide/src/Bittide/Counter.hs delete mode 100644 bittide/src/Bittide/DoubleBufferedRam.hs delete mode 100644 bittide/src/Bittide/ElasticBuffer.hs delete mode 100644 bittide/src/Bittide/Ethernet/Mac.hs delete mode 100644 bittide/src/Bittide/Node.hs delete mode 100644 bittide/src/Bittide/ProcessingElement.hs delete mode 100644 bittide/src/Bittide/ProcessingElement/DeviceTreeCompiler.hs delete mode 100644 bittide/src/Bittide/ProcessingElement/ProgramStream.hs delete mode 100644 bittide/src/Bittide/ProcessingElement/ReadElf.hs delete mode 100644 bittide/src/Bittide/ProcessingElement/Util.hs delete mode 100644 bittide/src/Bittide/ScatterGather.hs delete mode 100644 bittide/src/Bittide/SharedTypes.hs delete mode 100644 bittide/src/Bittide/Switch.hs delete mode 100644 bittide/src/Bittide/Transceiver.hs delete mode 100644 bittide/src/Bittide/Transceiver/Cdc.hs delete mode 100644 bittide/src/Bittide/Transceiver/Comma.hs delete mode 100644 bittide/src/Bittide/Transceiver/Prbs.hs delete mode 100644 bittide/src/Bittide/Transceiver/ResetManager.hs delete mode 100644 bittide/src/Bittide/Transceiver/WordAlign.hs delete mode 100644 bittide/src/Bittide/Wishbone.hs delete mode 100644 bittide/src/Clash/Cores/Extra.hs delete mode 100644 bittide/src/Clash/Cores/UART/Extra.hs delete mode 100644 bittide/src/Clash/Cores/Xilinx/Extra.hs delete mode 100644 bittide/src/Clash/Cores/Xilinx/GTH.hs delete mode 100644 bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs delete mode 100644 bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs delete mode 100644 bittide/src/Clash/Cores/Xilinx/SystemMonitor.hs delete mode 100644 bittide/src/Clash/Cores/Xilinx/Xpm/Cdc/Handshake/Extra.hs delete mode 100644 bittide/src/Clash/Explicit/Reset/Extra.hs delete mode 100644 bittide/src/Clash/Functor/Extra.hs delete mode 100644 bittide/src/Clash/Sized/Extra.hs delete mode 100644 bittide/src/Data/Constraint/Nat/Extra.hs delete mode 100644 bittide/src/System/IO/Temp/Extra.hs delete mode 100644 bittide/tests/Tests/Axi4.hs delete mode 100644 bittide/tests/Tests/Axi4/Generators.hs delete mode 100644 bittide/tests/Tests/Axi4/Properties.hs delete mode 100644 bittide/tests/Tests/Axi4/Types.hs delete mode 100644 bittide/tests/Tests/Calendar.hs delete mode 100644 bittide/tests/Tests/ClockControl/Si539xSpi.hs delete mode 100644 bittide/tests/Tests/Counter.hs delete mode 100644 bittide/tests/Tests/DoubleBufferedRam.hs delete mode 100644 bittide/tests/Tests/ElasticBuffer.hs delete mode 100644 bittide/tests/Tests/Haxioms.hs delete mode 100644 bittide/tests/Tests/ProcessingElement/ReadElf.hs delete mode 100644 bittide/tests/Tests/ScatterGather.hs delete mode 100644 bittide/tests/Tests/Shared.hs delete mode 100644 bittide/tests/Tests/StabilityChecker.hs delete mode 100644 bittide/tests/Tests/Switch.hs delete mode 100644 bittide/tests/Tests/Transceiver.hs delete mode 100644 bittide/tests/Tests/Transceiver/Prbs.hs delete mode 100644 bittide/tests/Tests/Transceiver/WordAlign.hs delete mode 100644 bittide/tests/Tests/Wishbone.hs delete mode 100644 bittide/tests/UnitTests.hs delete mode 100644 bittide/tests/doctests.hs delete mode 100644 cabal.project.freeze delete mode 100755 cargo.sh rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/.gitignore (100%) rename {bittide-experiments => clash-vexriscv-sim}/LICENSE (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/README.md (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/app/HdlTest.hs (79%) create mode 100644 clash-vexriscv-sim/app/VexRiscvChainSimulation.hs rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/app/VexRiscvSimulation.hs (80%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/bundle_test_binaries.sh (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/clash-vexriscv-sim.cabal (85%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/clash-vexriscv-sim.cabal.license (100%) rename clash-vexriscv/clash-vexriscv-sim/data/vexriscv_gdb.cfg => clash-vexriscv-sim/data/vexriscv_chain_gdba.cfg (100%) create mode 100644 clash-vexriscv-sim/data/vexriscv_chain_gdbb.cfg create mode 100644 clash-vexriscv-sim/data/vexriscv_chain_sim.cfg create mode 100644 clash-vexriscv-sim/data/vexriscv_gdb.cfg rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/data/vexriscv_sim.cfg (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/src/Utils/Cpu.hs (88%) create mode 100644 clash-vexriscv-sim/src/Utils/DebugConfig.hs create mode 100644 clash-vexriscv-sim/src/Utils/FilePath.hs rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/src/Utils/Instance.hs (93%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/src/Utils/Interconnect.hs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/src/Utils/ProgramLoad.hs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/src/Utils/ReadElf.hs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/src/Utils/Storage.hs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/Cargo.lock (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/Cargo.lock.license (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/Cargo.toml (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/build.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/memory.x (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/dyn_dispatch.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/dyn_dispatch.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/ebreak_exception.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/ebreak_exception.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/fpu_test.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/fpu_test.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/hello_world.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/hello_world.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/long_string.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/long_string.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/loop_write.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/loop_write.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/many_calls.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/many_calls.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/print_a.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/print_a.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/print_b.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/print_b.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/print_numbers.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/print_numbers.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/single_write.expected (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/test-programs/src/bin/single_write.rs (100%) rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/tests/Tests/Jtag.hs (100%) create mode 100644 clash-vexriscv-sim/tests/Tests/JtagChain.hs rename {clash-vexriscv/clash-vexriscv-sim => clash-vexriscv-sim}/tests/tests.hs (92%) delete mode 100644 clash-vexriscv/.cargo/config.toml delete mode 100644 clash-vexriscv/.envrc delete mode 100644 clash-vexriscv/.github/cabal.project delete mode 100644 clash-vexriscv/.github/docker/Dockerfile delete mode 100644 clash-vexriscv/.github/workflows/ci.yml delete mode 100644 clash-vexriscv/.reuse/dep5 delete mode 100644 clash-vexriscv/.vscode/settings.json delete mode 100644 clash-vexriscv/LICENSES/Apache-2.0.txt delete mode 100644 clash-vexriscv/LICENSES/CC0-1.0.txt delete mode 100644 clash-vexriscv/LICENSES/MIT.txt rename clash-vexriscv/{clash-vexriscv => }/Makefile (100%) rename clash-vexriscv/{clash-vexriscv => }/Setup.hs (100%) delete mode 100644 clash-vexriscv/cabal.project delete mode 100644 clash-vexriscv/clash-vexriscv-sim/LICENSE rename clash-vexriscv/{clash-vexriscv => }/clash-vexriscv.cabal (100%) rename clash-vexriscv/{clash-vexriscv => }/clash-vexriscv.cabal.license (100%) delete mode 100644 clash-vexriscv/clash-vexriscv/.gitignore delete mode 100644 clash-vexriscv/clash-vexriscv/LICENSE delete mode 100644 clash-vexriscv/clash-vexriscv/README.md rename clash-vexriscv/{clash-vexriscv => }/example-cpu/.gitignore (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/ExampleCpu.yaml (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/ExampleCpu.yaml.license (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/VexRiscv.v (99%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/VexRiscv.v.license (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/build.sbt (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/lib/update-vexriscv.py (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar.license (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/project/Dependencies.scala (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/project/build.properties (100%) rename clash-vexriscv/{clash-vexriscv => }/example-cpu/src/main/scala/example/ExampleCpu.scala (97%) delete mode 100644 clash-vexriscv/nix/nixpkgs.nix delete mode 100644 clash-vexriscv/nix/openocd-vexriscv.nix delete mode 100644 clash-vexriscv/nix/sources.json delete mode 100644 clash-vexriscv/nix/sources.nix delete mode 100644 clash-vexriscv/rust-toolchain.toml delete mode 100644 clash-vexriscv/shell.nix rename clash-vexriscv/{clash-vexriscv => }/src/VexRiscv.hs (92%) rename clash-vexriscv/{clash-vexriscv => }/src/VexRiscv/ClockTicks.hs (100%) rename clash-vexriscv/{clash-vexriscv => }/src/VexRiscv/FFI.hsc (94%) rename clash-vexriscv/{clash-vexriscv => }/src/VexRiscv/JtagTcpBridge.hs (100%) rename clash-vexriscv/{clash-vexriscv => }/src/VexRiscv/Random.hs (100%) rename clash-vexriscv/{clash-vexriscv => }/src/VexRiscv/TH.hs (100%) rename clash-vexriscv/{clash-vexriscv => }/src/VexRiscv/VecToTuple.hs (99%) rename clash-vexriscv/{clash-vexriscv => }/src/ffi/impl.cpp (87%) rename clash-vexriscv/{clash-vexriscv => }/src/ffi/interface.h (100%) rename clash-vexriscv/{clash-vexriscv => }/tests/unittests/Tests/Extra.hs (100%) rename clash-vexriscv/{clash-vexriscv => }/tests/unittests/Tests/VexRiscv/ClockTicks.hs (100%) rename clash-vexriscv/{clash-vexriscv => }/tests/unittests/Tests/VexRiscv/Random.hs (100%) rename clash-vexriscv/{clash-vexriscv => }/tests/unittests/main.hs (100%) delete mode 100644 default.nix delete mode 100644 docs/code-of-conduct.md delete mode 100644 docs/contributing.md delete mode 100644 firmware-binaries/.cargo/config.toml delete mode 100644 firmware-binaries/Cargo.lock delete mode 100644 firmware-binaries/Cargo.lock.license delete mode 100644 firmware-binaries/Cargo.toml delete mode 100644 firmware-binaries/clock-control-reg-cpy/Cargo.toml delete mode 100644 firmware-binaries/clock-control-reg-cpy/build.rs delete mode 100644 firmware-binaries/clock-control-reg-cpy/memory.x delete mode 100644 firmware-binaries/clock-control-reg-cpy/src/main.rs delete mode 100644 firmware-binaries/clock-control/Cargo.lock.license delete mode 100644 firmware-binaries/clock-control/Cargo.toml delete mode 100644 firmware-binaries/clock-control/build.rs delete mode 100644 firmware-binaries/clock-control/memory.x delete mode 100644 firmware-binaries/clock-control/src/main.rs delete mode 100644 firmware-binaries/examples/.cargo/config.toml delete mode 100644 firmware-binaries/examples/hello/Cargo.toml delete mode 100644 firmware-binaries/examples/hello/build.rs delete mode 100644 firmware-binaries/examples/hello/memory.x delete mode 100644 firmware-binaries/examples/hello/src/main.rs delete mode 100644 firmware-binaries/examples/smoltcp_echo/Cargo.toml delete mode 100644 firmware-binaries/examples/smoltcp_echo/build.rs delete mode 100644 firmware-binaries/examples/smoltcp_echo/memory.x delete mode 100644 firmware-binaries/examples/smoltcp_echo/src/main.rs delete mode 100644 firmware-binaries/processing-element-test/Cargo.lock.license delete mode 100644 firmware-binaries/processing-element-test/Cargo.toml delete mode 100644 firmware-binaries/processing-element-test/build.rs delete mode 100644 firmware-binaries/processing-element-test/memory.x delete mode 100644 firmware-binaries/processing-element-test/src/main.rs delete mode 100644 firmware-binaries/test-cases/axi_stream_self_test/Cargo.toml delete mode 100644 firmware-binaries/test-cases/axi_stream_self_test/build.rs delete mode 100644 firmware-binaries/test-cases/axi_stream_self_test/memory.x delete mode 100644 firmware-binaries/test-cases/axi_stream_self_test/rust-toolchain.toml delete mode 100644 firmware-binaries/test-cases/axi_stream_self_test/src/main.rs delete mode 100644 firmware-binaries/test-cases/dna_port_e2_test/Cargo.toml delete mode 100644 firmware-binaries/test-cases/dna_port_e2_test/build.rs delete mode 100644 firmware-binaries/test-cases/dna_port_e2_test/memory.x delete mode 100644 firmware-binaries/test-cases/dna_port_e2_test/src/main.rs delete mode 100644 firmware-binaries/test-cases/time_self_test/Cargo.toml delete mode 100644 firmware-binaries/test-cases/time_self_test/build.rs delete mode 100644 firmware-binaries/test-cases/time_self_test/memory.x delete mode 100644 firmware-binaries/test-cases/time_self_test/src/main.rs delete mode 100644 firmware-support/.cargo/config.toml delete mode 100644 firmware-support/Cargo.lock delete mode 100644 firmware-support/Cargo.lock.license delete mode 100644 firmware-support/Cargo.toml delete mode 100644 firmware-support/bittide-sys/Cargo.toml delete mode 100644 firmware-support/bittide-sys/src/axi.rs delete mode 100644 firmware-support/bittide-sys/src/axi/self_test.rs delete mode 100644 firmware-support/bittide-sys/src/callisto.rs delete mode 100644 firmware-support/bittide-sys/src/clock_control.rs delete mode 100644 firmware-support/bittide-sys/src/dna_port_e2.rs delete mode 100644 firmware-support/bittide-sys/src/gather_unit.rs delete mode 100644 firmware-support/bittide-sys/src/lib.rs delete mode 100644 firmware-support/bittide-sys/src/mac.rs delete mode 100644 firmware-support/bittide-sys/src/program_stream.rs delete mode 100644 firmware-support/bittide-sys/src/scatter_unit.rs delete mode 100644 firmware-support/bittide-sys/src/smoltcp.rs delete mode 100644 firmware-support/bittide-sys/src/smoltcp/axi.rs delete mode 100644 firmware-support/bittide-sys/src/time.rs delete mode 100644 firmware-support/bittide-sys/src/time/self_test.rs delete mode 100644 firmware-support/bittide-sys/src/uart.rs delete mode 100644 firmware-support/bittide-sys/src/uart/log.rs delete mode 100644 firmware-support/bittide-sys/src/utils.rs delete mode 100644 firmware-support/bittide-sys/tests/elf_common.rs delete mode 100644 firmware-support/bittide-sys/tests/program_stream.rs delete mode 100644 fourmolu.yaml delete mode 100644 hie.yaml delete mode 100644 hitl-setup/README.md delete mode 100644 hitl-setup/dhcpd.conf delete mode 100644 hitl-setup/fpganet delete mode 100644 hitl-setup/fpganet.service delete mode 100755 hitl-setup/setup.sh delete mode 100644 host-tools/.cargo/config.toml delete mode 100644 host-tools/Cargo.lock delete mode 100644 host-tools/Cargo.lock.license delete mode 100644 host-tools/Cargo.toml delete mode 100644 host-tools/callisto-lib/Cargo.toml delete mode 100644 host-tools/callisto-lib/src/lib.rs delete mode 100644 host-tools/dummy/Cargo.toml delete mode 100644 host-tools/dummy/src/main.rs delete mode 100755 nix/bin/cache delete mode 100755 nix/bin/format delete mode 100755 nix/bin/install-openocd-vexriscv-udev delete mode 100755 nix/bin/shake delete mode 100644 nix/mc.nix rename {clash-vexriscv/nix => nix}/sources.json.license (100%) rename {clash-vexriscv/nix => nix}/sources.nix.license (100%) delete mode 100644 nix/verilog-ethernet.nix delete mode 100755 run_test.sh delete mode 100755 scripts/diff-clash-vexriscv-subtree.py delete mode 100755 scripts/update-clash-vexriscv-subtree.sh delete mode 100644 vivado-hs/LICENSE delete mode 100644 vivado-hs/src/Vivado.hs delete mode 100644 vivado-hs/src/Vivado/Internal.hs delete mode 100644 vivado-hs/src/Vivado/Tcl.hs delete mode 100644 vivado-hs/tests/Tests/Vivado.hs delete mode 100644 vivado-hs/tests/unittests.hs delete mode 100644 vivado-hs/vivado-hs.cabal delete mode 100644 vivado-hs/vivado-hs.cabal.license diff --git a/.cargo/config.toml b/.cargo/config.toml index acbd98663..be5e2d99f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,4 +3,5 @@ # SPDX-License-Identifier: CC0-1.0 [build] -target = "x86_64-unknown-linux-gnu" +target = "riscv32imc-unknown-none-elf" +rustflags = ["-C", "target-feature=+f"] diff --git a/.envrc b/.envrc index ae3ce22b3..c551fc7b9 100644 --- a/.envrc +++ b/.envrc @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Google LLC +# SPDX-FileCopyrightText: 2022 Google LLC # -# SPDX-License-Identifier: CC0-1.0 +# SPDX-License-Identifier: Apache-2.0 use_nix -j8 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs deleted file mode 100644 index f9a83419f..000000000 --- a/.git-blame-ignore-revs +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 -a4e4b4b8833acdc0d1f9c488d01e827a04598d98 -54ca5d2cd0088f7d683ff8ab4be5d627d47a32f6 diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 6a0a44f0f..000000000 --- a/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -# exclude riscv-formal from language stats -/riscv-formal/** linguist-vendored diff --git a/.github/cabal.project b/.github/cabal.project index 3f4973278..1d9dfa214 100644 --- a/.github/cabal.project +++ b/.github/cabal.project @@ -1,24 +1,8 @@ -- SPDX-FileCopyrightText: 2022 Google LLC -- -- SPDX-License-Identifier: CC0-1.0 -package bittide +package clash-vexriscv ghc-options: -Werror -package bittide-extra +package clash-vexriscv-sim ghc-options: -Werror - -package bittide-instances - ghc-options: -Werror - -package bittide-shake - ghc-options: -Werror - -package bittide-experiments - ghc-options: -Werror - -package bittide-tools - ghc-options: -Werror - --- TODO: Fix upstream --- package clash-vexriscv --- ghc-options: -Werror diff --git a/.github/docker/Dockerfile b/.github/docker/Dockerfile index 3d4f7be1a..754c775b1 100644 --- a/.github/docker/Dockerfile +++ b/.github/docker/Dockerfile @@ -1,58 +1,82 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -FROM ubuntu:22.04 - -RUN \ - apt-get update && \ - apt-get install -y curl xz-utils sudo git && \ - rm -rf /var/lib/apt/lists/* - -RUN \ - groupadd -r nixbld && \ - for n in $(seq 1 10); do \ - useradd \ - -c "Nix build user $n"\ - -d /var/empty \ - -g nixbld \ - -G nixbld \ - -M -N -r \ - -s "$(command -v nologin)" "nixbld$n";\ - done && \ - curl -L https://nixos.org/nix/install --output install_nix.sh && \ - sh install_nix.sh - -ENV \ - NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \ - MANPATH="/root/.nix-profile/share/man:$MANPATH" \ - PATH="/root/.nix-profile/bin:$PATH" \ - LC_ALL=en_US.UTF-8 - -WORKDIR /build -COPY rust-toolchain.toml rust-toolchain.toml -COPY default.nix default.nix -COPY shell.nix shell.nix -COPY nix /build/nix - -RUN \ - nix-shell -j32 --run "echo OK" - -# Vivado hacks / dependencies: -RUN \ - apt-get update && \ - apt-get install language-pack-en-base -y && \ - locale-gen en_US.UTF-8 && \ - update-locale && \ - apt-get install libtinfo5 libncurses5 libx11-6 -y - -# Convenience wrapper for CI -RUN \ - echo '#!/usr/bin/env bash' >> /usr/bin/git-nix-shell && \ - echo 'nix-shell "${GITHUB_WORKSPACE}/shell.nix" "${@:2}" --run "bash -euf -o pipefail $1"' >> /usr/bin/git-nix-shell && \ - chmod +x /usr/bin/git-nix-shell - -# For HITL purposes we need to run as a normal user. 1001 is the user with plugdev -# access on the runner. -RUN \ - addgroup --gid 1001 bittide && \ - adduser --uid 1001 bittide --ingroup bittide +# syntax=docker/dockerfile:1.2 + +# SPDX-FileCopyrightText: 2024 Google LLC + +# SPDX-License-Identifier: CC0-1.0 + +ARG UBUNTU_VERSION +FROM ubuntu:$UBUNTU_VERSION AS builder + +LABEL vendor="QBayLogic B.V." maintainer="devops@qbaylogic.com" +ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 LC_ALL=C.UTF-8 PREFIX=/opt + +ARG DEPS_COMMON="build-essential ca-certificates curl git locales ca-certificates" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends $DEPS_COMMON \ + && locale-gen en_US.UTF-8 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +FROM builder AS build-openocd-vexriscv + +ARG DEPS_OPENOCD_VEXRISCV="autoconf automake libtool pkg-config libusb-1.0-0-dev libftdi-dev libhidapi-dev libusb-dev libyaml-dev" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends $DEPS_OPENOCD_VEXRISCV \ + && git clone --recursive https://github.com/SpinalHDL/openocd_riscv.git \ + && cd openocd_riscv \ + && ./bootstrap \ + && ./configure --enable-ftdi --enable-dummy --prefix=/opt \ + && make -j$(nproc) \ + && make install + +FROM builder AS build-verilator + +ARG DEPS_VERILATOR="perl python3 make autoconf g++ flex bison ccache libgoogle-perftools-dev numactl perl-doc libfl2 libfl-dev zlib1g zlib1g-dev help2man" +RUN apt-get update \ + && apt-get install -y --no-install-recommends $DEPS_VERILATOR + +ARG verilator_version="v5.020" +RUN git clone https://github.com/verilator/verilator verilator \ + && cd verilator \ + && git checkout $verilator_version \ + && autoconf \ + && ./configure --prefix $PREFIX \ + && make PREFIX=$PREFIX -j$(nproc) \ + && make PREFIX=$PREFIX install \ + && cd ../.. \ + && rm -Rf verilator + +FROM builder AS build-ghc + +ARG ghcup_version="0.1.22.0" + +# Must be explicitly set +ARG ghc_version +ARG cabal_version + +RUN curl "https://downloads.haskell.org/~ghcup/$ghcup_version/x86_64-linux-ghcup-$ghcup_version" --output /usr/bin/ghcup \ + && chmod +x /usr/bin/ghcup \ + && ghcup install ghc $ghc_version --set \ + && ghcup install cabal $cabal_version --set + +FROM builder AS run + +LABEL vendor="QBayLogic B.V." maintainer="devops@qbaylogic.com" +ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 LC_ALL=C.UTF-8 PATH="$PATH:/opt/bin:/root/.ghcup/bin" + +ARG DEPS_RUNTIME="gnupg pkg-config openjdk-8-jdk gdb-multiarch picocom libtinfo5 libtinfo-dev build-essential curl libc6-dev libgmp10-dev python3 ccache libftdi1 libhidapi-hidraw0 libusb-1.0-0 libyaml-0-2" +RUN apt-get update \ + && apt-get install -y --no-install-recommends $DEPS_RUNTIME \ + && echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | tee /etc/apt/sources.list.d/sbt.list \ + && echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | tee /etc/apt/sources.list.d/sbt_old.list \ + && curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | apt-key add \ + && apt-get update \ + && apt-get install -y --no-install-recommends sbt \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=build-verilator /opt /opt +COPY --from=build-openocd-vexriscv /opt /opt +COPY --from=build-ghc /root/.ghcup /root/.ghcup diff --git a/clash-vexriscv/.github/docker/build-and-publish-docker-image.sh b/.github/docker/build-and-publish-docker-image.sh similarity index 100% rename from clash-vexriscv/.github/docker/build-and-publish-docker-image.sh rename to .github/docker/build-and-publish-docker-image.sh diff --git a/.github/docker/build-and-publish.sh b/.github/docker/build-and-publish.sh deleted file mode 100755 index ce7e5994e..000000000 --- a/.github/docker/build-and-publish.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set -euo pipefail -IFS=$'\n\t' - -HERE=$(dirname $0) -ROOT="$(git rev-parse --show-toplevel)" -REPO="ghcr.io/clash-lang" -NAME="nixos-bittide-hardware" -TODAY="$(date +%F)" - -cd "${HERE}" - -cp "${ROOT}/default.nix" . -cp "${ROOT}/shell.nix" . -cp "${ROOT}/rust-toolchain.toml" . -cp -ap "${ROOT}/nix" . - -docker build -t "${REPO}/${NAME}:$TODAY" -t "${REPO}/${NAME}:latest" . - -read -p "Push to GitHub? (y/N) " push - -if [[ $push =~ ^[Yy]$ ]]; then - docker push "${REPO}/${NAME}:$TODAY" - docker push "${REPO}/${NAME}:latest" -else - echo "Skipping push to container registry" -fi diff --git a/.github/scripts/all_check.py b/.github/scripts/all_check.py deleted file mode 100755 index d842c1a39..000000000 --- a/.github/scripts/all_check.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -""" -Makes sure: - - * All jobs are listed in the 'all' job - * Only existing tests are listed - -""" - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -import sys -import yaml - -CI_PATH = ".github/workflows/ci.yml" -ALL_TEST = "all" - -def main(): - ci_yml_fp = open(CI_PATH, "r") - ci_yml_parsed = yaml.load(ci_yml_fp, Loader=yaml.FullLoader) - - all_jobs = set(ci_yml_parsed['jobs'].keys()) - {ALL_TEST} - all_needs = set(ci_yml_parsed["jobs"][ALL_TEST]["needs"]) - - if all_jobs - all_needs: - sys.exit(f"Not all jobs mentioned in {ALL_TEST}.needs: {all_jobs - all_needs}") - - if all_needs - all_jobs: - sys.exit(f"Non-existing jobs found in {ALL_TEST}.needs: {all_needs - all_jobs}") - - -if __name__ == '__main__': - main() diff --git a/.github/scripts/cabal-gild.sh b/.github/scripts/cabal-gild.sh deleted file mode 100755 index 4a3c4f330..000000000 --- a/.github/scripts/cabal-gild.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set -euf -o pipefail - -git ls-files *.cabal cabal.project \ - | grep --extended-regexp --invert-match '^clash-vexriscv/' \ - | xargs --max-procs=0 -I {} cabal-gild -i "{}" -o "{}" diff --git a/.github/scripts/cache.py b/.github/scripts/cache.py deleted file mode 100755 index 6dede38c6..000000000 --- a/.github/scripts/cache.py +++ /dev/null @@ -1,376 +0,0 @@ -#!/usr/bin/env python3 -""" -Strict caching script, that uploads to a hardcoded S3 server. Users should set -an environment variable 'S3_PASSWORD'. - -Usage: - cache push (cabal|cargo|build|build-post-synth) [--prefix=] [--empty-pattern-ok] [--write-cache-found] [--overwrite-ok] - cache pull (cabal|cargo|build|build-post-synth) [--prefix=] [--missing-ok] [--overwrite-ok] [--write-cache-found] - cache clean - cache -h | --help - cache --version - -Options: - -h --help Show this screen. - --version Show version. - --missing-ok Do not throw an error if a cache is missing - --overwrite-ok Allow file overwrites on pull, or cache overwrites on push - --write-cache-found Writes '0' or '1' to 'cache_found', depending on whether a cache existed - --empty-pattern-ok Allow patterns to not match any files - --prefix= Add prefix to the cache key -""" - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -import datetime -import dateutil.tz -import docopt -import functools -import glob -import hashlib -import itertools -import os -import random -import string -import subprocess -import sys - -from typing import List, Sequence - -PWD = os.getcwd() - -GITHUB_ORG="bittide" -GITHUB_REPO="bittide-hardware" - -CACHE_URL="http://hertme.local:9000" -CACHE_USER="root" -CACHE_BUCKET=f"github.org/{GITHUB_ORG}/{GITHUB_REPO}" - -CLEAR_AFTER_DAYS=7 -CLEAR_AFTER=f"{CLEAR_AFTER_DAYS}d00h00m00s" -TOUCH_AFTER=datetime.timedelta(days=1) - -GLOBAL_CACHE_BUST = 3 - -CARGO_CACHE_BUST = 2 -CARGO_KEY_PREFIX = f"cargo-g{GLOBAL_CACHE_BUST}-l{CARGO_CACHE_BUST}-" -CARGO_KEY_PATTERNS = ("**/Cargo.lock", "**/Cargo.toml", "**/rust-toolchain.toml") -CARGO_CACHE_PATTERNS = ("~/.cargo",) - -CABAL_CACHE_BUST = 2 -CABAL_KEY_PREFIX = f"cabal-g{GLOBAL_CACHE_BUST}-l{CABAL_CACHE_BUST}-" -CABAL_KEY_PATTERNS = ("**/cabal.project", "**/cabal.project.freeze") -CABAL_CACHE_PATTERNS = ("~/.cabal-nix",) - -BUILD_CACHE_BUST = 2 -BUILD_KEY_PREFIX = f"build-products-g{GLOBAL_CACHE_BUST}-l{BUILD_CACHE_BUST}-" -BUILD_CACHE_PATTERNS = ( - f"{PWD}/_build/", - f"{PWD}/clash-vexriscv/clash-vexriscv/build_out_dir/", - f"{PWD}/dist-newstyle/", -) - -BUILD_POST_SYNTH_CACHE_BUST = 1 -BUILD_POST_SYNTH_KEY_PREFIX = f"build-products-post-synth-g{GLOBAL_CACHE_BUST}-l{BUILD_CACHE_BUST}-" -BUILD_POST_SYNTH_CACHE_PATTERNS = ( - f"{PWD}/_build/clash", - f"{PWD}/_build/vivado", - f"{PWD}/_build/.shake.database", - f"{PWD}/_build/.shake.lock", -) - -def log(msg): - """A poor man's logging function""" - print(f"[{datetime.datetime.now().isoformat()}] {msg}", file=sys.stderr) - -@functools.lru_cache() -def get_all_git_files(): - output = subprocess.check_output(["git", "ls-files"]).decode() - paths = (line.strip() for line in output.split("\n") if line.strip()) - return frozenset(paths) - -def sha256sum_file(path : str) -> str: - hash = hashlib.sha256() - with open(path, "rb") as fp: - hash.update(fp.read()) - digest = hash.hexdigest() - log(f"{digest} {path}") - return digest - -def sha256sum_files(file_paths : Sequence[str]) -> str: - hash = hashlib.sha256() - for path in sorted(set(file_paths)): - hash.update(sha256sum_file(path).encode()) - return hash.hexdigest() - - -def get_git_files_from_patterns(patterns): - filesytem_files = itertools.chain.from_iterable( - glob.glob(pattern, recursive=True) for pattern in patterns - ) - - git_files = get_all_git_files() - return [p for p in filesytem_files if p in git_files] - - -def get_key_from_patterns(patterns): - return sha256sum_files(get_git_files_from_patterns(patterns)) - - -def get_cargo_key(): - return CARGO_KEY_PREFIX + get_key_from_patterns(CARGO_KEY_PATTERNS) - # return CARGO_KEY_PREFIX + os.environ["GITHUB_SHA"] - - -def get_cabal_key(): - return CABAL_KEY_PREFIX + get_key_from_patterns(CABAL_KEY_PATTERNS) - # return CABAL_KEY_PREFIX + os.environ["GITHUB_SHA"] - - -def get_build_key(): - return BUILD_KEY_PREFIX + os.environ["GITHUB_SHA"] - - -def get_build_post_synth_key(): - return BUILD_POST_SYNTH_KEY_PREFIX + os.environ["GITHUB_SHA"] - - -def get_random_string(): - return ''.join(random.choice(string.ascii_letters) for i in range(10)) - -def parse_ls_line(line): - """ - Parse a string like: - - 2023-10-25 13:45:51 CEST 9.5GiB STANDARD foo.tar.zst-JZVnMBIapA - - into its date and filename component. - """ - line = line.replace("[", "").replace("]", "").strip() - line_split_no_padding = filter(None, line.split(" ")) - year_month_day, hour_minute_seconds, timezone_name, _size, _status, *filename = line_split_no_padding - hour, minute, seconds = map(int, hour_minute_seconds.split(":")) - year, month, day = map(int, year_month_day.split("-")) - filename = " ".join(filename) - timezone = dateutil.tz.gettz(timezone_name) - timestamp = datetime.datetime(year, month, day, hour, minute, seconds, tzinfo=timezone) - return timestamp, filename - -class Mc: - def __init__(self, key, *, prefix=None): - self.key = key - if prefix is not None: - self.key = f"{prefix}-{self.key}" - self.password = os.environ["S3_PASSWORD"] - self._run(["mc", "alias", "set", "cache", CACHE_URL, CACHE_USER, self.password]) - self._run(["mc", "mb", "--ignore-existing", self._get_bucket()]) - - def _get_pretty_cmd(self, cmd): - return ' '.join(cmd).replace(self.password, "") - - def _pipe(self, cmd0, cmd1): - """ - Run two commands, with the first command's stdout piped into the second's - command stdin. Throws a `CalledProcessError` if either command exits with - a non-zero exit code. - - Prints executed command to stderr (with masked password). - """ - cmds = cmd0 + ["|"] + cmd1 - log(self._get_pretty_cmd(cmds)) - - cmd0_ps = subprocess.Popen(cmd0, stdout=subprocess.PIPE) - try: - output = subprocess.check_call(cmd1, stdin=cmd0_ps.stdout) - finally: - retcode = cmd0_ps.wait() - - if retcode: - raise subprocess.CalledProcessError(retcode, cmd0_ps.args) - - return output - - def _run(self, cmd : List[str]): - """ - Run a command with `check_output`. - - Prints executed command to stderr (with masked password). - """ - log(self._get_pretty_cmd(cmd)) - return subprocess.check_output(cmd) - - def _get_filename(self): - return f"{self.key}.tar.zst" - - def _get_bucket(self): - return f"cache/{CACHE_BUCKET}" - - def _get_path(self): - return f"{self._get_bucket()}/{self._get_filename()}" - - def ls(self): - """ - List tuples of (last_modified_timestamp, filename). Excludes '/' that is - usually returned by S3. - """ - lines = self._run(["mc", "ls", self._get_bucket()]).decode() - for line in lines.split("\n"): - if line.strip(): - timestamp, filename = parse_ls_line(line) - if filename != "/": - yield (timestamp, filename) - - def clean(self): - """Remove all files in our bucket older than `CLEAR_AFTER`""" - self._run([ - "mc", "rm", "--recursive", "--force", - f"--older-than={CLEAR_AFTER}", - self._get_bucket() - ] - ) - - def push(self, patterns, empty_pattern_ok=False, replace=False) -> bool: - """ - Upload files in `patterns`. Will error if none of the patterns yielded - any files. Will also error if _any_ pattern came up empty, unless - `empty_pattern_ok` is set. - - Will not overwrite existing caches, unless `replace` is set. Returns - 'True' if the cache already existed or if a cache was written. - """ - if not replace and self.stat() is not None: - return False - - files = [] - for pattern in patterns: - pattern = os.path.expanduser(pattern) - found_files = glob.glob(pattern, recursive=True) - if not found_files and not empty_pattern_ok: - raise ValueError(f"Pattern '{pattern}' did not match any files") - files.extend(found_files) - - if not files: - raise ValueError("No patterns matched: unable to create cache") - - self._pipe( - ["tar", "--zstd", "-C", "/", "-cf", "-"] + sorted(files), - ["mc", "pipe", self._get_path()] - ) - - return True - - def touch(self, older_than=TOUCH_AFTER): - """ - Renew a file's last modification date. Used to keep frequently used files - around. Because this is a fairly expensive operation, we only perform it - on files older than `TOUCH_AFTER` by default. - """ - timestamp = self.stat() - - if timestamp is None: - # File does not exist - return - - if datetime.datetime.now().astimezone() - timestamp <= older_than: - # File not old enough - return - - filename = self._get_path() - tmp_filename = f"{filename}-{get_random_string()}" - - self._run(["mc", "cp", filename, tmp_filename]) - self._run(["mc", "mv", tmp_filename, filename]) - - def stat(self): - """Return last modified stat or None if key does not exist""" - lines = self._run(["mc", "ls", self._get_path()]).decode() - filename = self._get_filename() - for line in lines.split("\n"): - if line.strip(): - listed_timestamp, listed_filename = parse_ls_line(line) - if filename == listed_filename: - return listed_timestamp - - def pull(self, missing_ok=False, overwrite_ok=False) -> bool: - """ - Retrieve and extract a file. Returns a bool indicating whether a pull - failed. Note that the function will error if `missing_ok` is _not_ set - and the cache does _not_ exist, instead of simply returning a boolean. - """ - # Renew 'last modified', which we use as an indication which caches get - # used frequently. Doesn't do anything if the file does not exist. - self.touch() - - if missing_ok: - if self.stat() is None: - return False - - # Though it might seem that a file could get deleted in between the call - # to `stat()` and the call to `_pipe()`, this shouldn't happen as we called - # `touch()` at the very start of this function - caches only get cleaned - # after `CLEAR_AFTER`. - self._pipe( - ["mc", "cat", self._get_path()], - ["tar", "--zstd", "-C", "/", "-x"] + ([] if overwrite_ok else ["--keep-old-files"]) - ) - - return True - -def write_cache_result(result : bool): - with open("cache_found", "w") as fp: - fp.write("1" if result else "0") - - -def main(opts): - - if opts["cabal"]: - key = get_cabal_key() - patterns = CABAL_CACHE_PATTERNS - elif opts["cargo"]: - key = get_cargo_key() - patterns = CARGO_CACHE_PATTERNS - elif opts["build"]: - key = get_build_key() - patterns = BUILD_CACHE_PATTERNS - elif opts["build-post-synth"]: - key = get_build_post_synth_key() - patterns = BUILD_POST_SYNTH_CACHE_PATTERNS - elif opts["clean"]: - key = None - patterns = () - else: - raise ValueError("Unrecognized name") - - mc = Mc(key, prefix=opts["--prefix"]) - - if opts["clean"]: - mc.clean() - - elif opts["pull"]: - cache_result = mc.pull(opts["--missing-ok"], opts["--overwrite-ok"]) - if not cache_result: - print(f"Cache not found: {mc._get_filename()}") - else: - print(f"Cache found and extracted: {mc._get_filename()}") - - if opts["--write-cache-found"]: - write_cache_result(cache_result) - - elif opts["push"]: - cache_result = mc.push(patterns, opts["--empty-pattern-ok"], opts["--overwrite-ok"]) - if not cache_result: - print(f"Cache hit occured on the key {mc._get_filename()}, not saving cache") - else: - print(f"Saved cache to {mc._get_filename()}") - - if opts["--write-cache-found"]: - write_cache_result(cache_result) - else: - raise ValueError(f"Unrecognized option in {opts}") - - -if __name__ == '__main__': - opts = docopt.docopt(__doc__) - main(opts) diff --git a/.github/scripts/cache_server_setup.sh b/.github/scripts/cache_server_setup.sh deleted file mode 100755 index d106583c2..000000000 --- a/.github/scripts/cache_server_setup.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set -euf -o pipefail - -MINIO_VERSION="20230823100706.0.0" -MINIO_HASH="b591fce770d2aaed7e9fcb62308efa781fe7c411481c442041df12ca2e157eea" - -MINIO_ROOT="root" -MINIO_PASSWORD=$((tr -dc A-Za-z0-9 minio -echo "MINIO_ROOT_PASSWORD=${MINIO_PASSWORD}" >> minio -echo "MINIO_VOLUMES=/var/cache/minio" >> minio -echo "MINIO_CONSOLE_ADDRESS=":9090"" >> minio -echo "MINIO_SERVER_URL=https://hetzner0.bittide.clash-lang.org" >> minio -echo "MINIO_BROWSER_REDIRECT_URL=https://hetzner0.bittide.clash-lang.org/minio/ui" >> minio -sudo cp minio /etc/default/minio - -# Stop previous minio instance -sudo systemctl stop minio.service || true - -# Download and install minio -sudo apt install curl -y -curl -o minio.deb -L "https://dl.min.io/server/minio/release/linux-amd64/archive/minio_${MINIO_VERSION}_amd64.deb" -echo "${MINIO_HASH} minio.deb" | shasum -a 256 -c -sudo apt install ./minio.deb -rm minio.deb - -# systemd starts minio as 'minio-user', but Debian package does not create this -# user on its own. -sudo adduser --disabled-password --gecos "" minio-user - -# Start service -sudo systemctl start minio.service diff --git a/.github/scripts/check_eol_whitespace.sh b/.github/scripts/check_eol_whitespace.sh deleted file mode 100755 index 7c5d9e0fa..000000000 --- a/.github/scripts/check_eol_whitespace.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set -xou pipefail - -grep -E ' $' -n -r . \ - --include=*.{hs,hs-boot,sh,cabal,.md} \ - --exclude-dir=contranomy/dist-newstyle \ - --exclude-dir=clash-vexriscv - -if [[ $? == 0 ]]; then - echo "EOL whitespace detected. See ^" - exit 1; -fi diff --git a/.github/scripts/check_missing_eof_newline.sh b/.github/scripts/check_missing_eof_newline.sh deleted file mode 100755 index 9b1a0f3c2..000000000 --- a/.github/scripts/check_missing_eof_newline.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set -xou pipefail - -pcregrep \ - --exclude=*.svg \ - --exclude=*.drawio \ - --exclude-dir=clash-vexriscv \ - --exclude-dir=.git \ - --binary-files=without-match -LMr '\n\Z' . - -if [[ $? == 0 ]]; then - echo "Files without a newline end detected. See ^" - exit 1; -fi diff --git a/.github/scripts/fix_spdx_header.py b/.github/scripts/fix_spdx_header.py deleted file mode 100644 index 7944c2afa..000000000 --- a/.github/scripts/fix_spdx_header.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -""" -Make sure the SPDX header is at the top of a given file. This is currently -hardcoded to look for the SPDX header formatted in a Haskell comment. This works -around an issue where Fourmolu thinks an SPDX header is part of a comment on a -language pragma, and wrongly produces formatting issues. -""" - -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys - -SPDX_START = "-- SPDX-FileCopyrightText" -SPDX_END = "-- SPDX-License-Identifier" - -def format(fp_src, fp_dst): - source_lines = [] - spdx_lines = [] - - for line in fp_src: - if line.startswith(SPDX_START): - # Collect SPDX header - spdx_lines.append(line) - for line in fp_src: - spdx_lines.append(line) - if line.startswith(SPDX_END): - break - break - else: - # Collect non-SPDX header lines - source_lines.append(line) - - # Write out SPDX header - for spdx_line in spdx_lines: - fp_dst.write(spdx_line) - - # Write out all non-SPDX header lines - for source_line in source_lines: - fp_dst.write(source_line) - - # Write out rest of the file - for line in fp_src: - fp_dst.write(line) - -def main(paths): - for path in paths: - with open(path) as fp_src: - with open(path + ".fix-spdx-header", "w") as fp_dst: - format(fp_src, fp_dst) - os.rename(path + ".fix-spdx-header", path) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/.github/scripts/force_expensive_checks.sh b/.github/scripts/force_expensive_checks.sh deleted file mode 100755 index 5074abf74..000000000 --- a/.github/scripts/force_expensive_checks.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set -exuo pipefail -IFS=$'\n\t' - -commit_msg="$(git log -1 --pretty=%B)" - -set +e -echo "${commit_msg}" | grep -q '\[force_expensive_checks\]' -exit_code="$?" -set -e - -if [[ "${exit_code}" == "0" ]]; then - echo "true" -elif [[ "${exit_code}" == "1" ]]; then - echo "false" -else - echo "Unexpected exit code: ${exit_code}" >> /dev/stderr - exit 1; -fi diff --git a/.github/scripts/fourmolu.sh b/.github/scripts/fourmolu.sh deleted file mode 100755 index 859a8b563..000000000 --- a/.github/scripts/fourmolu.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set -euf -o pipefail - -cd "$(git rev-parse --show-toplevel)" - -echo "Fourmolu.." -git ls-files *.hs \ - | grep --extended-regexp --invert-match '^clash-vexriscv/' \ - | xargs --max-procs=0 -I {} fourmolu --quiet --mode inplace "{}" - -echo "Fixing SPDX headers.." -git ls-files *.hs \ - | grep --extended-regexp --invert-match '^clash-vexriscv/' \ - | xargs --max-procs=0 -I {} python3 .github/scripts/fix_spdx_header.py "{}" diff --git a/.github/scripts/generate_checks.sh b/.github/scripts/generate_checks.sh deleted file mode 100755 index 026b62430..000000000 --- a/.github/scripts/generate_checks.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set -exuo pipefail -IFS=$'\n\t' -HERE=$(realpath $(dirname "$0")) -ROOT="${HERE}/../.." - -cd "${ROOT}" -rm -rf riscv-formal/cores/contranomy -cp -r riscv-formal-config riscv-formal/cores/contranomy -sed -i 's/const rand/rand const/g' riscv-formal/checks/rvfi_macros.* -cd riscv-formal/cores/contranomy -cp "${ROOT}"/contranomy/verilog/Contranomy.contranomyRVFITE/*.inc . -cp "${ROOT}"/contranomy/verilog/Contranomy.contranomyRVFITE/*.v . -python3 "${ROOT}"/riscv-formal/checks/genchecks.py diff --git a/.github/scripts/get_formal_checks.sh b/.github/scripts/get_formal_checks.sh deleted file mode 100755 index 4c871d69b..000000000 --- a/.github/scripts/get_formal_checks.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set -exuo pipefail -IFS=$'\n\t' -HERE=$(realpath $(dirname "$0")) -ROOT=$(git rev-parse --show-toplevel) - -cd "${HERE}" -./generate_checks.sh - -cd "${ROOT}" - -# This is all.. a bit hacky. The alternative would be to fork riscv-formal and -# modify genchecks.py such that it also yield a JSON. I'm not super thrilled -# about that either. -makefile=riscv-formal/cores/contranomy/checks/makefile -checks=$(grep -oP '^all: \K.*' "$makefile") -python3 -c "import json; print(json.dumps('${checks}'.split()))" > checks.json diff --git a/.github/scripts/register_runners.sh b/.github/scripts/register_runners.sh deleted file mode 100755 index 46865542c..000000000 --- a/.github/scripts/register_runners.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash -# SPDX-FileCopyrightText: 2022,2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -# -# Usage: -# -# register_runners -# -# Example: -# -# register_runners 8 compute jbg2PSMoQLfqvj0ZeBUW -# -# Currently we're labelling runners with either "compute" or "hardware-access". -# Note that a label "self-hosted" gets applied automatically. -# -# With a privileged enough account the token can be found at: -# https://github.com/organizations/[ORG_NAME]/settings/actions/runners/new -# -# If you're installing this on a fresh server, make sure to install docker: -# -# apt install docker.io -# -# And allow the user (e.g., gha-user) you want to run the GHA software with to use it: -# -# adduser gha-user docker -# -# Next up, a hack to get docker to write files as the 'gha-user' user, instead of -# 'root'. The latter interferes with clean jobs. Update the UID from 1000 to the UID of -# the gha-user, and make `/etc/subgid` contain: -# -# gha-user:1000:1 -# gha-user:100000:65536 -# -# Make `/etc/subuid` contain (also update the UID from 1000 to the UID of the gha-user): -# -# gha-user:1000:1 -# gha-user:100000:65536 -# -# Make `/etc/docker/daemon.json` contain: -# -# { -# "userns-remap": "gha-user" -# } -# -# These hacks will probably ruin your normal Docker experience, so only use on -# servers dedicated for CI! -# -# Last but not least, reboot. This should apply all the changes and start the GHA -# runners. If not, good luck debugging. -# -set -euf -o pipefail - -N_RUNNERS="$1" -LABELS="$2" -TOKEN="$3" - -ORG=bittide -REPO=bittide-hardware - -URL="https://github.com/${ORG}/${REPO}" - -RUNNER_VERSION="2.317.0" -RUNNER_HASH="9e883d210df8c6028aff475475a457d380353f9d01877d51cc01a17b2a91161d" - -# Stop previous runners (if any) -sudo systemctl stop "actions.runner.${ORG}-${REPO}.$(hostname)-*.service" - -# Download runner, verify contents -curl -o actions-runner-linux-x64-"${RUNNER_VERSION}".tar.gz -L https://github.com/actions/runner/releases/download/v"${RUNNER_VERSION}"/actions-runner-linux-x64-"${RUNNER_VERSION}".tar.gz -echo "${RUNNER_HASH} actions-runner-linux-x64-"${RUNNER_VERSION}".tar.gz" | shasum -a 256 -c - -# Install and register a bunch of runners -for i in $(seq $N_RUNNERS) -do - runner_name="$(hostname)-$i" - echo "$(tput bold)Registering ${runner_name}$(tput sgr0)" - - rm -rf actions-runner-$i - mkdir -p actions-runner-$i - cd actions-runner-$i - - # Install runner - tar xzf ../actions-runner-linux-x64-"${RUNNER_VERSION}".tar.gz - - # Register with GitHub - ./config.sh \ - --unattended \ - --replace \ - --url "${URL}" \ - --token "${TOKEN}" \ - --labels "${LABELS}" \ - --name "${runner_name}" - - # TODO: - # --labels vivado_standard_2022.1_0420_0327 \ - - # Remove previous systemd jobs - service_name="actions.runner.${ORG}-${REPO}.${runner_name}.service" - service_file="/etc/systemd/system/${service_name}" - sudo rm -f "/etc/systemd/system/multi-user.target.wants/${service_name}" - sudo rm -f "${service_file}" - - # Install as service - sudo ./svc.sh install - - # Remove ill-advised entries - sudo sed -i '/^KillMode=/d' "${service_file}" - sudo sed -i '/^KillSignal=/d' "${service_file}" - - # Make systemd aware of changes - sudo systemctl daemon-reload - sudo systemctl restart "${service_name}" - - cd .. -done diff --git a/.github/scripts/run_riscv_formal_check.sh b/.github/scripts/run_riscv_formal_check.sh deleted file mode 100755 index e93f0b36b..000000000 --- a/.github/scripts/run_riscv_formal_check.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set -exuo pipefail -IFS=$'\n\t' -HERE=$(realpath $(dirname "$0")) -ROOT="${HERE}/../.." - -cd "${HERE}" -./generate_checks.sh - -cd "${ROOT}"/riscv-formal/cores/contranomy -rm -rf checks/"$1" -make -C checks "$1" -[[ -f checks/"$1"/PASS ]] diff --git a/.github/scripts/self_hosted_docker_check.py b/.github/scripts/self_hosted_docker_check.py deleted file mode 100755 index 7f33d1ba0..000000000 --- a/.github/scripts/self_hosted_docker_check.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -""" -Make sure all self-hosted jobs run in a docker container and that each docker -container has a maximum amount of memory it can use. -""" - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -import yaml - -CI_PATH = ".github/workflows/ci.yml" -SELF_HOSTED_LABEL = "self-hosted" - - -def main(): - ci_yml_fp = open(CI_PATH, "r") - ci_yml_parsed = yaml.safe_load(ci_yml_fp) - - jobs = ci_yml_parsed["jobs"] - for job_name, job in jobs.items(): - runs_on = job["runs-on"] - if SELF_HOSTED_LABEL in runs_on: - try: - job["container"]["image"] - except KeyError: - raise ValueError(f"self-hosted jobs should run in docker containers, '{job_name}' does not") - try: - options = job["container"]["options"] - if "--memory=" not in options: - raise ValueError(f"Containers should have limited memory, '{job_name}' does not") - except KeyError: - raise ValueError(f"Containers should have limited memory, '{job_name}' does not") - - -if __name__ == "__main__": - main() diff --git a/.github/scripts/with_vivado.sh b/.github/scripts/with_vivado.sh deleted file mode 100755 index dd168fb70..000000000 --- a/.github/scripts/with_vivado.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set -euo pipefail -IFS=$'\n\t' - -# Get Vivado environment in scope -source /opt/tools/Xilinx/VivadoEnterprise/Vivado/2022.1/settings64.sh - -# Work around https://support.xilinx.com/s/question/0D52E000079NURRSA4/synthesis-failed-abnormal-termination-tcmalloc-large-allocation?language=en_US -export LD_PRELOAD=/lib/x86_64-linux-gnu/libudev.so.1 - -# Run command given as argument -"$@" diff --git a/.github/synthesis/all.json b/.github/synthesis/all.json deleted file mode 100644 index a4c570a5a..000000000 --- a/.github/synthesis/all.json +++ /dev/null @@ -1,28 +0,0 @@ -[ - {"top": "callisto3", "stage": "pnr"}, - {"top": "counterReducedPins", "stage": "pnr"}, - {"top": "elasticBuffer5", "stage": "pnr"}, - {"top": "gatherUnit1K", "stage": "hdl"}, - {"top": "gatherUnit1KReducedPins", "stage": "pnr"}, - {"top": "safeDffSynchronizer", "stage": "pnr"}, - {"top": "scatterUnit1K", "stage": "hdl"}, - {"top": "scatterUnit1KReducedPins", "stage": "pnr"}, - {"top": "si5391Spi", "stage": "pnr"}, - {"top": "stabilityChecker_3_1M", "stage": "pnr"}, - {"top": "switchCalendar1k", "stage": "hdl"}, - {"top": "switchCalendar1kReducedPins", "stage": "pnr"}, - - {"top": "boardTestExtended", "stage": "test"}, - {"top": "boardTestSimple", "stage": "test"}, - {"top": "fincFdecTests", "stage": "test"}, - {"top": "fullMeshHwCcTest", "stage": "test"}, - {"top": "fullMeshHwCcWithRiscvTest", "stage": "test"}, - {"top": "fullMeshSwCcTest", "stage": "test"}, - {"top": "hwCcTopologyTest", "stage": "test"}, - {"top": "linkConfigurationTest", "stage": "test"}, - {"top": "syncInSyncOut", "stage": "test"}, - {"top": "temperatureMonitor", "stage": "test"}, - {"top": "transceiversUpTest", "stage": "test"}, - {"top": "vexRiscvTest", "stage": "test"}, - {"top": "vexRiscvTcpTest", "stage": "test"} -] diff --git a/.github/synthesis/staging.json b/.github/synthesis/staging.json deleted file mode 100644 index 68a2ca0f2..000000000 --- a/.github/synthesis/staging.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - {"top": "fullMeshHwCcTest", "stage": "test"}, - {"top": "fullMeshSwCcTest", "stage": "test"}, - {"top": "linkConfigurationTest", "stage": "test"}, - {"top": "safeDffSynchronizer", "stage": "hdl"}, - {"top": "transceiversUpTest", "stage": "test"}, - {"top": "vexRiscvTest", "stage": "test"}, - {"top": "vexRiscvTcpTest", "stage": "test"} -] diff --git a/.github/systemd/README.md b/.github/systemd/README.md deleted file mode 100644 index 7142f108c..000000000 --- a/.github/systemd/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# CI hardware test server - -Since the hardware-in-the-loop tests on CI depend on the Vivado Hardware -Server, the GitHub runner should not start before it. We therefore add a -systemd service which starts the Vivado Hardware Server. That service must run -before starting the GitHub runner service. - -Since the "unit" file for the GitHub runner is generated by another script, we -add an additional configuration for the GitHub runner service with the -`systemd drop-in` mechanism. - -## Setup -Follow these steps to setup the two services. Note that our server is called -`hoeve`, and you should change the name of your GitHub runner. - -1. Generate a GitHub runner service using `scripts/register_runners.sh` -2. Create a drop-in directory for the GitHub runner service. The directory -should be named as the service with `.d` appended to it: -``` -sudo mkdir -p /etc/systemd/system/actions.runner.bittide-bittide-hardware.hoeve-1.service.d -``` -3. Copy the drop-in file inside the directory: -``` -sudo cp vivado-hw-server.service.conf /etc/systemd/system/actions.runner.bittide-bittide-hardware.hoeve-1.service.d -``` -4. Reload the systemd daemon: -``` -sudo systemctl daemon-reload -``` diff --git a/.github/systemd/vivado-hw-server.service b/.github/systemd/vivado-hw-server.service deleted file mode 100644 index 0f26f6f65..000000000 --- a/.github/systemd/vivado-hw-server.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Vivado Hardware Server -After=network.target -Before=actions.runner.bittide-bittide-hardware.hoeve-1.service - -[Service] -Type=simple -ExecStart=/bin/bash -c "source /opt/tools/Xilinx/VivadoEnterprise/Vivado/2022.1/settings64.sh && hw_server" -Restart=always -User=graaf -# XXX: We actually want to run the hw_server as another user. However, it seems -# like that user currently does not have hardware access. We did add the -# user to the dialout group, but this did not solve the problem. For the -# time being, we run the hw_server as user graaf. - -[Install] -WantedBy=multi-user.target diff --git a/.github/systemd/vivado-hw-server.service.conf b/.github/systemd/vivado-hw-server.service.conf deleted file mode 100644 index 13eace9b1..000000000 --- a/.github/systemd/vivado-hw-server.service.conf +++ /dev/null @@ -1,2 +0,0 @@ -[Unit] -Requires=vivado-hw-server.service diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e036a54cb..779e7aede 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,702 +4,178 @@ name: CI -# XXX: What we'd really like is run on every pull request / pull request change, -# which allows GitHub to cancel runs after (force) pushes. Triggering on -# these seems to be broken since January 19th 2023 however. Note that not -# everyone likes auto-cancellation so we should probably recognize [no-skip] -# commits. -on: - schedule: - # Works out to be approx midnight (CET/CEST) accounting for UTC offset and - # GitHub scheduling. See the comment at 'branches-ignore' for more - # information on our development flow. This job will run in context of the - # branch 'staging'. - - cron: '0 19 * * *' - - push: - branches-ignore: - # Developers request to merge their changes into 'staging'. In order to - # do so, relatively cheap tests should pass CI. Every night, CI runs our - # full test suite on 'staging'. If these pass, a pull request may be - # created to get 'main' up to date with 'staging'. Note that this should - # be automated in the future, but this will require some engineering. - - 'staging' - - 'main' - -env: - HW_SERVER_URL: hoeve.local:3121 - S3_PASSWORD: ${{ secrets.S3_PASSWORD }} - SYNTHESIS_BOARD: xilinx.com:kcu105:part0:1.7 - - # Suppress warnings related to locals - LC_ALL: C - LANG: C - -concurrency: - group: ${{ github.head_ref || github.run_id }} - cancel-in-progress: true +on: [push] + +# Updating Rust versions: +# +# When updating to a newer (or older) version of Rust for the main build process +# then the version should be updated in the /rust-toolchain.toml file too. +# That file determines which version gets used locally on developer machines. jobs: license-check: - runs-on: [self-hosted, compute] + runs-on: ubuntu-22.04 container: image: ubuntu:22.04 - options: --memory=11g steps: - uses: actions/checkout@v4 - name: REUSE Compliance Check - uses: fsfe/reuse-action@v1 - - lint: - name: Basic linting - runs-on: [self-hosted, compute] - - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: EOL whitespace - run: | - .github/scripts/check_eol_whitespace.sh - - - name: Enforce EOF newline - run: | - .github/scripts/check_missing_eof_newline.sh - - - name: Check that all self-hosted jobs run in a docker container - run: | - .github/scripts/self_hosted_docker_check.py - - - name: Check that the 'all' job depends on all other jobs - run: | - .github/scripts/all_check.py - - - name: Check that all cabal files are formatted correctly - run: | - .github/scripts/cabal-gild.sh - git diff --exit-code + uses: fsfe/reuse-action@v2 - - name: Check that all Haskell files are formatted correctly - run: | - .github/scripts/fourmolu.sh - git diff --exit-code - - - name: Check that we don't introduce accidental infinite loops in type checkers - run: | - ! grep --include=*.hs -E -r '\-fconstraint-solver-iterations *= *0' - - build: - name: Build dependencies - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set CI flags - run: | - cp .github/cabal.project cabal.project.local - - - run: cache clean - - run: cache pull cabal --missing-ok --write-cache-found - - run: cache pull cargo --missing-ok - - - name: Update Cabal index info - run: | - if [[ $(cat cache_found) == 0 ]]; then - cabal update - else - echo "Restored cache, no need to update cabal package list" - fi - - - run: ./cargo.sh fetch - - run: ./cargo.sh build --frozen --release - - run: ./cargo.sh build --frozen - - run: cabal build all - - - run: cache push cabal - - run: cache push cargo - - run: cache push build - - generate-full-clock-control-report: - name: Generate clock control report - runs-on: [self-hosted, compute] - if: ${{ !cancelled() && needs.bittide-instances-hardware-in-the-loop-test-matrix.outputs.should_generate_report == 'true' }} - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - needs: [ - build, - bittide-instances-hardware-in-the-loop, - bittide-instances-hardware-in-the-loop-test-matrix, - ] + rust-checks: + name: Rust checks + runs-on: ubuntu-22.04 container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g + image: ubuntu:22.04 steps: - name: Checkout uses: actions/checkout@v4 - - name: Set CI flags + - name: Install dependencies run: | - cp .github/cabal.project cabal.project.local - - - run: cache pull cabal - - run: cache pull cargo - - run: cache pull build + apt-get update + apt-get install -y curl build-essential - - name: Generate HITL based plots - run: | - ./cargo.sh build --frozen --release - export BITTIDE_ARTIFACT_ACCESS_TOKEN="${{ secrets.GITHUB_TOKEN }}" - export RUNREF="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - cabal run -- bittide-tools:cc-plot ${{ github.run_id }}:hwCcTopologyTest hitl-topology-plots - - - name: Generate clock control reports - run: | - set +f # Enable globbing - mkdir -p reports - pdfunite \ - hitl-topology-plots/*/report.pdf \ - reports/HITLT-Report.pdf - rm hitl-topology-plots/*/report.pdf - mkdir -p plot-sources - mv hitl-topology-plots plot-sources/hwCcTopologyTest - - - name: Upload Reports - uses: actions/upload-artifact@v4 - if: always() + - uses: actions-rs/toolchain@v1 with: - name: Clock Control Reports - path: reports - retention-days: 14 + toolchain: 1.67 # See Note [Updating Rust versions] + profile: minimal + target: riscv32imc-unknown-none-elf + components: clippy, rustfmt - - name: Upload HITLT Plot Sources - uses: actions/upload-artifact@v4 - if: always() + - name: Rust formatting + uses: actions-rs/cargo@v1 with: - name: CC-HITLT Plot Sources - path: plot-sources - retention-days: 14 - - bittide-experiments-tests: - name: bittide-experiments unittests - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - needs: [build] - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set CI flags - run: | - cp .github/cabal.project cabal.project.local - - - run: cache pull cabal - - run: cache pull build - - - name: Run unittests - run: | - cabal run bittide-experiments:unittests - - - name: Run doctests - run: | - cabal run -- bittide-experiments:doctests + command: fmt + args: --all -- --check - bittide-tests: - name: Bittide tests - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - needs: [build] + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --target riscv32imc-unknown-none-elf --all-features + rust-build-programs: + name: Build Programs + runs-on: ubuntu-22.04 container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g + image: ubuntu:22.04 steps: - name: Checkout uses: actions/checkout@v4 - - name: Set CI flags - run: | - cp .github/cabal.project cabal.project.local - - - run: cache pull cabal - - run: cache pull build - - - name: Unittests - run: | - # While Hedgehog can fill many cores with "work", it doesn't actually - # speed up the tests that much (<5% runtime benefits). - cabal run -- bittide:unittests -j 2 +RTS -N4 - - - name: Bittide Doctests - run: | - cabal run -- bittide:doctests - - - name: Bittide.Extra Doctests + - name: Install dependencies run: | - cabal run -- bittide-extra:doctests + apt-get update + apt-get install -y curl build-essential + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.67.1 # See Note [Updating Rust versions] + profile: minimal + target: riscv32imc-unknown-none-elf + components: clippy, rustfmt - - name: Bittide.Extra Unittests - run: | - # While Hedgehog can fill many cores with "work", it doesn't actually - # speed up the tests that much (<5% runtime benefits). - cabal run -- bittide-extra:unittests -j 2 +RTS -N4 - - rust-lints: - name: Rust Lints - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - needs: [build] + - name: Caching + uses: Swatinem/rust-cache@v2 - steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Build release binaries + uses: actions-rs/cargo@v1 + with: + command: build + args: --release - - run: cache pull cargo + - name: Build debug binaries + uses: actions-rs/cargo@v1 + with: + command: build - - name: Rust formatting + - name: Archive Integration Test Binaries run: | - ./cargo.sh fmt --all -- --check + cd clash-vexriscv-sim; sh bundle_test_binaries.sh - - name: Clippy check - run: | - ./cargo.sh clippy --all-features -- -Dwarnings - - firmware-support-tests: - name: Firmware Support Unit Tests - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - needs: [build] + - name: Upload Integration Test Binaries + uses: actions/upload-artifact@v3 + with: + name: vexriscv-test-binaries + path: clash-vexriscv-sim/vexriscv-test-binaries.tar - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Set CI flags - run: | - cp .github/cabal.project cabal.project.local + vex-riscv: + name: VexRiscv integration + runs-on: ubuntu-22.04 + needs: [rust-build-programs] - - run: cache pull cabal - - run: cache pull cargo - - run: cache pull build + strategy: + fail-fast: false + matrix: + ghc: + - "9.0.2" + - "9.2.8" + - "9.4.8" + - "9.6.6" - - name: Running Tests - run: | - cargo test --all - working-directory: firmware-support/ - - host-tools-tests: - name: Host Tools Unit Tests - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - needs: [build] + image: ghcr.io/clash-lang/clash-vexriscv-ci:${{ matrix.ghc }}-20240823 steps: - name: Checkout uses: actions/checkout@v4 - - name: Set CI flags + - name: Update Cabal index info run: | cp .github/cabal.project cabal.project.local + cabal update + cabal freeze + - name: Cache + uses: actions/cache@v3 + with: + path: | + ~/.local/state/cabal/store/ + key: packages-cachebust-3-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze', 'cabal.project') }} + restore-keys: packages-cachebust-3-${{ matrix.ghc }} - - run: cache pull cargo - - - name: Running Tests + - name: Stash existing VexRiscv.v run: | - cargo test --all - working-directory: host-tools/ - - firmware-limit-checks: - name: Firmware Limit Checks - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - needs: [build] - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - run: cache pull cargo - - run: cache pull build - - - name: Install `elf-limits` - run: cargo install --locked --git https://github.com/cuddlefishie/elf-limits - - - name: Checking firmware binary limits + cp clash-vexriscv/example-cpu/VexRiscv.v clash-vexriscv/example-cpu/VexRiscv.v.comitted + - name: Build clash-vexriscv run: | - ~/.cargo/bin/elf-limits \ - --instruction-mem-limit 64K \ - --data-mem-limit 64K \ - clock-control \ - hello \ - processing-element-test - working-directory: _build/cargo/firmware-binaries/riscv32imc-unknown-none-elf/release/ - - bittide-instances-doctests: - name: bittide-instances doctests - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - needs: [build] - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set CI flags + export PKG_CONFIG_PATH=/opt/share/pkgconfig/:${PKG_CONFIG_PATH} + cabal build clash-vexriscv + - name: Check whether committed VexRiscv.v corresponds to generated one run: | - cp .github/cabal.project cabal.project.local - - - run: cache pull cabal - - run: cache pull build - - - name: Doctests - run : | - cabal run -- bittide-instances:doctests - - bittide-instances-unittests: - name: bittide-instances unittests - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_SHA" --keep "S3_PASSWORD" - needs: [build] - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set CI flags + diff -u clash-vexriscv/example-cpu/VexRiscv.v clash-vexriscv/example-cpu/VexRiscv.v.comitted + - name: Build clash-vexriscv-sim run: | - cp .github/cabal.project cabal.project.local + cabal build clash-vexriscv-sim - - run: cache pull cabal - - run: cache pull build - - - name: Unit tests - run : | - cabal run -- bittide-instances:unittests - - bittide-instances-hdl-matrix: - name: bittide-instances synthesis matrix generation - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_OUTPUT" - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Download VexRiscv Integration Tests + uses: actions/download-artifact@v3 + with: + name: vexriscv-test-binaries - - name: Generate matrix + - name: Work around dubious owner error run: | - if [[ "${{ github.event_name }}" == "schedule" || $(.github/scripts/force_expensive_checks.sh) == "true" ]]; then - cp .github/synthesis/all.json checks.json - elif [[ -f .github/synthesis/debug.json ]]; then - cp .github/synthesis/debug.json checks.json - else - cp .github/synthesis/staging.json checks.json - fi - - - name: Set test matrix - id: set-matrix - run: | - echo "check_matrix=$(cat checks.json | tr '\n' ' ')" | tee -a "$GITHUB_OUTPUT" - - outputs: - check_matrix: ${{ steps.set-matrix.outputs.check_matrix }} - - bittide-instances-hardware-in-the-loop-test-matrix: - name: bittide-instances hardware-in-the-loop test matrix generation - runs-on: [self-hosted, compute] - defaults: - run: - shell: git-nix-shell {0} --option connect-timeout 360 --pure --keep "GITHUB_OUTPUT" + git config --global --add safe.directory "$(pwd)" - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - options: --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Generate matrix - run: | - if [[ "${{ github.event_name }}" == "schedule" || $(.github/scripts/force_expensive_checks.sh) == "true" ]]; then - cp .github/synthesis/all.json checks.json - elif [[ -f .github/synthesis/debug.json ]]; then - cp .github/synthesis/debug.json checks.json - else - cp .github/synthesis/staging.json checks.json - fi - - jq '[.[] | select(.stage=="test")]' checks.json > checks.json.filtered - cp checks.json.filtered checks.json - - - name: Set test matrix - id: set-matrix + - name: Extract VexRiscv Integration Tests run: | + tar -x -f vexriscv-test-binaries.tar - echo "check_matrix=$(cat checks.json | tr '\n' ' ')" | tee -a "$GITHUB_OUTPUT" - - should_generate_report=$(jq 'map(select(.top == "hwCcTopologyTest")) | length > 0' checks.json) - echo "should_generate_report=${should_generate_report}" | tee -a "$GITHUB_OUTPUT" - - outputs: - check_matrix: ${{ steps.set-matrix.outputs.check_matrix }} - should_generate_report: ${{ steps.set-matrix.outputs.should_generate_report }} - - bittide-instances-hdl: - name: synth - runs-on: [self-hosted, compute] - defaults: - run: - # We leave out '--pure', as 'with_vivado.sh' relies on basic Ubuntu - shell: git-nix-shell {0} --option connect-timeout 360 - needs: [build, bittide-instances-hdl-matrix] - - strategy: - matrix: - target: ${{ fromJson(needs.bittide-instances-hdl-matrix.outputs.check_matrix) }} - fail-fast: false - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - volumes: - - /opt/tools:/opt/tools - options: --init --mac-address="6c:5a:b0:6c:13:0b" --memory=11g - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set CI flags + - name: OpenOCD bin symlink run: | - # We use the same flags as the 'build' step to reuse caches. This - # "simulation" config only sets altopts on the Risc core, which we - # don't synthesize yet. - # - # TODO: Either switch to VexRisc or update cache strategy. - # - cp .github/cabal.project cabal.project.local - - - run: cache pull cabal - - run: cache pull cargo - - run: cache pull build - - - name: HDL generation and synthesis - run : | - target="${{ matrix.target.stage }}" - - if [[ "${target}" == "program" ]]; then - echo "Illegal Shake target on CI: program" - exit 1 - fi - - # Only the local runner connected to the bittide demonstrator (hoeve) - # should perform the jobs which need hardware access. And that runner - # should do only that, so we let other 'compute' runners do the - # bitstream generation. - if [[ "${target}" == "test" ]]; then - target="bitstream" - fi - - .github/scripts/with_vivado.sh \ - shake ${{ matrix.target.top }}:"${target}" - - # Workaround for https://github.com/bittide/bittide-hardware/issues/523 - mkdir -p _build/vivado - touch _build/vivado/workaround-issue-523 + ln -s /opt/bin/openocd /opt/bin/openocd-vexriscv - - name: cache push build-post-synth - run: cache push build-post-synth --prefix="${{ matrix.target.top }}" - - - name: Archive build artifacts - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} - with: - name: _build-${{ matrix.target.top }} - # We don't pack `ip`, as it is generally useless for debugging while - # also taking up a lot of space. - path: | - _build/clash - _build/vivado - _build/.shake.database - _build/.shake.lock - !_build/vivado/*/ip - retention-days: 14 - - bittide-instances-hardware-in-the-loop: - name: HITL - runs-on: [self-hosted, hardware-access] - defaults: - run: - # We leave out '--pure', as 'with_vivado.sh' relies on basic Ubuntu - shell: git-nix-shell {0} --option connect-timeout 360 - needs: [bittide-instances-hdl, bittide-instances-hardware-in-the-loop-test-matrix] - - strategy: - matrix: - target: ${{ fromJson(needs.bittide-instances-hardware-in-the-loop-test-matrix.outputs.check_matrix) }} - fail-fast: false - - container: - image: ghcr.io/clash-lang/nixos-bittide-hardware:2024-08-07 - volumes: - - /opt/tools:/opt/tools - - /dev:/dev - - # XXX: User '1001' corresponds to 'bittide' on Linux installation that has - # access to the hardware. We need to set this, lest Docker will write - # files as 'root' and the runner software will be unable to run - # cleaning jobs. - options: >- - --memory=11g - --userns host - --privileged - --user 1001:1001 - --group-add plugdev - --group-add dialout - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set CI flags + - name: Run `clash-vexriscv` unittests run: | - cp .github/cabal.project cabal.project.local + cabal run clash-vexriscv:unittests - - run: cache pull cabal - - run: cache pull cargo - - run: cache pull build - - name: cache pull build-post-synth - run: cache pull build-post-synth --prefix="${{ matrix.target.top }}" - - - name: Run tests on hardware + - name: Run `clash-vexriscv-sim` unittests run: | - .github/scripts/with_vivado.sh \ - shake ${{ matrix.target.top }}:test - - - name: Archive ILA data - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v4 - with: - name: _build-${{ matrix.target.top }}-debug - path: | - _build/vivado/*/ila-data - _build/hitl/* - retention-days: 14 - - all: - name: All jobs finished - if: ${{ !cancelled() }} - needs: [ - bittide-instances-doctests, - bittide-instances-hardware-in-the-loop-test-matrix, - bittide-instances-hardware-in-the-loop, - bittide-instances-hdl-matrix, - bittide-instances-hdl, - bittide-instances-unittests, - bittide-tests, - build, - bittide-experiments-tests, - firmware-limit-checks, - firmware-support-tests, - generate-full-clock-control-report, - host-tools-tests, - license-check, - lint, - rust-lints, - ] - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v4 + # Can't run the unit tests with multiple threads because of the common use of port 7894. + cabal run clash-vexriscv-sim:unittests -- -j1 - - name: Check dependencies for failures + - name: Run `clash-vexriscv-sim` HDL test run: | - # Test all dependencies for success/failure - set -x - success="${{ contains(needs.*.result, 'success') }}" - fail="${{ contains(needs.*.result, 'failure') }}" - set +x - - # Test whether success/fail variables contain sane values - if [[ "${success}" != "true" && "${success}" != "false" ]]; then exit 1; fi - if [[ "${fail}" != "true" && "${fail}" != "false" ]]; then exit 1; fi - - # If this is a debug run, fail even when all dependencies succeeded. - if [[ -f .github/synthesis/debug.json ]]; then - echo "This is a debug run, which may never succeed. Remove '.github/synthesis/debug.json' before trying to merge." - exit 1 - fi - - # We want to fail if one or more dependencies fail. For safety, we introduce - # a second check: if no dependencies succeeded something weird is going on. - if [[ "${fail}" == "true" || "${success}" == "false" ]]; then - echo "One or more dependency failed, or no dependency succeeded." - exit 1 - fi + cabal run clash-vexriscv-sim:hdl-test diff --git a/.gitignore b/.gitignore index ff8983993..fc2b16ff9 100644 --- a/.gitignore +++ b/.gitignore @@ -47,11 +47,6 @@ cabal.config /.idea *.iml -# HDL directories often created during development cycle -vhdl/ -verilog/ -systemverilog/ - # Nix output result* @@ -71,11 +66,8 @@ vivado_* tight_setup_hold_pins.txt .Xil -# Docker builds -.github/docker/rust-toolchain.toml -.github/docker/*.nix -.github/docker/nix +# Verilator debug output +simulation_dump.vcd -# Python temporary files -*.pyc -__pycache__ +# Clash output +verilog diff --git a/.reuse/dep5 b/.reuse/dep5 index 615097678..aacbba431 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -1,59 +1,16 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: bittide +Upstream-Name: clash-vexrisc Upstream-Contact: QBayLogic -Source: https://github.com/google-research/bittide/ +Source: https://github.com/clash-lang/clash-vexrisc + # Sample paragraph, commented out: # # Files: src/* # Copyright: $YEAR $NAME <$CONTACT> # License: ... -Files: cabal.project.freeze -Copyright: 2023-2024 Google LLC -License: CC0-1.0 - -Files: .vscode/* -Copyright: 2022 Google LLC -License: CC0-1.0 -Files: bittide-tools/clockcontrol/sim/imgs/* -Copyright: 2022-2024 Google LLC -License: Apache-2.0 - -Files: bittide-instances/imgs/* -Copyright: 2022-2023 Google LLC -License: Apache-2.0 - -Files: bittide-instances/tests/reports/* -Copyright: 2022 Google LLC -License: Apache-2.0 - -Files: nix/* -Copyright: 2022 Google LLC -License: Apache-2.0 - -Files: .github/synthesis/* -Copyright: 2022 Google LLC -License: Apache-2.0 - -Files: .github/simulation/* -Copyright: 2022-2024 Google LLC -License: Apache-2.0 - -Files: .github/hitl/* -Copyright: 2023 Google LLC -License: Apache-2.0 - -Files: .github/systemd/* -Copyright: 2023 Google LLC -License: Apache-2.0 - -Files: bittide/data/clock_configs/* -Copyright: 2024 Google LLC -License: Apache-2.0 - -# This is already handled in the clash-vexriscv repository, but reuse doesn't -# check the nested dep5 file :( -Files: clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/*.expected +Files: clash-vexriscv-sim/test-programs/src/bin/*.expected Copyright: 2022-2023 Google LLC License: CC0-1.0 + diff --git a/.vscode/schemas/hitl-test-schema.json b/.vscode/schemas/hitl-test-schema.json deleted file mode 100644 index 0fa9c97db..000000000 --- a/.vscode/schemas/hitl-test-schema.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "$defs": { - "probes": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "defaults": { - "type": "object", - "additionalProperties": false, - "properties": { - "probes": { - "$ref": "#/$defs/probes" - } - } - }, - "target": { - "oneOf": [ - { - "type": "object", - "additionalProperties": false, - "properties": { - "index": { - "type": "integer" - } - }, - "required": [ - "index" - ] - } - ] - } - }, - "properties": { - "defaults": { - "$ref": "#/$defs/defaults" - }, - "tests": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "probes": { - "$ref": "#/$defs/probes" - }, - "targets": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "target": { - "$ref": "#/$defs/target" - }, - "probes": { - "$ref": "#/$defs/probes" - } - } - } - } - } - } - ] - } - } - }, - "required": [ - "tests" - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 61232bd43..f5b064694 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Google LLC + +// SPDX-License-Identifier: CC0-1.0 { "files.exclude": { "**/*.dyn_hi": true, @@ -13,37 +16,11 @@ "files.insertFinalNewline": true, "files.trimFinalNewlines": true, "files.trimTrailingWhitespace": true, - "editor.rulers": [ - 80, - 90 - ], "editor.tabSize": 2, "[rust]": { "editor.tabSize": 4 }, - "haskell.toolchain": { - "hls": "2.8.0.0", - "ghc": null, - "cabal": null, - "stack": null - }, "[haskell]": { "editor.formatOnSave": false }, - "yaml.schemas": { - ".vscode/schemas/hitl-test-schema.json": [ - "_build/hitl/*.yml", - "bittide-instances/data/test_configs/*.yml" - ] - }, - "rust-analyzer.linkedProjects": [ - "firmware-support/Cargo.toml", - "firmware-binaries/Cargo.toml", - "host-tools/Cargo.toml" - ], - "rust-analyzer.check.allTargets": false, - "cSpell.words": [ - "bitstream", - "Vivado" - ] } diff --git a/clash-vexriscv/Cargo.lock b/Cargo.lock similarity index 100% rename from clash-vexriscv/Cargo.lock rename to Cargo.lock diff --git a/clash-vexriscv/Cargo.lock.license b/Cargo.lock.license similarity index 100% rename from clash-vexriscv/Cargo.lock.license rename to Cargo.lock.license diff --git a/clash-vexriscv/Cargo.toml b/Cargo.toml similarity index 100% rename from clash-vexriscv/Cargo.toml rename to Cargo.toml diff --git a/bittide-instances/LICENSE b/LICENSE similarity index 99% rename from bittide-instances/LICENSE rename to LICENSE index d64569567..261eeb9e9 100644 --- a/bittide-instances/LICENSE +++ b/LICENSE @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt deleted file mode 100644 index 13ca539f3..000000000 --- a/LICENSES/CC-BY-4.0.txt +++ /dev/null @@ -1,156 +0,0 @@ -Creative Commons Attribution 4.0 International - - Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. - -Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. - -Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. - -Section 1 – Definitions. - - a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. - - d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. - - g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. - - i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. - -Section 2 – Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: - - A. reproduce and Share the Licensed Material, in whole or in part; and - - B. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. - - 3. Term. The term of this Public License is specified in Section 6(a). - - 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. - - 5. Downstream recipients. - - A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. - - B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. - - 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). - -b. Other rights. - - 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this Public License. - - 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. - -Section 3 – License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified form), You must: - - A. retain the following if it is supplied by the Licensor with the Licensed Material: - - i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of warranties; - - v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; - - B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and - - C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. - - 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. - -Section 4 – Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; - - b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. -For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. - -Section 5 – Disclaimer of Warranties and Limitation of Liability. - - a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. - - b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. - - c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. - -Section 6 – Term and Termination. - - a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or - - 2. upon express reinstatement by the Licensor. - - c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. - - d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. - - e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. - -Section 7 – Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. - -Section 8 – Interpretation. - - a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. - - c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. - - d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. - -Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/Makefile b/Makefile deleted file mode 100644 index 28bf3b612..000000000 --- a/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -CARGO_TARGET_DIR=target - -.PHONY: build-sim -build-sim: - cabal build simcontranomy - - -.PHONY: contranomy-tests -contranomy-tests: build-sim copy-firmware-tests - cabal run contranomy:unittests - cabal run contranomy-sim:unittests - - -.PHONY: build-firmware-tests -build-firmware-tests: - cd firmware/tests; cargo build --release - -.PHONY: copy-firmware-tests -copy-firmware-tests: build-firmware-tests - cd firmware/tests; sh copy_test_binaries.sh - - -.PHONY: build-firmware-example-hello -build-firmware-example-hello: - cd firmware/examples/hello; cargo build --release --target-dir ../../../$(CARGO_TARGET_DIR) - - -.PHONY: sim-firmware-example-hello -sim-firmware-example-hello: build-sim build-firmware-example-hello - cabal run simcontranomy -- target/riscv32imc-unknown-none-elf/release/hello - - - -.PHONY: build-firmware-example-fdt-read -build-firmware-example-fdt-read: - cd firmware/examples/fdt-read; cargo build --release --target-dir ../../../$(CARGO_TARGET_DIR) - -.PHONY: sim-firmware-example-fdt-read -sim-firmware-example-fdt-read: build-sim build-firmware-example-fdt-read - cabal run simcontranomy -- target/riscv32imc-unknown-none-elf/release/fdt-read diff --git a/README.md b/README.md index f7cc12885..5bcc2e1e5 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,39 @@ -# bittide-hardware -`bittide/bittide-hardware` contains a (very much work-in-progress) hardware -implementation of a Bittide system (also see [About Bittide](#about-bittide)). We -currently target Vivado FPGAs paired with SkyWorks clock adjustment boards. +# VexRiscv core for Clash -# Getting started -This project uses a bunch of different languages and tool(chain)s. Nix is used -to manage this. To get a development shell [install nix](https://nixos.org/download.html) -and run the following command in the root of this repository: +This repository contains a [VexRiscv](https://github.com/SpinalHDL/VexRiscv) based CPU core and +bindings for use in [Clash](https://clash-lang.org/). -``` -nix-shell -``` +For now this repository only contains one +[example CPU configuration](clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala), +which implements a 32 bit IMC RISC-V core. -The started shell contains everything needed to develop Bittide components. +This package includes simulation via [`verilator`](https://github.com/verilator/verilator) as +well as a black-box for Verilog synthesis. -TODO: Add overview of components +The core interfaces with other components via [Wishbone](https://cdn.opencores.org/downloads/wbspec_b4.pdf) +interfaces, using [`clash-protocols`](https://github.com/clash-lang/clash-protocols) types. -# Development -We follow a standard GitHub development flow. Our development branch is called `staging`, which periodically gets merged into `main` (after running more expensive CI checks at night). Free free to open a PR. If you're not sure what to do, open a [discussion](https://github.com/bittide/bittide-hardware/discussions) thread. +## Building -## Tips & Tricks +For building the CPU, the following software needs to be installed and available in the `PATH`: - * The full (expensive) test suite only runs nightly on `staging`. If you want to run the full test suite on a PR, add `[force_expensive_checks]` to your commit message. - * While debugging, we often only want one bittide instance to be tested with our hardware-in-the-loop infrastructure. With the increasing number of bittide instances with are synthesized and tested, these CI runs take a long time. You can add a file `.github/synthesis/debug.json`, with only the instances you want CI to synthesize/test. The CI run will always fail on the 'all' job when this file exists to prevent a premature merge. - * You can run `format` in the Nix shell to format all Cabal, Haskell, and Rust files. +- a recent JDK installation +- SBT, the scala build tool +- a recent C compiler +- `verilator`, at least version 5.001 (development version at time of writing) +- `make` for building the verilated library and FFI code -# About Bittide -Bittide is a novel distributed system architecture based on the idea -that synchronous, ahead-of-time scheduling using a logical global -clock provides a set of properties that simplify the design and -utilization of large systems. +## Notes for using the core -With bittide we aim to build a system with guaranteed performance and -robustness and address today's distributed systems inefficiencies and -unpredictable long tail latencies. This ranges from simple things like -video conferencing without glitches to virtual reality sports -competitions with precisely equivalent system response times for all -competitors. The properties underlying bittide support the -orchestration and composition of microservices into a system without -queueing delays. +- VexRiscv has a "reset vector" for the instruction bus. This is the initial PC that gets fetched. + This address only gets presented to the IBUS after at least one cycle of RST being asserted. -_Bittide is part of Google Research, and is not an official Google product._ - -# Licence - -This code is shared under by Google LLC under the Apache 2.0 -license. See the [LICENSE](LICENSES/Apache-2.0.txt) file for terms. - -This project uses the [REUSE](https://reuse.software/) tool to check for -license compliance. +- The contents of memories need to be stored in little endian. This means that for example the + contents of an ELF file need to be endian-swapped before being used as the contents of the + instruction storage. This applies to all storages. diff --git a/bittide-experiments/bittide-experiments.cabal b/bittide-experiments/bittide-experiments.cabal deleted file mode 100644 index e39e2619a..000000000 --- a/bittide-experiments/bittide-experiments.cabal +++ /dev/null @@ -1,157 +0,0 @@ -cabal-version: 2.4 -name: bittide-experiments -synopsis: - Infrastructure and configurations needed for running - experiments with the Bittide systems under development - -version: 0.1 -license: Apache-2.0 -license-file: LICENSE -author: QBayLogic B.V. -maintainer: devops@qbaylogic.com -copyright: Copyright © 2024 Google LLC - -common common-options - default-extensions: - -- TemplateHaskell is used to support convenience functions such as - -- 'listToVecTH' and 'bLit'. - -- - -- `NoImplicitPrelude` is used because Clash offers Clash.Prelude - BangPatterns - BinaryLiterals - ConstraintKinds - DataKinds - DefaultSignatures - DeriveAnyClass - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveLift - DeriveTraversable - DerivingStrategies - ImportQualifiedPost - InstanceSigs - KindSignatures - LambdaCase - NoImplicitPrelude - NoStarIsType - PolyKinds - QuasiQuotes - RankNTypes - ScopedTypeVariables - StandaloneDeriving - TemplateHaskell - TupleSections - TypeApplications - TypeFamilies - TypeOperators - ViewPatterns - - ghc-options: - -- Plugins to support type-level constraint solving on naturals: - -- - GHC.TypeLits.Extra.Solver - -- - GHC.TypeLits.Normalise - -- - GHC.TypeLits.KnownNat.Solver - -- Clash needs access to the source code in compiled modules: - -- -fexpose-all-unfoldings - -- Worker wrappers introduce unstable names for functions that might have - -- blackboxes attached for them. You can disable this, but be sure to add - -- a no-specialize pragma to every function with a blackbox. - -- -fno-worker-wrapper - -Wall - -Wcompat - -haddock - -fplugin=GHC.TypeLits.Extra.Solver - -fplugin=GHC.TypeLits.Normalise - -fplugin=GHC.TypeLits.KnownNat.Solver - -fexpose-all-unfoldings - -fno-worker-wrapper - - build-depends: - -- clash-prelude will set suitable version bounds for the plugins - Cabal, - base, - clash-prelude >=1.7.0 && <1.10, - ghc-typelits-extra, - ghc-typelits-knownnat, - ghc-typelits-natnormalise, - template-haskell, - -library - import: common-options - hs-source-dirs: src - build-depends: - aeson, - aeson-pretty, - array, - bittide, - bytestring, - cassava, - clash-cores, - clash-lib, - containers, - data-default, - directory, - extra, - filepath, - happy-dot, - http-conduit, - http-types, - matplotlib, - optparse-applicative, - process, - random, - temporary, - text, - typelits-witnesses, - vector, - - exposed-modules: - Bittide.Github.Artifacts - Bittide.Hitl - Bittide.Plot - Bittide.Report.ClockControl - Bittide.Simulate.Config - Bittide.Topology - - default-language: Haskell2010 - -test-suite unittests - import: common-options - type: exitcode-stdio-1.0 - main-is: unittests.hs - ghc-options: - -Wall - -Wcompat - -threaded - -rtsopts - -with-rtsopts=-N - - default-language: Haskell2010 - hs-source-dirs: tests - build-depends: - base, - bittide, - bittide-experiments, - clash-prelude, - tasty, - tasty-hedgehog, - tasty-hunit, - -test-suite doctests - type: exitcode-stdio-1.0 - hs-source-dirs: tests - main-is: doctests.hs - ghc-options: -threaded - ghc-options: - -Wall - -Wcompat - -threaded - - build-depends: - base, - bittide-experiments, - doctest-parallel >=0.3.0.1 && <0.4, - - default-language: Haskell2010 diff --git a/bittide-experiments/bittide-experiments.cabal.license b/bittide-experiments/bittide-experiments.cabal.license deleted file mode 100644 index 2b4d55897..000000000 --- a/bittide-experiments/bittide-experiments.cabal.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/bittide-experiments/src/Bittide/Github/Artifacts.hs b/bittide-experiments/src/Bittide/Github/Artifacts.hs deleted file mode 100644 index d6ba92d61..000000000 --- a/bittide-experiments/src/Bittide/Github/Artifacts.hs +++ /dev/null @@ -1,200 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ImplicitPrelude #-} - -module Bittide.Github.Artifacts ( - RunId, - ArtifactName, - ArtifactAccessError, - retrieveArtifact, -) where - -import Control.Exception (throwIO) -import Control.Monad (forM) -import Data.Aeson ( - FromJSON (..), - Result (..), - Value (..), - decodeFileStrict, - fromJSON, - withArray, - withObject, - (.:), - ) -import Data.Map.Strict (Map) -import Network.HTTP.Conduit (requestHeaders) -import Network.HTTP.Simple ( - JSONException (..), - getResponseBody, - getResponseStatus, - httpJSONEither, - parseRequest, - ) -import Network.HTTP.Types.Header (hUserAgent) -import Network.HTTP.Types.Status (Status (..)) -import System.Directory (createDirectoryIfMissing, listDirectory) -import System.Environment (getProgName, lookupEnv) -import System.FilePath (()) -import System.IO.Temp (withSystemTempDirectory) -import System.Process (callCommand, callProcess) - -import Data.ByteString.Char8 qualified as ByteString (pack) -import Data.Map.Strict qualified as Map (fromList, lookup) -import Data.Text qualified as Text (unpack) -import Data.Vector qualified as Vector (toList) - --- | The environment variable used to share the artifact access token. -accessTokenEnvVar :: String -accessTokenEnvVar = "BITTIDE_ARTIFACT_ACCESS_TOKEN" - --- | The Bittide repository on Github. -bittideRepo :: String -bittideRepo = "bittide/bittide-hardware" - --- | Offers the artifacts list of a given run via the Github API. -githubApiArtifacts :: String -> String -> String -githubApiArtifacts repo run = - "https://api.github.com/repos/" - <> repo - <> "/actions/runs/" - <> run - <> "/artifacts?per_page=100" - --- | The unique identifier of the Github Action run. -type RunId = String - --- | The name of the artifact to be downloaded. -type ArtifactName = String - -{- | Everything that can go wrong while trying to download an artifact -from the Bittide Github repository with 'retrieveArtifact' that has -its origin on some invalid user input passed to 'retrieveArtifact'. --} -data ArtifactAccessError - = NoAccessToken - | InvalidAccessToken - | RunNotFound RunId - | ArtifactNotFound RunId ArtifactName - -instance Show ArtifactAccessError where - show = \case - NoAccessToken -> - "No access token found. A valid access token must be set via\n" - <> "the " - <> accessTokenEnvVar - <> " environment variable." - InvalidAccessToken -> - "The provided access token has no access to the Bittide artifacts." - RunNotFound runId -> - "Invalid run ID \"" - <> runId - <> "\". Cannot access the data for\n" - <> "the provided ID." - ArtifactNotFound runId artifactName -> - "There is no artifact named \"" - <> artifactName - <> "\" for the\n" - <> "run with ID " - <> runId - <> "." - -{- | A newtype wrapper for extracting the "artifact name -> download -url" mapping of a run via the Github API. --} -newtype ArtifactDownloadUrl = ArtifactDownloadUrl (Map String String) - -instance FromJSON ArtifactDownloadUrl where - parseJSON = - withObject "root" $ \root -> - (root .: "artifacts" >>=) $ - withArray "artifacts" $ \as -> - fmap (ArtifactDownloadUrl . Map.fromList) $ - forM (Vector.toList as) $ - withObject "artifact" $ \artifact -> do - String name <- artifact .: "name" - String url <- artifact .: "archive_download_url" - return (Text.unpack name, Text.unpack url) - --- | A newtype wrapper for reading back curl response messages -newtype CurlResponseMessage = CurlResponseMessage String - -instance FromJSON CurlResponseMessage where - parseJSON = - (CurlResponseMessage . Text.unpack <$>) - . withObject "root" (.: "message") - -{- | Retrieve the artifact with the given name for the given run id -and save it at the provided location. An 'ArtifactAccessError' is -returned on failure with respect to the provided arguments. If the -arguments are valid, but there is some external problem with the -utilized process, then that error gets reported via an exception -instead. --} -retrieveArtifact :: - RunId -> ArtifactName -> FilePath -> IO (Maybe ArtifactAccessError) -retrieveArtifact runId artifactName destination = do - appName <- getProgName - request <- parseRequest $ githubApiArtifacts bittideRepo runId - response <- - httpJSONEither - request - { -- Github requires to set the User Agent header, as the request - -- will always be rejected otherwise - requestHeaders = [(hUserAgent, ByteString.pack appName)] - } - case getResponseBody response of - Left err@(JSONParseException{}) -> throwIO err - Left err@(JSONConversionException _ resp _) -> - if statusCode (getResponseStatus resp) == 404 - then return $ Just $ RunNotFound runId - else throwIO err - Right (ArtifactDownloadUrl downloadUrls) -> do - case Map.lookup artifactName downloadUrls of - Nothing -> return $ Just $ ArtifactNotFound runId artifactName - Just downloadUrl -> - lookupEnv accessTokenEnvVar >>= \case - Nothing -> return $ Just NoAccessToken - Just accessToken -> - withSystemTempDirectory "retrieve-artifact" $ \path -> do - let file = path "artifact.zip" - putStrLn $ "Retrieving " <> artifactName <> ".zip" - putStrLn "---" - callCommand $ - unwords - [ "curl" - , "--location" - , "--header" - , "\"authorization: Bearer " <> accessToken <> "\"" - , "--output" - , file - , downloadUrl - ] - putStr "---" - -- if the downloaded file is a JSON instead of zip, then - -- something went wrong - decodeFileStrict file >>= \case - Just jsonValue -> do - putStrLn " Failed." - if case fromJSON jsonValue of - Success (CurlResponseMessage msg) -> - msg == "Bad credentials" - _ -> False - then return $ Just InvalidAccessToken - else do - req <- parseRequest downloadUrl - throwIO $ - JSONConversionException - req - (jsonValue <$ response) - "curl download failed" - Nothing -> do - putStrLn " Success." - callProcess "unzip" ["-q", file, "-d", path] - callProcess "rm" [file] - createDirectoryIfMissing True destination - (listDirectory path >>=) $ mapM_ $ \x -> do - callProcess "rm" ["-Rf", destination x] - callProcess "mv" [path x, destination x] - return Nothing diff --git a/bittide-experiments/src/Bittide/Hitl.hs b/bittide-experiments/src/Bittide/Hitl.hs deleted file mode 100644 index 699524cf1..000000000 --- a/bittide-experiments/src/Bittide/Hitl.hs +++ /dev/null @@ -1,334 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} - -{- | Tooling to define hardware-in-the-loop (HITL) tests. HITL tests in the -Bittide project involve FPGA designs that incorporate a -[VIO IP core](https://www.xilinx.com/products/intellectual-property/vio.html) -to interface with the HITL test controller. This VIO is used to start tests, -communicate test status and to optionally (depending on the test -definition) provide the FPGA under test with an additional configurable -parameter. In practice, developers writing HITL tests should make sure to do -two things: - - 1. They should incorporate a HITL VIO in their design. The HITL test controller - expects such a VIO to have at minimum an output probe named - @probe_test_start@ and input probes named @probe_test_done@ and - @probe_test_success@, all booleans. See 'hitlVio' and - 'hitlVioBool' for examples. When parameters are used (see below) that - have a BitSize larger than 0, an @probe_test_data@ output probe with an - equivalent BitSize must be added. - - 2. They should define the hardware targets to run the tests against - (multiple FPGAs, or just one), and with which parameters each of these - hardware targets should be provided before the test is started. - See 'HitlTestGroup' for examples, together with its convenience functions - 'allTargets', 'paramForHwTargets', 'paramForSingleHwTarget' and 'testCasesFromEnum'. - -Tests are collected in @Bittide.Instances.Hitl.Tests@. - -=== __Flow overview__ - - 1. User calls @shake \:test@ to run HITL tests. - 2. Shake builds a bitstream, programs the FPGA, and runs the HITL tests by - interacting with Vivado in TCL mode using the @vivado-hs@ package. --} -module Bittide.Hitl ( - ClashTargetName, - FpgaId, - HwTargetRef (..), - - -- * Test definition - HitlTestGroup (..), - HitlTestCase (..), - MayHavePostProcData (..), - Done, - Success, - hitlVio, - hitlVioBool, - - -- * Test construction convenience functions - paramForHwTargets, - paramForSingleHwTarget, - testCasesFromEnum, - hwTargetRefsFromHitlTestGroup, -) -where - -import Prelude - -import Clash.Prelude (BitPack (BitSize), KnownDomain, Vec (Nil, (:>)), natToInteger) - -import Clash.Cores.Xilinx.VIO (vioProbe) - -import Data.Containers.ListUtils (nubOrd) -import Data.Map.Strict (Map) -import Data.Maybe (isJust) -import Data.Typeable (Typeable) -import Language.Haskell.TH.Syntax (Name) -import Numeric.Natural (Natural) - -import Clash.Prelude qualified as P -import Data.Map.Strict qualified as Map - -{- | Fully qualified name to a function that is the target for Clash -compilation. E.g. @Bittide.Foo.topEntity@. --} -type ClashTargetName = Name - -{- | The FPGA ID section of a Vivado hardware target. This is what Vivado seems -to call the UID of a hardware target minus the vendor string. - -For example, the ID of hardware target -"localhost:3121/xilinx_tcf/Digilent/210308B0B0C2" is "210308B0B0C2". --} -type FpgaId = String - -{- | A reference to an FPGA hardware target, either by index/relative position -in the Bittide demo rig or by ID. --} -data HwTargetRef - = HwTargetByIndex Natural - | HwTargetById FpgaId - deriving (Eq, Ord, Show) - -{- | A definition of a test that should be performed with hardware in the loop. -Such a HITL test definition can have one or more named test cases that may differ in -what hardware targets (FPGAs) they involve and in what parameters they provide -to every such hardware target (see `parameters`). -Furthermore, some additional data can be provided, if required by optional -subsequent post-processing steps. - -=== __Example: Test without parameters__ -A test that runs for all FPGAs, and does not require any parameters (the -parameter is set to `()`): - -> test :: HitlTestGroup -> test = HitlTestGroup -> { topEntity = ... -> , extraXdcFiles = [] -> , testCases = [HitlTestCase "testCaseName" (paramForHwTargets allHwTargets ()) ()] -> , mPostProc = Nothing -> } - -This must be accompanied by a @hitlVioBool@ in the design. - -=== __Example: Test based on an enum__ -A test that runs for each constructor of an enum: - -> data ABC = A | B | C -> -> testExtended :: HitlTestGroup -> testExtended = HitlTestGroup -> { topEntity = ... -> , extraXdcFiles = [] -> , testCases = testCasesFromEnum @ABC allHwTargets () -> , mPostProc = Nothing -> } - -This must be accompanied by a @hitlVio \@ABC@ in the design. - -=== __Example: Test without post processing data that runs on specific FPGAs, -and requires a (hypothetical) 8-bit number indicating the -\"number of stages\" to be set on each FPGA: - -> type NumberOfStages = P.Unsigned 8 -> -> test :: HitlTestGroup -> test = HitlTestGroup -> { topEntity = '() -> , extraXdcFiles = [] -> , testCases = -> [ HitlTestCase -> { name = "Twelve stages on FPGA 2 and 5" -> , parameters = Map.fromList -> [ (HwTargetByIndex 2, 12 :: NumberOfStages) -> , (HwTargetByIndex 5, 12) -> ] -> , postProcData = () -> } -> , HitlTestCase -> { name = "Six stages on FPGA 3, seven on FPGA 4" -> , parameters = Map.fromList -> [ (HwTargetByIndex 3, 6) -> , (HwTargetByIndex 4, 7) -> ] -> , postProcData = () -> } -> ] -> , mPostProc = Nothing -> } - -This must be accompanied by a @hitlVio \@NumberOfStages@ in the design. --} -data HitlTestGroup where - HitlTestGroup :: - (Typeable a, Typeable b) => - { topEntity :: ClashTargetName - -- ^ Reference to the Design Under Test - , extraXdcFiles :: [String] - , testCases :: [HitlTestCase HwTargetRef a b] - -- ^ List of test cases - , mPostProc :: Maybe String - -- ^ Optional post processing step. If present, the name of the executable - -- in the @bittide-instances@ package. - , externalHdl :: [String] - -- ^ List of external HDL files to include in the project - } -> - HitlTestGroup - -{- | A HITL test case. One HITL test group can have multiple test cases -associated with it. --} -data HitlTestCase h a b where - HitlTestCase :: - (Show h, Show a, BitPack a, Show b, Typeable h) => - { name :: String - , parameters :: Map h a - , postProcData :: b - } -> - HitlTestCase h a b - -deriving instance Show (HitlTestCase h a b) - --- | A class for extracting optional post processing data from a test. -class MayHavePostProcData b where - -- | Returns the test names with some post processing data of type @c@, - -- if that data exists. - mGetPPD :: - forall h a. - [HitlTestCase h a b] -> - Map String (Maybe b) - -instance MayHavePostProcData a where - mGetPPD cases = - Map.fromList - [(name, Just postProcData) | HitlTestCase{..} <- cases] - -instance MayHavePostProcData () where - mGetPPD = Map.fromList . map ((,Nothing) . name) - --- | Obtain a list of the hardware targets that are relevant for a given HITL test. -hwTargetRefsFromHitlTestGroup :: HitlTestGroup -> [HwTargetRef] -hwTargetRefsFromHitlTestGroup HitlTestGroup{testCases} = - nubOrd $ concatMap (map fst . Map.toList . parameters) testCases - --- | Provide a given list of hardware targets with one parameter. -paramForHwTargets :: [HwTargetRef] -> a -> Map HwTargetRef a -paramForHwTargets hwTs param = Map.fromList $ map (,param) hwTs - --- | Returns the hardware target to parameter map for a single hardware target. -paramForSingleHwTarget :: HwTargetRef -> a -> Map HwTargetRef a -paramForSingleHwTarget = Map.singleton - -{- | Generate a set of HITL test cases from an enum. E.g., if you defined a -data type looking like: - -> data ABC = A | B | C -> deriving (BitPack, Bounded, Enum, Generic, Show) - -You can use the following to generate a test case for each contructor -of @ABC@. Every such case is named after the constructor that gave rise -to it and receives that constructur as test parameter. - -> testCases :: [HitlTestCase HwTargetRef ABC ()] -> testCases = testCasesFromEnum @ABC allHwTargets () --} -testCasesFromEnum :: - forall a b. - (Show a, Bounded a, Enum a, BitPack a, Show b, Typeable a, Typeable b) => - [HwTargetRef] -> - b -> - [HitlTestCase HwTargetRef a b] -testCasesFromEnum hwTs ppd = - [ HitlTestCase - { name = show constr - , parameters = Map.fromList ((,constr) <$> hwTs) - , postProcData = ppd - } - | (constr :: a) <- [minBound ..] - ] - --- | Whether a test has been completed, see 'hitlVio'. -type Done = Bool - --- | Whether a test has been completed successfully, see 'hitlVio'. -type Success = Bool - -{- | Instantiate this VIO in a design you'd like to test with hardware in the -loop. Its output is set to 'Nothing' if a test is not running, and will be -set to 'Just' if it is. --} -hitlVio :: - forall a dom. - ( KnownDomain dom - , BitPack a - ) => - -- | Default value for @a@. This is an artifact of this VIO internally representing - -- the output value as two probes (\"valid\" and \"data\") to accommodate the - -- HITL test infrastructure. Hence, the actual value of the default doesn't - -- matter: whenever it is output, this VIO will output 'Nothing'. - -- - -- TODO: Allow use of 'errorX' in 'vioProbe' - a -> - P.Clock dom -> - -- | Should be asserted when a test is done. For sanity checking the HITL test - -- infrastructure, this must be *deasserted* when a test is not running. - P.Signal dom Done -> - -- | When 'Done' is asserted, this signal indicates whether a test has been - -- completed successfully. - P.Signal dom Success -> - -- | Test parameter supplied by the VIO. Test modules should export a symbol - -- @test :: HitlTestGroup@ that defines this parameter for every hardware target - -- (FPGA) that the test involves. - P.Signal dom (Maybe a) -hitlVio dflt clk done success - | natToInteger @(BitSize a) == 0 = - -- XXX: This branch is a workaround for 'vioProbe' not handling zero-width - -- ports properly. - P.mux start (pure (Just dflt)) (pure Nothing) - where - start = - P.setName @"vioHitlt" $ - vioProbe - ("probe_test_done" :> "probe_test_success" :> Nil) - ("probe_test_start" :> Nil) - False - clk - done - success -hitlVio dflt clk done success = - P.mux start (Just <$> dat) (pure Nothing) - where - (P.unbundle -> (start, dat)) = - P.setName @"vioHitlt" $ - vioProbe - ("probe_test_done" :> "probe_test_success" :> Nil) - ("probe_test_start" :> "probe_test_data" :> Nil) - (False, dflt) - clk - done - success - -{- | Instantiate this VIO in a design you'd like to test with hardware in the -loop. Its output is set to 'True' if a test is not running, and will be -set to 'False' if it is. --} -hitlVioBool :: - forall dom. - (KnownDomain dom) => - P.Clock dom -> - -- | Should be asserted when a test is done. For sanity checking the HITL test - -- infrastructure, this must be *deasserted* when a test is not running. - P.Signal dom Done -> - -- | When 'Done' is asserted, this signal indicates whether a test has been - -- completed successfully. - P.Signal dom Success -> - -- | Test started? - P.Signal dom Bool -hitlVioBool clk done success = isJust <$> hitlVio () clk done success diff --git a/bittide-experiments/src/Bittide/Plot.hs b/bittide-experiments/src/Bittide/Plot.hs deleted file mode 100644 index ea527f8a7..000000000 --- a/bittide-experiments/src/Bittide/Plot.hs +++ /dev/null @@ -1,237 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE ImplicitPrelude #-} - -module Bittide.Plot ( - ReframingStage (..), - fromRfState, - plot, - plotClocksFileName, - plotElasticBuffersFileName, -) where - -import Clash.Prelude (KnownNat, Vec) -import Clash.Signal.Internal (Femtoseconds (..)) -import Clash.Sized.Vector qualified as Vec - -import Bittide.Arithmetic.PartsPer (PartsPer, toPpm) -import Control.Monad (void) -import Data.Graph (edges) -import Data.Int (Int64) -import Data.List (foldl', transpose, unzip4, zip4) -import GHC.Float.RealFracMethods (roundFloatInteger) -import System.FilePath (()) - -import Graphics.Matplotlib ( - Matplotlib, - axes, - figure, - file, - legend, - mp, - o1, - o2, - xlabel, - ylabel, - (%), - (@@), - ) -import Graphics.Matplotlib qualified as MP (plot) - -import Bittide.ClockControl (RelDataCount) -import Bittide.ClockControl.Callisto (ReframingState (..)) -import Bittide.ClockControl.StabilityChecker qualified as SC (StabilityIndication (..)) -import Bittide.Topology - -{- $setup ->>> import Clash.Prelude ->>> import Data.Proxy --} - -{- | 'Bittide.ClockControl.Callisto.ReframingState' reduced to its -stages. --} -data ReframingStage = RSDetect | RSWait | RSDone deriving (Show) - --- | Default name of the clocks plot file. -plotClocksFileName :: String -plotClocksFileName = "clocks.pdf" - --- | Default name of the elastic buffers plot file. -plotElasticBuffersFileName :: String -plotElasticBuffersFileName = "elasticbuffers.pdf" - -fromRfState :: ReframingState -> ReframingStage -fromRfState = \case - Detect{} -> RSDetect - Wait{} -> RSWait - Done{} -> RSDone - -plot :: - forall nNodes m. - (KnownNat nNodes, KnownNat m) => - -- | A common correction to apply to all clock plots - Maybe PartsPer -> - -- | output directory for storing the results - FilePath -> - -- | topology corresponding to the plot - Topology nNodes -> - -- | plot data - Vec - nNodes - [ ( Femtoseconds -- time - , PartsPer -- relative clock offset - , ReframingStage - , [(RelDataCount m, SC.StabilityIndication)] - ) - ] -> - IO () -plot maybeCorrection outputDir graph plotData = - matplotWrite outputDir maybeCorrection clockPlots elasticBufferPlots - where - clockPlots = Vec.imap toClockPlot plotData - elasticBufferPlots = Vec.imap toElasticBufferPlot plotData - - edgeCount = length $ edges $ topologyGraph graph - - toElasticBufferPlot nodeIndex (unzip4 -> (time, _, reframingStage, buffersPerNode)) = - foldPlots - $ fmap - ( -- Too many legend entries don't fit. We picked 20 by - -- simply observing when legend entries wouldn't fit - -- anymore. - if edgeCount <= 20 - then \(j, p) -> - withLegend $ - p @@ [o2 "label" $ show nodeIndex <> " ← " <> show j] - else snd - ) - $ zip (filter (hasEdge graph nodeIndex) [0, 1 ..]) - $ fmap plotEbData - -- Organize data by node instead of by timestamp. I.e., the first item in - -- 'timedBuffers' is for this node's first neighbor. - $ transpose - $ [ [(t, r, dataCount, stability) | (dataCount, stability) <- bs] - | (t, r, bs) <- zip3 time reframingStage buffersPerNode - ] - - toClockPlot nodeIndex (unzip4 -> (time, relativeOffsetPartsPer, _, _)) = - withLegend $ - (@@ [o2 "label" $ fromEnum nodeIndex]) $ - MP.plot - (map fsToMs time) - (map (toPpm . correctOffset) relativeOffsetPartsPer) - - withLegend = - ( @@ - [ o2 "bbox_to_anchor" (1.01 :: Double, 1 :: Double) - , o2 "loc" "upper left" - ] - ) - . (% legend) - - correctOffset = case maybeCorrection of - Just c -> (+ c) - Nothing -> id - -data Marking = Waiting | Stable | Settled | None deriving (Eq) - --- | Convert femtoseconds to milliseconds -fsToMs :: Femtoseconds -> Int64 -fsToMs (Femtoseconds fs) = - -- fs -> ps -> ns -> µs -> ms - fs `div` 1_000_000_000_000 - -{- | Plots the datacount of an elastic buffer and marks those parts of -the plots that are reported to be stable/settled by the stability -checker as well as the time frames at which the reframing detector -is in the waiting state. --} -plotEbData :: - (KnownNat m) => - [(Femtoseconds, ReframingStage, RelDataCount m, SC.StabilityIndication)] -> - Matplotlib -plotEbData (unzip4 -> (timestampsFs, reframingStages, dataCounts, stabilities)) = - foldPlots markedIntervals % ebPlot - where - timestamps = map fsToMs timestampsFs - xs = zip4 timestamps reframingStages dataCounts stabilities - - mGr = (@@ [o1 "g-", o2 "linewidth" (8 :: Int)]) -- green marking - mBl = (@@ [o1 "b-", o2 "linewidth" (8 :: Int)]) -- blue marking - mRe = (@@ [o1 "r-", o2 "linewidth" (8 :: Int)]) -- red marking - ebPlot = MP.plot timestamps dataCounts - - mindMarking ys ms = \case - Waiting -> (mRe, reverse ys) : ms - Stable -> (mBl, reverse ys) : ms - Settled -> (mGr, reverse ys) : ms - None -> ms - - markedIntervals = - (\(mark, ys) -> mark $ uncurry MP.plot $ unzip ys) - <$> collectIntervals ((None, []), []) xs - - collectIntervals ((previous, ys), markings) [] = - mindMarking ys markings previous - collectIntervals ((previous, ys), markings) ((t, mode, d, sci) : xr) = - collectIntervals a' xr - where - current = case mode of - RSWait{} -> Waiting - _ - | SC.settled sci -> Settled - | SC.stable sci -> Stable - | otherwise -> None - - markings' = mindMarking ys markings previous - - a' - | current == previous = ((current, (t, d) : ys), markings) - | current == None = ((None, []), markings') - | otherwise = ((current, [(t, d)]), markings') - -{- | Folds the vectors of generated plots and writes the results to -the disk. --} -matplotWrite :: - (KnownNat n) => - -- | output directory - FilePath -> - -- | Correction to add to Y-axis label - Maybe PartsPer -> - -- | clock plots - Vec n Matplotlib -> - -- | elastic buffer plots - Vec n Matplotlib -> - IO () -matplotWrite dir maybeCorrection clockDats ebDats = do - void $ - file (dir plotClocksFileName) $ - constrained - ( xlabel "Time (ms)" - % ylabel ("Relative frequency (ppm)" <> correctionLabel) - % foldPlots (reverse $ Vec.toList clockDats) - ) - void $ - file (dir plotElasticBuffersFileName) $ - constrained - ( xlabel "Time (ms)" - % ylabel "Occupancy count" - % foldPlots (Vec.toList ebDats) - ) - where - correctionLabel = case maybeCorrection of - Just (roundFloatInteger . toPpm -> c) - | c < 0 -> " [" <> show c <> "]" - | otherwise -> " [+" <> show c <> "]" - _ -> "" - - constrained = - ((figure @@ [o2 "layout" "constrained"] % axes) %) - --- | Folds multiple plots together -foldPlots :: [Matplotlib] -> Matplotlib -foldPlots = foldl' (%) mp diff --git a/bittide-experiments/src/Bittide/Report/ClockControl.hs b/bittide-experiments/src/Bittide/Report/ClockControl.hs deleted file mode 100644 index 14ca20932..000000000 --- a/bittide-experiments/src/Bittide/Report/ClockControl.hs +++ /dev/null @@ -1,352 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ImplicitPrelude #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} - -module Bittide.Report.ClockControl ( - generateReport, - checkDependencies, - checkIntermediateResults, - formatThousands, -) where - -import Clash.Prelude (Domain, KnownDomain, Milliseconds, natToNum) - -import Data.Bool (bool) -import Data.List (intercalate) -import Data.List.Extra (chunksOf) -import Data.Proxy (Proxy (..)) -import GHC.Float.RealFracMethods (roundFloatInteger) -import System.Directory (doesDirectoryExist, doesFileExist, findExecutable) -import System.Environment (lookupEnv) -import System.FilePath (takeFileName, ()) -import System.IO ( - BufferMode (..), - IOMode (..), - hClose, - hFlush, - hPutStr, - hSetBuffering, - withFile, - ) -import System.IO.Temp (withSystemTempDirectory) -import System.Process (callProcess, readProcess) - -import Bittide.Arithmetic.PartsPer (toPpm) -import Bittide.Arithmetic.Time (PeriodToCycles) -import Bittide.Plot -import Bittide.Simulate.Config (CcConf (..), simTopologyFileName) - -{- | Format a number with underscores every three digits. - ->>> formatThousands 123456789 -"123,456,789" ->>> formatThousands 100000000 -"100,000,000" ->>> formatThousands 1000000 -"1,000,000" --} -formatThousands :: (Num a, Show a) => a -> String -formatThousands = reverse . intercalate "," . chunksOf 3 . reverse . show - -generateReport :: - (KnownDomain refDom) => - -- | Reference domain - Proxy (refDom :: Domain) -> - -- | Document description header - String -> - -- | Directory containing the intermediate plot results - FilePath -> - -- | Node identifiers - [(Integer, String)] -> - -- | The utilized simulation configuration - CcConf -> - IO () -generateReport refDom (("Bittide - " <>) -> header) dir ids cfg = - withSystemTempDirectory "generate-report" $ \tmpDir -> do - Just runref <- lookupEnv "RUNREF" - -- remove the 'n' prefix from the node names - readProcess - "sed" - [ "-e" - , "s/n\\([0-9]*\\)/\\1/g" - , topologyGv - ] - [] - >>= writeFile (tmpDir takeFileName topologyGv) - -- render the topology as a tikz picture - topTikz <- - readProcess - "dot2tex" - [ "-f" - , "tikz" - , "--figonly" - , "--progoptions" - , "-K neato" - , "--nodeoptions" - , "every node/.style={" - <> intercalate - "," - [ "fill" - , "text=white" - , "font=\\Large\\tt" - , "minimum size=2em" - , "inner sep=0pt" - ] - <> "}" - , "--edgeoptions" - , "line width=0.3em" - , tmpDir takeFileName topologyGv - ] - [] - -- get the current date/time reference - datetime <- - readProcess - "date" - [ "+%Y-%m-%d %H:%M:%S" - ] - [] - -- create the latex report - withFile (tmpDir "report.tex") WriteMode $ \h -> do - hSetBuffering h NoBuffering - hPutStr h $ toLatex refDom datetime runref header clocksPdf ebsPdf topTikz ids cfg - hFlush h - hClose h - -- create the report pdf - callProcess - "lualatex" - [ "--output-directory=" <> tmpDir - , tmpDir "report.tex" - ] - -- move it to the target directory - callProcess - "mv" - [ tmpDir "report.pdf" - , dir "report.pdf" - ] - where - clocksPdf = dir plotClocksFileName - ebsPdf = dir plotElasticBuffersFileName - topologyGv = dir simTopologyFileName - -checkDependencies :: IO (Maybe String) -checkDependencies = - either Just (const Nothing) . sequence_ - <$> sequence - [ checkEVarExists "RUNREF" - , checkProgExists "mv" - , checkProgExists "sed" - , checkProgExists "dot2tex" - , checkProgExists "date" - ] - where - checkEVarExists e = - maybe (Left $ "Missing environment variable: " <> e) (const $ Right ()) - <$> lookupEnv e - - checkProgExists p = - maybe (Left $ "Missing dependency: " <> p) (const $ Right ()) - <$> findExecutable p - -checkIntermediateResults :: FilePath -> IO (Maybe String) -checkIntermediateResults dir = - either Just (const Nothing) . sequence_ - <$> sequence - [ checkDirExists dir - , checkFileExists $ dir plotClocksFileName - , checkFileExists $ dir plotElasticBuffersFileName - , checkFileExists $ dir simTopologyFileName - ] - where - checkDirExists d = - bool (Left $ "No such directory: " <> d) (Right ()) - <$> doesDirectoryExist d - - checkFileExists f = - bool (Left $ "Missing file: " <> f) (Right ()) - <$> doesFileExist f - -toLatex :: - forall refDom. - (KnownDomain refDom) => - -- | Reference domain - Proxy (refDom :: Domain) -> - -- | date & time reference - String -> - -- | Github run reference - String -> - -- | Document description header - String -> - -- | File path of the clocks plot pdf - FilePath -> - -- | File path of the elastic buffers plot pdf - FilePath -> - -- | Tikz plot of the topology - String -> - -- | Node identifiers - [(Integer, String)] -> - -- | The utilized simulation configuration - CcConf -> - String -toLatex _refDom datetime runref header clocksPdf ebsPdf topTikz ids CcConf{..} = - unlines - [ "\\documentclass[landscape]{article}" - , "" - , "\\usepackage[top=3cm,bottom=3cm,left=1.5cm,right=1.5cm]{geometry}" - , "\\usepackage[hidelinks]{hyperref}" - , "\\usepackage[dvipsnames]{xcolor}" - , "\\usepackage{graphicx}" - , "\\usepackage{pifont}" - , "\\usepackage{fancyhdr}" - , "\\usepackage{tikz}" - , "\\usepackage{siunitx}" - , "" - , "\\usetikzlibrary{shapes, calc, shadows}" - , "" - , "% Not actually an SI unit" - , "\\DeclareSIUnit\\ppm{ppm}" - , "" - , "\\pagestyle{fancy}" - , "\\fancyhf{}" - , "\\fancyhead[L]{\\large \\textbf{" <> header <> "}}" - , "\\fancyhead[C]{\\large Topology Type: \\texttt{" - <> show ccTopologyType - <> "}}" - , "\\fancyhead[R]{\\large " <> datetime <> "}" - , "\\renewcommand{\\headrulewidth}{0.4pt}" - , "\\fancyfoot[L]{\\large\\textit{\\href{" - <> runref - <> "}{" - <> runref - <> "}}}" - , "\\fancyfoot[R]{\\large\\copyright~Google Inc., QBayLogic B.V.}" - , "\\renewcommand{\\footrulewidth}{0.4pt}" - , "" - , "\\parindent0pt" - , "" - , "\\begin{document}" - , "" - , "\\ \\vspace{3em}" - , "" - , "\\begin{center}" - , "\\begin{tikzpicture}[overlay, xshift=0.27\\textwidth, yshift=-3]" - , "\\node {" - , "\\begin{tikzpicture}" - , "\\node (A) {\\resizebox{!}{10em}{" - , topTikz - , "}};" - , if null ids - then "" - else - unlines $ - [ "\\node at ($ (A.east) + (3,0) $) {" - , "\\small\\tt" - , "\\begin{tabular}{r|c}" - , " & \\textbf{FPGA ID} \\\\[0.1em]" - , "\\hline \\\\[-0.9em]" - ] - <> map (\(i, n) -> show i <> " & " <> n <> " \\\\") ids - <> [ "\\end{tabular}" - , "};" - ] - , "\\end{tikzpicture}" - , "};" - , "\\end{tikzpicture}" - , "\\end{center}" - , "" - , "\\vspace{-5em}" - , "" - , "\\begin{large}" - , " \\begin{tabular}{rl}" - , " timeout after:" - , " & " <> qtyMs durationMs <> " \\\\" - , " stability detector - framesize:" - , " & " <> qtyMs stabilityFrameSizeMs <> " \\\\" - , " stability detector - margin:" - , " & \\textpm\\," <> formatThousands stabilityMargin <> " elements \\\\" - , " when stable, stop after:" - , " & " <> maybe "\\textit{not used}" qtyMs stopAfterStableMs <> " \\\\" - , " clock offsets:" - , " & " <> maybe "\\textit{not used}" formatOffsets clockOffsets <> " \\\\" - , " startup delays:" - , " & " <> intercalate "; " (qtyMs <$> startupDelaysMs) <> " \\\\" - , " reframing:" - , " & " - <> "\\textit{" - <> bool "disabled" "enabled" reframe - <> "} \\\\" - , if reframe then " wait time: & " <> show waitTime <> " \\\\" else "" - , " all buffers stable end of run:" - , " & " - <> maybe - "" - ( bool - "\\textcolor{red!50!black}{\\ding{55}}" - "\\textcolor{green!50!black}{\\ding{51}}" - ) - stable - <> " \\\\" - , " \\end{tabular}" - , "\\end{large}" - , "" - , "\\vfill" - , "" - , "\\begin{center}" - , " \\begin{tikzpicture}" - , " \\node (clocks) at (0,0) {" - , " \\includegraphics[width=.49\\textwidth]{" <> clocksPdf <> "}" - , " };" - , " \\node (ebs) at (0.51\\textwidth, 0) {" - , " \\includegraphics[width=.49\\textwidth]{" <> ebsPdf <> "}" - , " };" - , " \\node at ($ (clocks.north) + (0,0) $) {" - , " \\textbf{Clocks}" - , " };" - , " \\node at ($ (ebs.north) + (0,0) $) {" - , " \\textbf{Elastic Buffer Occupancies}" - , " };" - , " \\node[overlay,anchor=north west,fill=blue]" - , " (A) at ($ (ebs.south west) + (1.55,0) $) {};" - , " \\node[overlay,anchor=north west,inner sep=0pt]" - , " (B) at ($ (A.north east) + (0.2,0) $) {" - , " \\small\\textit{buffer is stable}" - , " };" - , " \\node[overlay,anchor=north west,fill=OliveGreen]" - , " (C) at ($ (B.north east) + (0.6,0) $) {};" - , " \\node[overlay,anchor=north west,inner sep=0pt]" - , " (D) at ($ (C.north east) + (0.2,0) $) {" - , " \\small\\textit{buffer is stable and centered}" - , " };" - , if not reframe - then "" - else - unlines - [ " \\node[overlay,anchor=north west,fill=red]" - , " (E) at ($ (A.south west) + (0,-0.2) $) {};" - , " \\node[overlay,anchor=north west,inner sep=0pt]" - , " (F) at ($ (E.north east) + (0.2,0) $) {" - , " \\small\\textit{waiting period (reframing)}" - , " };" - ] - , " \\end{tikzpicture}" - , "\\end{center}" - , "" - , "~" - , "" - , "\\end{document}" - ] - where - formatOffsets = - intercalate "; " - . (fmap (qtyPpm . roundFloatInteger . toPpm)) - - qtyMs ms = "\\qty{" <> show ms <> "}{\\milli\\second}" - qtyPpm ppm = "\\qty{" <> show ppm <> "}{\\ppm}" - - nCyclesOneMs = natToNum @(PeriodToCycles refDom (Milliseconds 1)) - durationMs = duration `div` nCyclesOneMs - stabilityFrameSizeMs = stabilityFrameSize `div` nCyclesOneMs - startupDelaysMs = (`div` nCyclesOneMs) <$> startupDelays - stopAfterStableMs = (`div` nCyclesOneMs) <$> stopAfterStable diff --git a/bittide-experiments/src/Bittide/Simulate/Config.hs b/bittide-experiments/src/Bittide/Simulate/Config.hs deleted file mode 100644 index 324c90545..000000000 --- a/bittide-experiments/src/Bittide/Simulate/Config.hs +++ /dev/null @@ -1,110 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ImplicitPrelude #-} - -module Bittide.Simulate.Config ( - CcConf (..), - simJsonConfigFileName, - simTopologyFileName, - saveCcConfig, -) where - -import Bittide.Arithmetic.PartsPer (PartsPer) -import Bittide.Topology (STop (..), TopologyType (..), toDot) - -import Data.Aeson (FromJSON (..), ToJSON (..), encode) -import Data.ByteString.Lazy qualified as BS (writeFile) -import Data.Default (Default (..)) -import GHC.Generics (Generic) -import Language.Dot.Pretty (render) -import System.Directory (createDirectoryIfMissing) -import System.FilePath (()) - --- | Default name of the clock control JSON configuration file. -simJsonConfigFileName :: String -simJsonConfigFileName = "simulate.json" - --- | Default name of the clock control topology Graphviz file. -simTopologyFileName :: String -simTopologyFileName = "topology.gv" - --- | Collection of all clock control configuration parameters. -data CcConf = CcConf - { ccTopologyType :: TopologyType IO Integer - -- ^ The topology type of the network to be simulated. Have a - -- look at 'Bittide.Topology' for more insights on the supported - -- topology types and their corresponding topologies. - , duration :: Int - -- ^ The number of clock cycles to simulate. - , samples :: Int - -- ^ The number of samples to be utilized for result - -- generation. From the 'duration' many samples available, only - -- every @duration `quot` samples@th sample is used. - , stabilityMargin :: Int - -- ^ Maximum number of elements a buffer occupancy is allowed to - -- deviate to be considered stable. - -- (cf. 'Bittide.ClockControl.StabilityChecker') - , stabilityFrameSize :: Int - -- ^ The minimum number of clock cycles a buffer occupancy must - -- remain within to be considered stable. - -- (cf. 'Bittide.ClockControl.StabilityChecker') - , reframe :: Bool - -- ^ Some flag for enabeling or disabling reframing. - , waitTime :: Int - -- ^ Number of clock cycles to wait until reframing takes place - -- (after stability has been detected, for all elastic buffers). - , stopWhenStable :: Bool - -- ^ Stop simulation as soon as all buffers get stable. - , stopAfterStable :: Maybe Int - -- ^ Stop simulation after all buffers have been stable for - -- at least the given number of clock cycles. - , clockOffsets :: Maybe [PartsPer] - -- ^ The initital clock offsets in Femtoseconds - -- (randomly generated if missing). - , startupDelays :: [Int] - -- ^ The Initital startup offsets, i.e, the number of clock - -- cycles to wait before a node gets started (according to the - -- node's individual clock, randomly generated if missing). - , outDir :: FilePath - -- ^ The directory, in which the generated files are stored. - , stable :: Maybe Bool - -- ^ Stability result of the elastic buffers at the end of - -- simulation, if available. - } - deriving (Show, Ord, Eq, Generic, ToJSON, FromJSON) - -instance Default CcConf where - def = - CcConf - { ccTopologyType = Complete 8 - , duration = 150000 - , samples = 100 - , stabilityMargin = 8 - , stabilityFrameSize = 1500000 - , reframe = True - , waitTime = 100000 - , stopWhenStable = False - , stopAfterStable = Nothing - , clockOffsets = Nothing - , startupDelays = [] - , outDir = "_build" - , stable = Nothing - } - -{- | Saves a topology and a corresponding simulation configuration to -respective files in 'outDir'. --} -saveCcConfig :: STop -> CcConf -> IO () -saveCcConfig (STop t) cfg@CcConf{..} = do - createDirectoryIfMissing True outDir - let topologyFile = outDir simTopologyFileName - writeFile topologyFile $ (<> "\n") $ render $ toDot t - BS.writeFile (outDir simJsonConfigFileName) $ - encode - cfg - { ccTopologyType = case ccTopologyType of - Random{} -> DotFile topologyFile - _ -> ccTopologyType - } diff --git a/bittide-experiments/src/Bittide/Topology.hs b/bittide-experiments/src/Bittide/Topology.hs deleted file mode 100644 index b9a942ed1..000000000 --- a/bittide-experiments/src/Bittide/Topology.hs +++ /dev/null @@ -1,773 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Bittide.Topology ( - -- * Data Types - TopologyType (..), - TopologyName, - Topology ( - topologyName, - topologyGraph, - topologyType, - hasEdge - ), - STop (..), - TreeSize, - - -- * Special Topologies - cyclic, - complete, - diamond, - grid, - star, - torus2d, - torus3d, - tree, - pendulum, - line, - hypercube, - dumbbell, - hourglass, - beads, - - -- * Utilities - fromGraph, - froccTopologyType, - fromDot, - toDot, - randomTopology, - pairwise, -) where - -import Prelude - -import Clash.Prelude ( - Index, - KnownNat, - Nat, - SNat (..), - SomeNat (..), - d0, - d1, - natToInteger, - snatProxy, - snatToInteger, - snatToNum, - someNatVal, - type Div, - type (*), - type (+), - type (-), - type (^), - ) - -import Control.Monad (forM, forM_, replicateM, replicateM_, unless, when) -import Data.Aeson (FromJSON (..), ToJSON (..), Value (..), object, (.:), (.=)) -import Data.Aeson.Types (typeMismatch) -import Data.Array.IO (IOUArray) -import Data.Array.MArray (freeze, getElems, newListArray, readArray, writeArray) -import Data.Bifunctor (bimap, first) -import Data.Bits (Bits (..)) -import Data.Containers.ListUtils (nubOrd) -import Data.Function (on) -import Data.Graph (Graph, buildG, edges, graphFromEdges, scc) -import Data.List (groupBy, sort) -import Data.Maybe (catMaybes, mapMaybe) -import Data.Proxy (Proxy (..)) -import Data.Tuple (swap) -import System.Random (randomIO, randomRIO) - -import Data.Array qualified as A (accumArray, array, listArray, (!)) -import Data.Map.Strict qualified as M (fromList, (!)) -import Data.Set qualified as S (fromList, toList) - -import GHC.Num.Natural (Natural) -import GHC.TypeLits.KnownNat (KnownNat2 (..), SNatKn (..), nameToSymbol) -import GHC.TypeNats (natVal) - -import Language.Dot.Graph (GraphType (..), Name (..), Statement (..), Subgraph (..)) -import Language.Dot.Parser (parse) - --- | Special topologies may have names given as a string. -type TopologyName = String - -{- | A topology is just a simple graph extended with a type level size -bound, a name and a 'TopologyType'. --} -data Topology (n :: Nat) = Topology - { topologyName :: TopologyName - , topologyGraph :: Graph - , topologyType :: TopologyType IO Integer - , hasEdge :: Index n -> Index n -> Bool - } - --- | Existentially quantified version hiding the type level bound. -data STop = forall n. (KnownNat n) => STop (Topology n) - --- | Smart constructor of 'Topology'. -fromGraph :: forall n. (KnownNat n) => TopologyName -> Graph -> Topology n -fromGraph name graph = - Topology name graph (Random $ natToInteger @n) $ - curry $ - (A.!) $ - A.accumArray (const id) False bounds $ - zip (filter (uncurry (/=)) edgeIndices) [True, True ..] - where - bounds = ((minBound, minBound), (maxBound, maxBound)) - edgeIndices = map (bimap fromIntegral fromIntegral) $ edges graph - -{- | Disambiguates between a selection of known topologies, topologies -that are loaded from DOT files, and random topologies. The first -type parameter @m@ indicates a context, that may be required to -generate an instance of the given topology type. If no specific -context is required @m@ is left unspecified. The second type -parameter @n@ indicates an integral type, in which the topology may -be parameterized. --} -data TopologyType m n where - Diamond :: TopologyType m n - Pendulum :: n -> n -> TopologyType m n - Line :: n -> TopologyType m n - HyperCube :: n -> TopologyType m n - Grid :: n -> n -> TopologyType m n - Torus2D :: n -> n -> TopologyType m n - Torus3D :: n -> n -> n -> TopologyType m n - Tree :: n -> n -> TopologyType m n - Star :: n -> TopologyType m n - Cycle :: n -> TopologyType m n - Complete :: n -> TopologyType m n - Dumbbell :: n -> n -> n -> TopologyType m n - Hourglass :: n -> TopologyType m n - Beads :: n -> n -> n -> TopologyType m n - DotFile :: FilePath -> TopologyType IO n - Random :: n -> TopologyType IO n - -instance Show (TopologyType m a) where - show = \case - Diamond{} -> topologyName diamond - Pendulum{} -> topologyName $ pendulum d0 d0 - Line{} -> topologyName $ line d0 - HyperCube{} -> topologyName $ hypercube d0 - Grid{} -> topologyName $ grid d0 d0 - Torus2D{} -> topologyName $ torus2d d0 d0 - Torus3D{} -> topologyName $ torus3d d0 d0 d0 - Tree{} -> topologyName $ tree d0 d0 - Star{} -> topologyName $ star d0 - Cycle{} -> topologyName $ cyclic d0 - Complete{} -> topologyName $ complete d0 - Dumbbell{} -> topologyName $ dumbbell d0 d0 d0 - Hourglass{} -> topologyName $ hourglass d0 - Beads{} -> topologyName $ beads d0 d0 d0 - DotFile{} -> "dotfile" - Random{} -> "random" - --- Unfortunately, we cannot derive 'Eq' and 'Ord' for GADTs. -instance (Eq a) => Eq (TopologyType m a) where - x == y = case (x, y) of - (Diamond, Diamond) -> True - (Pendulum n m, Pendulum n' m') -> n == n' && m == m' - (Line n, Line n') -> n == n' - (HyperCube n, HyperCube n') -> n == n' - (Grid n m, Grid n' m') -> n == n' && m == m' - (Torus2D n m, Torus2D n' m') -> n == n' && m == m' - (Torus3D n m k, Torus3D n' m' k') -> n == n' && m == m' && k == k' - (Tree n m, Tree n' m') -> n == n' && m == m' - (Star n, Star n') -> n == n' - (Cycle n, Cycle n') -> n == n' - (Complete n, Complete n') -> n == n' - (Dumbbell n m k, Dumbbell n' m' k') -> n == n' && m == m' && k == k' - (Hourglass n, Hourglass n') -> n == n' - (Beads n m k, Beads n' m' k') -> n == n' && m == m' && k == k' - (DotFile p, DotFile p') -> p == p' - _ -> False - -instance (Ord a) => Ord (TopologyType m a) where - compare x y = case (x, y) of - (Diamond, Diamond) -> EQ - (Pendulum n m, Pendulum n' m') -> compare (n, m) (n', m') - (Line n, Line n') -> compare n n' - (HyperCube n, HyperCube n') -> compare n n' - (Grid n m, Grid n' m') -> compare (n, m) (n', m') - (Torus2D n m, Torus2D n' m') -> compare (n, m) (n', m') - (Torus3D n m k, Torus3D n' m' k') -> compare (n, m, k) (n', m', k') - (Tree n m, Tree n' m') -> compare (n, m) (n', m') - (Star n, Star n') -> compare n n' - (Cycle n, Cycle n') -> compare n n' - (Complete n, Complete n') -> compare n n' - (Dumbbell n m k, Dumbbell n' m' k') -> compare (n, m, k) (n', m', k') - (Hourglass n, Hourglass n') -> compare n n' - (Beads n m k, Beads n' m' k') -> compare (n, m, k) (n', m', k') - (DotFile p, DotFile p') -> compare p p' - (Random{}, Random{}) -> LT - _ -> compare (ordId x) (ordId y) - where - ordId = \case - Diamond{} -> 0 :: Int - Pendulum{} -> 1 - Line{} -> 2 - HyperCube{} -> 3 - Grid{} -> 4 - Torus2D{} -> 5 - Torus3D{} -> 6 - Tree{} -> 7 - Star{} -> 8 - Cycle{} -> 9 - Complete{} -> 10 - Dumbbell{} -> 11 - Hourglass{} -> 12 - Beads{} -> 13 - DotFile{} -> 14 - Random{} -> 15 - -instance (ToJSON a) => ToJSON (TopologyType m a) where - toJSON t = object $ case t of - Diamond -> [gt] - DotFile f -> [gt, "filepath" .= f] - Line n -> [gt, "nodes" .= n] - HyperCube n -> [gt, "dimensions" .= n] - Star n -> [gt, "nodes" .= n] - Cycle n -> [gt, "nodes" .= n] - Complete n -> [gt, "nodes" .= n] - Hourglass n -> [gt, "nodes" .= n] - Random n -> [gt, "nodes" .= n] - Pendulum l w -> [gt, "length" .= l, "weight" .= w] - Tree d c -> [gt, "depth" .= d, "childs" .= c] - Grid r c -> [gt, "rows" .= r, "cols" .= c] - Torus2D r c -> [gt, "rows" .= r, "cols" .= c] - Dumbbell w l r -> [gt, "width" .= w, "left" .= l, "right" .= r] - Beads c d w -> [gt, "count" .= c, "distance" .= d, "weight" .= w] - Torus3D r c p -> [gt, "rows" .= r, "cols" .= c, "planes" .= p] - where - gt = "graph" .= show t - -instance (FromJSON a) => FromJSON (TopologyType IO a) where - parseJSON v = case v of - Object o -> - o .: "graph" >>= \(name :: String) -> case name of - "diamond" -> return Diamond - "dotfile" -> DotFile <$> o .: "filepath" - "line" -> Line <$> o .: "nodes" - "hypercube" -> HyperCube <$> o .: "dimensions" - "star" -> Star <$> o .: "nodes" - "cycle" -> Cycle <$> o .: "nodes" - "complete" -> Complete <$> o .: "nodes" - "hourglass" -> Hourglass <$> o .: "nodes" - "random" -> Random <$> o .: "nodes" - "pendulum" -> Pendulum <$> o .: "length" <*> o .: "weight" - "tree" -> Tree <$> o .: "depth" <*> o .: "childs" - "grid" -> Grid <$> o .: "rows" <*> o .: "cols" - "torus2d" -> Torus2D <$> o .: "rows" <*> o .: "cols" - "torus3d" -> - Torus3D - <$> o .: "rows" - <*> o .: "cols" - <*> o .: "planes" - "dumbbell" -> - Dumbbell - <$> o .: "width" - <*> o .: "left" - <*> o .: "right" - "beads" -> - Beads - <$> o .: "count" - <*> o .: "distance" - <*> o .: "weight" - _ -> tmm - _ -> tmm - where - tmm = typeMismatch "Topology" v - -newtype FUN a = FUN (forall n. SNat n -> a) - --- | Generates some topology of the given topology type, if possible. -froccTopologyType :: - (Integral n, Applicative m) => - TopologyType m n -> - m (Either String STop) -froccTopologyType tt = case tt of - Diamond -> ret $ Just $ STop diamond - Pendulum n m -> - let pendulum# :: FUN (FUN STop) - pendulum# = FUN (\l@SNat -> FUN (\w@SNat -> STop $ pendulum l w)) - in ret $ pendulum# <#> n m - Line n -> ret $ FUN (\sn@SNat -> STop $ line sn) <#> n - HyperCube n -> ret $ FUN (\sn@SNat -> STop $ hypercube sn) <#> n - Grid rows cols -> - let grid# :: FUN (FUN STop) - grid# = FUN (\n@SNat -> FUN (\m@SNat -> STop $ grid n m)) - in ret $ grid# <#> rows cols - Torus2D rows cols -> - let torus2d# :: FUN (FUN STop) - torus2d# = FUN (\r@SNat -> FUN (\c@SNat -> STop $ torus2d r c)) - in ret $ torus2d# <#> rows cols - Torus3D rows cols planes -> - let torus3d# :: FUN (FUN (FUN STop)) - torus3d# = FUN (\r@SNat -> FUN (\c@SNat -> FUN (\p@SNat -> STop $ torus3d r c p))) - in ret $ torus3d# <#> rows cols planes - Tree depth childs -> - let tree# :: FUN (FUN STop) - tree# = FUN (\n@SNat -> FUN (\m@SNat -> STop $ tree n m)) - in ret $ tree# <#> depth childs - Star n -> ret $ FUN (\sn@SNat -> STop $ star sn) <#> n - Cycle n -> ret $ FUN (\sn@SNat -> STop $ cyclic sn) <#> n - Complete n -> ret $ FUN (\sn@SNat -> STop $ complete sn) <#> n - Dumbbell n m k -> - let dumbbell# :: FUN (FUN (FUN STop)) - dumbbell# = FUN (\w@SNat -> FUN (\l@SNat -> FUN (\r@SNat -> STop $ dumbbell w l r))) - in ret $ dumbbell# <#> n m k - Hourglass n -> ret $ FUN (\sn@SNat -> STop $ hourglass sn) <#> n - Beads n m k -> - let beads# :: FUN (FUN (FUN STop)) - beads# = FUN (\c@SNat -> FUN (\d@SNat -> FUN (\w@SNat -> STop $ beads c d w))) - in ret $ beads# <#> n m k - Random n -> - either (return . Left) (Right <$>) $ - maybeToEither $ - FUN (\sn@SNat -> STop <$> randomTopology sn) <#> n - DotFile f -> - readFile f - >>= return . first (("Invalid DOT file - " <> f <> "\n") <>) . fromDot - where - ret = pure . maybeToEither - maybeToEither = maybe (Left "cannot construct SNat arguments") Right - - infixl 8 - () :: (Integral i) => Maybe (FUN a) -> i -> Maybe a - msnf n = do - snf <- msnf - SomeNat p <- someNatVal $ toInteger n - case snf of - FUN f -> pure (f (snatProxy p)) - - infixl 8 <#> - (<#>) :: (Integral i) => FUN a -> i -> Maybe a - (<#>) f i = Just f i - --- | Given a list of edges, turn it into a directed graph. -fromEdgeList :: forall a. (Ord a) => [(a, a)] -> Graph -fromEdgeList es = dirGraph - where - -- "Data.Graph" deals with directed graphs - allEdges :: [(a, a)] - allEdges = es ++ fmap swap es - adjList :: [(a, [a])] - adjList = g <$> groupBy ((==) `on` fst) (nubOrd $ sort allEdges) - -- now that we have a sorted/grouped list of edges, reformat by attaching - -- a list of all connected nodes to each node. - g :: [(a, b)] -> (a, [b]) - g ps@((x, _) : _) = (x, snd <$> ps) - g [] = error "No edges." - (dirGraph, _, _) = - graphFromEdges ((\(key, keys) -> ((), key, keys)) <$> adjList) - --- | @n@ nodes in a line, with a fully connected blob of @m@ nodes at one end. -pendulum :: SNat l -> SNat w -> Topology (l + w) -pendulum sl sw = - (dumbbell sl d0 sw) - { topologyName = "pendulum" - , topologyType = Pendulum (snatToInteger sl) $ snatToInteger sw - } - -{- | @n@ nodes in a line, connected to their neighbors. - -(differs from the -[mathematical terminology](https://mathworld.wolfram.com/LineGraph.html) but -conforms to callisto) --} -line :: SNat n -> Topology n -line sn = - (dumbbell sn d0 d0) - { topologyName = "line" - , topologyType = Line $ snatToInteger sn - } - --- | @n@-dimensional hypercube -hypercube :: SNat n -> Topology (2 ^ n) -hypercube sn@SNat = - (fromGraph "hypercube" $ fromEdgeList es) - { topologyType = HyperCube $ snatToInteger sn - } - where - n = snatToNum sn - k = (2 :: Int) ^ n - es = - -- see Callisto code (julia): - -- https://github.com/bittide/Callisto.jl/blob/73d908c6cb02b9b953cc104e5b42d432efc42598/src/topology.jl#L224 - [ let j = i .|. (1 `shiftL` b) in (i + 1, j + 1) - | i <- [0 .. (k - 1)] - , b <- [0 .. (n - 1)] - , i .&. (1 `shiftL` b) == 0 - ] - --- | Diamond graph -diamond :: Topology 4 -diamond = - (fromGraph "diamond" $ A.listArray (0, 3) [[1, 3], [0, 2, 3], [1, 3], [0, 1, 2]]) - { topologyType = Diamond - } - --- | Three dimensional torus. -torus3d :: SNat a -> SNat b -> SNat c -> Topology (a * b * c) -torus3d sna@SNat snb@SNat snc@SNat = - (fromGraph "torus3d" $ fromEdgeList dirEdges) - { topologyType = Torus3D a b c - } - where - a = snatToInteger sna - b = snatToInteger snb - c = snatToInteger snc - pairs = [(l, m, n) | l <- [0 .. (a - 1)], m <- [0 .. (b - 1)], n <- [0 .. (c - 1)]] - neighborsOf (l, m, n) = - [ ((l - 1) `mod` a, m, n) - , ((l + 1) `mod` a, m, n) - , (l, (m - 1) `mod` b, n) - , (l, (m + 1) `mod` b, n) - , (l, m, (n - 1) `mod` c) - , (l, m, (n + 1) `mod` c) - ] - dirEdges = concatMap (\p -> fmap (p,) (neighborsOf p)) pairs - --- | See [this figure](https://www.researchgate.net/figure/The-two-dimensional-torus-4x4_fig1_221134153) -torus2d :: SNat rows -> SNat cols -> Topology (rows * cols) -torus2d snRows@SNat snCols@SNat = - (fromGraph "torus2d" $ fromEdgeList dirEdges) - { topologyType = Torus2D rows cols - } - where - rows = snatToInteger snRows - cols = snatToInteger snCols - pairs = [(m, n) | m <- [0 .. (rows - 1)], n <- [0 .. (cols - 1)]] - neighborsOf (m, n) = - [ ((m - 1) `mod` rows, n) - , ((m + 1) `mod` rows, n) - , (m, (n - 1) `mod` cols) - , (m, (n + 1) `mod` cols) - ] - dirEdges = concatMap (\p -> fmap (p,) (neighborsOf p)) pairs - --- | [Grid graph](https://mathworld.wolfram.com/GridGraph.html) -grid :: SNat rows -> SNat cols -> Topology (rows * cols) -grid snRows@SNat snCols@SNat = - (fromGraph "grid" $ fromEdgeList dirEdges) - { topologyType = Grid rows cols - } - where - rows = snatToInteger snRows - cols = snatToInteger snCols - pairs = [(m, n) | m <- [1 .. rows], n <- [1 .. cols]] - mkEdges (m, n) = - [ (a, b) - | a <- [(m - 1) .. (m + 1)] - , b <- [(n - 1) .. (n + 1)] - , a /= m || b /= n - , a == m || b == n - , a > 0 - , b > 0 - , a <= rows - , b <= cols - ] - dirEdges = concatMap (\p -> fmap (p,) (mkEdges p)) pairs - --- | Type family for calculating the size of a tree. -type family TreeSize (depth :: Nat) (children :: Nat) where - TreeSize depth 0 = 1 - TreeSize depth 1 = depth + 1 - TreeSize depth children = - Div ((children ^ (depth + 1)) - 1) (children - 1) - -instance (KnownNat n, KnownNat m) => KnownNat2 $(nameToSymbol ''TreeSize) n m where - natSing2 = - let - x = natVal (Proxy @n) - y = natVal (Proxy @m) - z = treeSize x y - in - SNatKn z - where - treeSize :: Natural -> Natural -> Natural - treeSize d = \case - 0 -> 1 - 1 -> d + 1 - c -> ((c ^ (d + 1)) - 1) `div` (c - 1) - {-# INLINE natSing2 #-} - --- | Tree of depth @depth@ with @childs@ children -tree :: SNat depth -> SNat childs -> Topology (TreeSize depth childs) -tree snDepth@SNat snChilds@SNat = - (fromGraph "tree" treeGraph) - { topologyType = Tree depth c - } - where - depth = snatToInteger snDepth - c = snatToInteger snChilds - -- \| At depth @d_i@, child node @i@ is connected to the @(i-1) `div` c + 1@st - -- node at depth @d_i - 1@ - pairs = [(d_i, i, (i - 1) `div` c + 1) | d_i <- [0 .. depth], i <- [1 .. (c ^ d_i)]] - mkEdges (0, _, _) = Nothing - mkEdges (lvl, node, p_node) = Just ((lvl, node), (lvl - 1, p_node)) - directedEdges = mapMaybe mkEdges pairs - treeGraph = fromEdgeList directedEdges - --- | [Star graph](https://mathworld.wolfram.com/StarGraph.html) -star :: SNat childs -> Topology (TreeSize 1 childs) -star sn = - (tree SNat sn) - { topologyName = "star" - , topologyType = Star $ snatToInteger sn - } - -{- | [Cyclic graph](https://mathworld.wolfram.com/CycleGraph.html) with @n@ -vertices. --} -cyclic :: SNat n -> Topology n -cyclic sn = - (beads sn d0 d1) - { topologyName = "cycle" - , topologyType = Cycle $ snatToInteger sn - } - -{- | [Complete graph](https://mathworld.wolfram.com/CompleteGraph.html) with @n@ -vertices. --} -complete :: SNat n -> Topology n -complete sn = - (beads d1 d0 sn) - { topologyName = "complete" - , topologyType = Complete $ snatToInteger sn - } - -{- | An dumbbell shaped graph consisting of two independent complete -sub-graphs of size @l@ and @r@, connected via a chain of @w@ nodes -in between two distinct nodes of each sub-graph. --} -dumbbell :: SNat w -> SNat l -> SNat r -> Topology (w + l + r) -dumbbell sw@SNat sl@SNat sr@SNat = - t{topologyType = Dumbbell (sn2i sw) (sn2i sl) (sn2i sr)} - where - sn2i = snatToInteger - w = snatToNum sw - l = snatToNum sl - r = snatToNum sr - m = w + l + r - 1 - - t = - fromGraph "dumbbell" $ - if w + l + r == 0 - then A.array (0, -1) [] - else A.array (0, m) $ fmap (\i -> (i, neighbours i)) [0 .. m] - - neighbours i - | i < l = - (if i == l - 1 && w + r > 0 then (l :) else id) - [j | j <- [0 .. l - 1], j /= i] - | i >= w + l = - (if i == w + l && w + l > 0 then ((w + l - 1) :) else id) - [j + w + l | j <- [0 .. r - 1], j + w + l /= i] - | otherwise = - (if l > 0 || i > l then ((i - 1) :) else id) $ - (if r > 0 || i < l + w - 1 then ((i + 1) :) else id) - [] - -{- | An hourglass shaped graph consisting of two independent complete -sub-graphs, only connected via a single edge between two distinct -nodes of each sub-graph. --} -hourglass :: SNat n -> Topology (n + n) -hourglass sn = - (dumbbell d0 sn sn) - { topologyName = "hourglass" - , topologyType = Hourglass $ snatToInteger sn - } - -{- | A beads shaped graph consisting of two @c@ independend complete -subgraphs of size @w@ (representing the beads), connected via a -closed circular chain of @d@ nodes in between (representing the -thread). --} -beads :: SNat c -> SNat d -> SNat w -> Topology (c * (d + w)) -beads sc@SNat sd@SNat sw@SNat = - (fromGraph "beads" $ fromEdgeList es) - { topologyType = Beads (sn2i sc) (sn2i sd) (sn2i sw) - } - where - sn2i = snatToInteger - c = snatToNum sc :: Int - d = snatToNum sd :: Int - w = snatToNum sw :: Int - s = c * (d + w) - es = - [ (x + i, x + j) - | n <- [0 .. c - 1] - , i <- [0 .. w - 1] - , j <- [0 .. w - 1] - , i /= j - , let x = n * (d + w) - ] - <> [ (x', x) - | w > 0 - , n <- [0 .. c - 1] - , let x' = n * (d + w) - x = (x' - 1) `mod` s - , x /= x' - ] - <> [ (x', x) - | n <- [0 .. c - 1] - , i <- [0 .. d - 1] - , let x' = n * (d + w) + w + i - x = (x' - 1) `mod` s - , x /= x' - ] - --- | Generates a random topology of the given size. -randomTopology :: SNat n -> IO (Topology n) -randomTopology sn@SNat = do - let - n = snatToNum sn - is = [0, 1 .. n - 1] - - -- get some random vertex permuation for ensuring connectivity - aP <- newListArray (0, n - 1) is - replicateM_ (10 * n) $ do - p1 <- randomRIO (0, n - 1) - p2 <- randomRIO (0, n - 1) - v1 <- readArray aP p1 - v2 <- readArray aP p2 - writeArray aP p1 v2 - writeArray aP p2 v1 - - randomPermutation <- getElems (aP :: IOUArray Int Int) - - -- get some random edge availability matrix for self-loop-free, - -- undirected graphs - aE <- replicateM (n * n) randomIO >>= newListArray ((0, 0), (n - 1, n - 1)) - forM_ is $ \i -> - writeArray aE (i, i) False - forM_ [(i, j) | i <- is, j <- is, i /= j] $ \(i, j) -> do - x1 <- readArray aE (i, j) - x2 <- readArray aE (j, i) - when (x1 /= x2) $ do - writeArray aE (i, j) False - writeArray aE (j, i) False - - -- ensure connectivity - forM_ (pairwise randomPermutation) $ \(i, j) -> do - writeArray aE (i, j) True - writeArray aE (j, i) True - - available <- freeze (aE :: IOUArray (Int, Int) Bool) - - -- create the graph - return $ - fromGraph "random" $ - buildG - (0, n - 1) - [ (i, j) - | i <- is - , j <- is - , available A.! (i, j) - ] - -{- | Turns a topology into a graphviz DOT structure, as it is required by -the [happy-dot](https://hackage.haskell.org/package/happy-dot) library. --} -toDot :: - (KnownNat n) => - -- | topology to be turned into graphviz dot - Topology n -> - -- | the result, as it is needed by happy-dot - (Bool, GraphType, Maybe Name, [Statement]) -toDot t = - ( True - , Graph - , Just $ StringID $ topologyName t - , map asEdgeStatement $ edges $ topologyGraph t - ) - where - asEdgeStatement (x, y) = - EdgeStatement - (map (\i -> NodeRef (XMLID ('n' : show i)) Nothing) [x, y]) - [] - -{- | Reads a topology from a DOT file. Only the name and structure of the -graph is used, i.e., any additional graphviz dot specific -annotations are ignored. --} -fromDot :: - -- | the string holding the graphviz dot content - String -> - -- | either an error, if given an unusable input, or the extracted topology - Either String STop -fromDot cnt = do - (strict, gType, maybe "" fromDotName -> name, statements) <- parse cnt - - unless strict $ Left "Graph must be strict" - when (gType == Digraph) $ Left "Graph must be undirected" - - edgeChains <- catMaybes <$> mapM fromStatement statements - - let - namedEdges = concatMap (pairwise . map asString) edgeChains - edgeNames = dedupAndSort $ concatMap (\(x, y) -> [x, y]) namedEdges - n = length edgeNames - idx = (M.!) $ M.fromList $ zip edgeNames [0, 1 ..] - graph = - buildG (0, n - 1) - -- Self loops are removed at this point as they are redundant in - -- terms of the simulated topology. In terms of connectivity, a - -- node can synchronize its clock with itself even without a - -- elastic buffer in between. Therefore, we all self-loops in - -- the input, but remove them here, since they don't have any - -- effect on the topology that is created out of the graph. - $ - filter (uncurry (/=)) - -- remove duplicate edges and sort for pretty printing - $ - dedupAndSort $ - concatMap - (\(x, y) -> [(idx x, idx y), (idx y, idx x)]) - namedEdges - - when (length (scc graph) > 1) $ Left "Graph must be strongly connected" - - case someNatVal $ toInteger n of - Nothing -> Left "cannot construct SNat arguments" - Just (SomeNat (_ :: Proxy n)) -> - return $ STop $ fromGraph @n name graph - where - fromStatement :: Statement -> Either String (Maybe [Name]) - fromStatement = \case - EdgeStatement xs _ -> fmap Just $ forM xs $ \case - NodeRef n _ -> return n - Subgraph{} -> Left "Subgraphs are not supported" - -- we are only interested in the edges, everthing else can be - -- ignored - _ -> pure Nothing - - dedupAndSort :: (Ord a) => [a] -> [a] - dedupAndSort = S.toList . S.fromList - - asString = \case - StringID x -> 's' : x - XMLID x -> 'x' : x - - fromDotName = \case - StringID x -> x - XMLID x -> x - -{- | Successive overlapping pairs. - ->>> pairwise [1, 2, 3, 4] -[(1,2),(2,3),(3,4)] ->>> pairwise [] -[] --} -pairwise :: [a] -> [(a, a)] -pairwise as = zip as (tail as) diff --git a/bittide-experiments/tests/doctests.hs b/bittide-experiments/tests/doctests.hs deleted file mode 100644 index c1b610a22..000000000 --- a/bittide-experiments/tests/doctests.hs +++ /dev/null @@ -1,14 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main (main) where - -import System.Environment (getArgs) -import Test.DocTest (mainFromCabal) - -main :: IO () -main = do - -- We use Nix to setup tooling, not to provide GHC packages so we need to set --no-nix - args <- getArgs - mainFromCabal "bittide-experiments" ("--no-nix" : args) diff --git a/bittide-experiments/tests/unittests.hs b/bittide-experiments/tests/unittests.hs deleted file mode 100644 index 27c2219f0..000000000 --- a/bittide-experiments/tests/unittests.hs +++ /dev/null @@ -1,17 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import Test.Tasty -import Prelude - -tests :: TestTree -tests = - testGroup - "Tests" - [] - -main :: IO () -main = defaultMain tests diff --git a/bittide-extra/LICENSE b/bittide-extra/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/bittide-extra/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/bittide-extra/bittide-extra.cabal b/bittide-extra/bittide-extra.cabal deleted file mode 100644 index f86a457a2..000000000 --- a/bittide-extra/bittide-extra.cabal +++ /dev/null @@ -1,128 +0,0 @@ -cabal-version: 2.4 -name: bittide-extra -version: 0.1 -license: Apache-2.0 -license-file: LICENSE -author: QBayLogic B.V. -maintainer: devops@qbaylogic.com -copyright: Copyright © 2022-2024 Google LLC - -common common-options - default-extensions: - -- TemplateHaskell is used to support convenience functions such as - -- 'listToVecTH' and 'bLit'. - -- - -- `NoImplicitPrelude` is used because Clash offers Clash.Prelude - BangPatterns - BinaryLiterals - ConstraintKinds - DataKinds - DefaultSignatures - DeriveAnyClass - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveLift - DeriveTraversable - DerivingStrategies - InstanceSigs - KindSignatures - LambdaCase - NoImplicitPrelude - NoStarIsType - PolyKinds - QuasiQuotes - RankNTypes - ScopedTypeVariables - StandaloneDeriving - TemplateHaskell - TupleSections - TypeApplications - TypeFamilies - TypeOperators - ViewPatterns - - ghc-options: - -- Plugins to support type-level constraint solving on naturals: - -- - GHC.TypeLits.Extra.Solver - -- - GHC.TypeLits.Normalise - -- - GHC.TypeLits.KnownNat.Solver - -- Clash needs access to the source code in compiled modules: - -- -fexpose-all-unfoldings - -- Worker wrappers introduce unstable names for functions that might have - -- blackboxes attached for them. You can disable this, but be sure to add - -- a no-specialize pragma to every function with a blackbox. - -- -fno-worker-wrapper - -Wall - -Wcompat - -fplugin=GHC.TypeLits.Extra.Solver - -fplugin=GHC.TypeLits.Normalise - -fplugin=GHC.TypeLits.KnownNat.Solver - -fexpose-all-unfoldings - -fno-worker-wrapper - - default-language: Haskell2010 - build-depends: - -- clash-prelude will set suitable version bounds for the plugins - Cabal, - array, - base, - clash-prelude >=1.6.3 && <1.10, - clash-protocols, - containers >=0.4.0 && <0.7, - ghc-typelits-extra, - ghc-typelits-knownnat, - ghc-typelits-natnormalise, - template-haskell, - -library - import: common-options - hs-source-dirs: src - exposed-modules: - Bittide.Extra.Maybe - Bittide.Extra.Wishbone - Clash.Explicit.Signal.Extra - Clash.Sized.Vector.Extra - Numeric.Extra - -test-suite unittests - import: common-options - type: exitcode-stdio-1.0 - main-is: Main.hs - hs-source-dirs: tests/unittests - default-language: Haskell2010 - ghc-options: - -Wall - -Wcompat - -threaded - - hs-source-dirs: tests/doctests - other-modules: - Tests.Numeric.Extra - - build-depends: - base, - bittide-extra, - clash-prelude, - clash-prelude-hedgehog, - hedgehog, - tasty, - tasty-hedgehog, - tasty-th, - -test-suite doctests - type: exitcode-stdio-1.0 - default-language: Haskell2010 - main-is: Main.hs - ghc-options: - -Wall - -Wcompat - -threaded - - hs-source-dirs: tests/doctests - build-depends: - base, - bittide-extra, - doctest-parallel >=0.3.0.1 && <0.4, - filepath, diff --git a/bittide-extra/bittide-extra.cabal.license b/bittide-extra/bittide-extra.cabal.license deleted file mode 100644 index 848612f0e..000000000 --- a/bittide-extra/bittide-extra.cabal.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/bittide-extra/src/Bittide/Extra/Maybe.hs b/bittide-extra/src/Bittide/Extra/Maybe.hs deleted file mode 100644 index 8f2624b48..000000000 --- a/bittide-extra/src/Bittide/Extra/Maybe.hs +++ /dev/null @@ -1,48 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -module Bittide.Extra.Maybe where - -import Clash.Prelude - -import Data.Maybe - -{- $setup ->>> import Clash.Prelude --} - -{- | Extract `Just` from a `Vec` of `Maybe`s. Prefer left-most `Just`. If no `Just` is -found, use a default value. - ->>> fromMaybesL 0 (Just 1 :> Just 2 :> Nothing :> Nil) -1 ->>> fromMaybesL 0 (Nothing :> Nothing :> Nothing :> Nil) -0 --} -fromMaybesL :: a -> Vec n (Maybe a) -> a -fromMaybesL a = fromMaybe a . fold (<|>) . (Nothing :>) - -{- | Extract `Just` from a `Vec` of `Maybe`s. Prefer right-most `Just`. If no `Just` is -found, use a default value. - ->>> fromMaybesR 0 (Just 1 :> Just 2 :> Nothing :> Nil) -2 ->>> fromMaybesR 0 (Nothing :> Nothing :> Nothing :> Nil) -0 --} -fromMaybesR :: a -> Vec n (Maybe a) -> a -fromMaybesR a = fromMaybe a . fold (flip (<|>)) . (Nothing :>) - -{- | Returns 'Just a' when the boolean is 'True', or 'Nothing' when 'False'. - -* Examples: - - >>> orNothing True 5 - Just 5 - - >>> orNothing False "Hello" - Nothing --} -orNothing :: Bool -> a -> Maybe a -orNothing True a = Just a -orNothing False _ = Nothing diff --git a/bittide-extra/src/Bittide/Extra/Wishbone.hs b/bittide-extra/src/Bittide/Extra/Wishbone.hs deleted file mode 100644 index 11a6ac386..000000000 --- a/bittide-extra/src/Bittide/Extra/Wishbone.hs +++ /dev/null @@ -1,120 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE PatternSynonyms #-} - -{- | -See: http://cdn.opencores.org/downloads/wbspec_b4.pdf --} -module Bittide.Extra.Wishbone where - -import Clash.Prelude -import Clash.Signal.Internal - -import Protocols -import Protocols.Wishbone - -import qualified Data.IntMap as I - -type DWord = BitVector (4 * 8) - -{- | The wishbone storage is a simulation only memory element that communicates via the -Wishbone protocol : http://cdn.opencores.org/downloads/wbspec_b4.pdf . -It receives a name for error identification, an Intmap of BitVector 8 as initial content. -The storage is byte addressable. --} -wishboneStorage :: - String -> - I.IntMap (BitVector 8) -> - Circuit (Wishbone dom 'Standard 32 DWord) () -wishboneStorage name initial = - Circuit $ \(input, ()) -> (wishboneStorage' name state input, ()) - where - state = (initial, False) - -wishboneStorage' :: - String -> - (I.IntMap (BitVector 8), Bool) -> - Signal dom (WishboneM2S 32 (BitSize DWord `DivRU` 8) DWord) -> - Signal dom (WishboneS2M DWord) -wishboneStorage' name state inputs = dataOut :- (wishboneStorage' name state' inputs') - where - input :- inputs' = inputs - state' = (file', ack') - (file, ack) = state - WishboneM2S - { addr - , writeData - , busSelect - , busCycle - , strobe - , writeEnable - } = input - file' - | writeEnable = I.fromList assocList <> file - | otherwise = file - ack' = busCycle && strobe - address = fromIntegral (unpack $ addr :: Unsigned 32) - readData = - if not writeEnable - then - (file `lookup'` (address + 3)) - ++# (file `lookup'` (address + 2)) - ++# (file `lookup'` (address + 1)) - ++# (file `lookup'` address) - else 0 - lookup' x addr' = - I.findWithDefault - (error $ name <> ": Uninitialized Memory Address = " <> show addr') - addr' - x - assocList = case busSelect of - $(bitPattern "0001") -> [byte0] - $(bitPattern "0010") -> [byte1] - $(bitPattern "0100") -> [byte2] - $(bitPattern "1000") -> [byte3] - $(bitPattern "0011") -> half0 - $(bitPattern "1100") -> half1 - _ -> word0 - byte0 = (address, slice d7 d0 writeData) - byte1 = (address + 1, slice d15 d8 writeData) - byte2 = (address + 2, slice d23 d16 writeData) - byte3 = (address + 3, slice d31 d24 writeData) - half0 = [byte0, byte1] - half1 = [byte2, byte3] - word0 = [byte0, byte1, byte2, byte3] - dataOut = - (emptyWishboneS2M @DWord) - { readData = readData - , acknowledge = ack - , err = False - } - -{- | Wrapper for the wishboneStorage that allows two ports to be connected. -Port A can only be used for reading, port B can read and write to the te storage. -Writing from port A is illegal and write attempts will set the err signal. --} -instructionStorage :: - String -> - I.IntMap (BitVector 8) -> - Circuit (Wishbone dom 'Standard 32 DWord, Wishbone dom 'Standard 32 DWord) () -instructionStorage name initial = Circuit go - where - go ((aM2S, bM2S), ()) = ((aS2M, bS2M), ()) - where - Circuit storageFn = wishboneStorage name initial - (storageOut, ()) = storageFn (storageIn, ()) - aActive = strobe <$> aM2S .&&. busCycle <$> aM2S - bActive = strobe <$> bM2S .&&. busCycle <$> bM2S - aWriting = aActive .&&. writeEnable <$> aM2S - - storageIn = mux (not <$> bActive) (noWrite <$> aM2S) bM2S - - aS2M = mux (not <$> bActive) (writeIsErr <$> storageOut <*> aWriting) (noAck <$> storageOut) - bS2M = storageOut - - noAck wb = wb{acknowledge = False, err = False} - noWrite wb = wb{writeEnable = False} - writeIsErr wb write = wb{err = err wb || write} diff --git a/bittide-extra/src/Clash/Explicit/Signal/Extra.hs b/bittide-extra/src/Clash/Explicit/Signal/Extra.hs deleted file mode 100644 index 232de3fec..000000000 --- a/bittide-extra/src/Clash/Explicit/Signal/Extra.hs +++ /dev/null @@ -1,19 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Clash.Explicit.Signal.Extra where - -import Clash.Explicit.Prelude - --- | Give a pulse whenever the signal changes. -changepoints :: - forall dom a. - (KnownDomain dom, NFDataX a, Eq a) => - Clock dom -> - Reset dom -> - Enable dom -> - Signal dom a -> - Signal dom Bool -changepoints clk rst ena = - mealy clk rst ena (\s i -> (Just i, maybe False (/= i) s)) Nothing diff --git a/bittide-extra/src/Clash/Sized/Vector/Extra.hs b/bittide-extra/src/Clash/Sized/Vector/Extra.hs deleted file mode 100644 index bb2803a46..000000000 --- a/bittide-extra/src/Clash/Sized/Vector/Extra.hs +++ /dev/null @@ -1,3171 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Clash.Sized.Vector.Extra where - -import Clash.Explicit.Prelude -import Data.Maybe (fromMaybe) - -{- | Finds first element in given vector matching the predicate. Returns -'Nothing' if no element satisfied the predicate. --} -find :: (KnownNat n) => (a -> Bool) -> Vec n a -> Maybe a -find f = foldl (<|>) Nothing . map go - where - go a - | f a = Just a - | otherwise = Nothing - -{- | Finds first element in given vector matching the predicate. Returns a -default element (the first argument) if no element satisfied the predicate. --} -findWithDefault :: (KnownNat n) => a -> (a -> Bool) -> Vec n a -> a -findWithDefault a f = fromMaybe a . find f - --- XXX: We need a bunch of zip functions due to an unfortunate coinciding bugs: --- --- https://github.com/clash-lang/clash-compiler/issues/2723 --- https://github.com/clash-lang/clash-compiler/issues/2722 --- - --- | Like 'zip', but for 8 vectors -zip8 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7) -zip8 = zipWith8 (,,,,,,,) -{-# INLINE zip8 #-} - --- | Like 'zipWith', but for 8 vectors -zipWith8 :: - (a0 -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> a8) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -zipWith8 f a0s a1s a2s a3s a4s a5s a6s a7s = - zipWith - (\a0 (a1, a2, a3, a4, a5, a6, a7) -> f a0 a1 a2 a3 a4 a5 a6 a7) - a0s - (zip7 a1s a2s a3s a4s a5s a6s a7s) -{-# INLINE zipWith8 #-} - --- | Like 'zip', but for 9 vectors -zip9 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8) -zip9 = zipWith9 (,,,,,,,,) -{-# INLINE zip9 #-} - --- | Like 'zipWith', but for 9 vectors -zipWith9 :: - (a0 -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> a8 -> a9) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -zipWith9 f a0s a1s a2s a3s a4s a5s a6s a7s a8s = - zipWith - (\a0 (a1, a2, a3, a4, a5, a6, a7, a8) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8) - a0s - (zip8 a1s a2s a3s a4s a5s a6s a7s a8s) -{-# INLINE zipWith9 #-} - --- | Like 'zip', but for 10 vectors -zip10 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) -zip10 = zipWith10 (,,,,,,,,,) -{-# INLINE zip10 #-} - --- | Like 'zipWith', but for 10 vectors -zipWith10 :: - (a0 -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> a8 -> a9 -> a10) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -zipWith10 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s = - zipWith - (\a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9) - a0s - (zip9 a1s a2s a3s a4s a5s a6s a7s a8s a9s) -{-# INLINE zipWith10 #-} - --- | Like 'zip', but for 11 vectors -zip11 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) -zip11 = zipWith11 (,,,,,,,,,,) -{-# INLINE zip11 #-} - --- | Like 'zipWith', but for 11 vectors -zipWith11 :: - (a0 -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> a8 -> a9 -> a10 -> a11) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -zipWith11 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s = - zipWith - (\a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10) - a0s - (zip10 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s) -{-# INLINE zipWith11 #-} - --- | Like 'zip', but for 12 vectors -zip12 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) -zip12 = zipWith12 (,,,,,,,,,,,) -{-# INLINE zip12 #-} - --- | Like 'zipWith', but for 12 vectors -zipWith12 :: - (a0 -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> a8 -> a9 -> a10 -> a11 -> a12) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -zipWith12 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 - ) - a0s - (zip11 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s) -{-# INLINE zipWith12 #-} - --- | Like 'zip', but for 13 vectors -zip13 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) -zip13 = zipWith13 (,,,,,,,,,,,,) -{-# INLINE zip13 #-} - --- | Like 'zipWith', but for 13 vectors -zipWith13 :: - (a0 -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> a8 -> a9 -> a10 -> a11 -> a12 -> a13) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -zipWith13 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 - ) - a0s - (zip12 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s) -{-# INLINE zipWith13 #-} - --- | Like 'zip', but for 14 vectors -zip14 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) -zip14 = zipWith14 (,,,,,,,,,,,,,) -{-# INLINE zip14 #-} - --- | Like 'zipWith', but for 14 vectors -zipWith14 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -zipWith14 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 - ) - a0s - (zip13 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s) -{-# INLINE zipWith14 #-} - --- | Like 'zip', but for 15 vectors -zip15 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) -zip15 = zipWith15 (,,,,,,,,,,,,,,) -{-# INLINE zip15 #-} - --- | Like 'zipWith', but for 15 vectors -zipWith15 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -zipWith15 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 - ) - a0s - (zip14 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s) -{-# INLINE zipWith15 #-} - --- | Like 'zip', but for 16 vectors -zip16 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) -zip16 = zipWith16 (,,,,,,,,,,,,,,,) -{-# INLINE zip16 #-} - --- | Like 'zipWith', but for 16 vectors -zipWith16 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -zipWith16 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 - ) - a0s - (zip15 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s) -{-# INLINE zipWith16 #-} - --- | Like 'zip', but for 17 vectors -zip17 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) -zip17 = zipWith17 (,,,,,,,,,,,,,,,,) -{-# INLINE zip17 #-} - --- | Like 'zipWith', but for 17 vectors -zipWith17 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -zipWith17 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 - ) - a0s - (zip16 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s) -{-# INLINE zipWith17 #-} - --- | Like 'zip', but for 18 vectors -zip18 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) -zip18 = zipWith18 (,,,,,,,,,,,,,,,,,) -{-# INLINE zip18 #-} - --- | Like 'zipWith', but for 18 vectors -zipWith18 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -zipWith18 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 - ) - a0s - (zip17 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s) -{-# INLINE zipWith18 #-} - --- | Like 'zip', but for 19 vectors -zip19 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec - n - (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) -zip19 = zipWith19 (,,,,,,,,,,,,,,,,,,) -{-# INLINE zip19 #-} - --- | Like 'zipWith', but for 19 vectors -zipWith19 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -zipWith19 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 - ) - a0s - (zip18 a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s) -{-# INLINE zipWith19 #-} - --- | Like 'zip', but for 20 vectors -zip20 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec - n - (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) -zip20 = zipWith20 (,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip20 #-} - --- | Like 'zipWith', but for 20 vectors -zipWith20 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -zipWith20 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s = - zipWith - ( \a0 (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 - ) - a0s - ( zip19 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - ) -{-# INLINE zipWith20 #-} - --- | Like 'zip', but for 21 vectors -zip21 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - ) -zip21 = zipWith21 (,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip21 #-} - --- | Like 'zipWith', but for 21 vectors -zipWith21 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -zipWith21 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - ) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 - ) - a0s - ( zip20 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - ) -{-# INLINE zipWith21 #-} - --- | Like 'zip', but for 22 vectors -zip22 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - ) -zip22 = zipWith22 (,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip22 #-} - --- | Like 'zipWith', but for 22 vectors -zipWith22 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -zipWith22 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - ) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 a21 - ) - a0s - ( zip21 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - ) -{-# INLINE zipWith22 #-} - --- | Like 'zip', but for 23 vectors -zip23 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - ) -zip23 = zipWith23 (,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip23 #-} - --- | Like 'zipWith', but for 23 vectors -zipWith23 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -zipWith23 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - ) -> f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 a21 a22 - ) - a0s - ( zip22 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - ) -{-# INLINE zipWith23 #-} - --- | Like 'zip', but for 24 vectors -zip24 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - ) -zip24 = zipWith24 (,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip24 #-} - --- | Like 'zipWith', but for 24 vectors -zipWith24 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -zipWith24 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - ) -> - f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 a21 a22 a23 - ) - a0s - ( zip23 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - ) -{-# INLINE zipWith24 #-} - --- | Like 'zip', but for 25 vectors -zip25 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - ) -zip25 = zipWith25 (,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip25 #-} - --- | Like 'zipWith', but for 25 vectors -zipWith25 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -zipWith25 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - ) - a0s - ( zip24 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - ) -{-# INLINE zipWith25 #-} - --- | Like 'zip', but for 26 vectors -zip26 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - ) -zip26 = zipWith26 (,,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip26 #-} - --- | Like 'zipWith', but for 26 vectors -zipWith26 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 -> - a26 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -zipWith26 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s a25s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - a25 - ) - a0s - ( zip25 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - a25s - ) -{-# INLINE zipWith26 #-} - --- | Like 'zip', but for 27 vectors -zip27 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - ) -zip27 = zipWith27 (,,,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip27 #-} - --- | Like 'zipWith', but for 27 vectors -zipWith27 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 -> - a26 -> - a27 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -zipWith27 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s a25s a26s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - a25 - a26 - ) - a0s - ( zip26 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - a25s - a26s - ) -{-# INLINE zipWith27 #-} - --- | Like 'zip', but for 28 vectors -zip28 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - ) -zip28 = zipWith28 (,,,,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip28 #-} - --- | Like 'zipWith', but for 28 vectors -zipWith28 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 -> - a26 -> - a27 -> - a28 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -zipWith28 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s a25s a26s a27s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - a25 - a26 - a27 - ) - a0s - ( zip27 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - a25s - a26s - a27s - ) -{-# INLINE zipWith28 #-} - --- | Like 'zip', but for 29 vectors -zip29 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - ) -zip29 = zipWith29 (,,,,,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip29 #-} - --- | Like 'zipWith', but for 29 vectors -zipWith29 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 -> - a26 -> - a27 -> - a28 -> - a29 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec n a29 -zipWith29 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s a25s a26s a27s a28s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - a25 - a26 - a27 - a28 - ) - a0s - ( zip28 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - a25s - a26s - a27s - a28s - ) -{-# INLINE zipWith29 #-} - --- | Like 'zip', but for 30 vectors -zip30 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec n a29 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - , a29 - ) -zip30 = zipWith30 (,,,,,,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip30 #-} - --- | Like 'zipWith', but for 30 vectors -zipWith30 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 -> - a26 -> - a27 -> - a28 -> - a29 -> - a30 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec n a29 -> - Vec n a30 -zipWith30 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s a25s a26s a27s a28s a29s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - , a29 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - a25 - a26 - a27 - a28 - a29 - ) - a0s - ( zip29 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - a25s - a26s - a27s - a28s - a29s - ) -{-# INLINE zipWith30 #-} - --- | Like 'zip', but for 31 vectors -zip31 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec n a29 -> - Vec n a30 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - , a29 - , a30 - ) -zip31 = zipWith31 (,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip31 #-} - --- | Like 'zipWith', but for 31 vectors -zipWith31 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 -> - a26 -> - a27 -> - a28 -> - a29 -> - a30 -> - a31 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec n a29 -> - Vec n a30 -> - Vec n a31 -zipWith31 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s a25s a26s a27s a28s a29s a30s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - , a29 - , a30 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - a25 - a26 - a27 - a28 - a29 - a30 - ) - a0s - ( zip30 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - a25s - a26s - a27s - a28s - a29s - a30s - ) -{-# INLINE zipWith31 #-} - --- | Like 'zip', but for 32 vectors -zip32 :: - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec n a29 -> - Vec n a30 -> - Vec n a31 -> - Vec - n - ( a0 - , a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - , a29 - , a30 - , a31 - ) -zip32 = zipWith32 (,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,) -{-# INLINE zip32 #-} - --- | Like 'zipWith', but for 32 vectors -zipWith32 :: - ( a0 -> - a1 -> - a2 -> - a3 -> - a4 -> - a5 -> - a6 -> - a7 -> - a8 -> - a9 -> - a10 -> - a11 -> - a12 -> - a13 -> - a14 -> - a15 -> - a16 -> - a17 -> - a18 -> - a19 -> - a20 -> - a21 -> - a22 -> - a23 -> - a24 -> - a25 -> - a26 -> - a27 -> - a28 -> - a29 -> - a30 -> - a31 -> - a32 - ) -> - Vec n a0 -> - Vec n a1 -> - Vec n a2 -> - Vec n a3 -> - Vec n a4 -> - Vec n a5 -> - Vec n a6 -> - Vec n a7 -> - Vec n a8 -> - Vec n a9 -> - Vec n a10 -> - Vec n a11 -> - Vec n a12 -> - Vec n a13 -> - Vec n a14 -> - Vec n a15 -> - Vec n a16 -> - Vec n a17 -> - Vec n a18 -> - Vec n a19 -> - Vec n a20 -> - Vec n a21 -> - Vec n a22 -> - Vec n a23 -> - Vec n a24 -> - Vec n a25 -> - Vec n a26 -> - Vec n a27 -> - Vec n a28 -> - Vec n a29 -> - Vec n a30 -> - Vec n a31 -> - Vec n a32 -zipWith32 f a0s a1s a2s a3s a4s a5s a6s a7s a8s a9s a10s a11s a12s a13s a14s a15s a16s a17s a18s a19s a20s a21s a22s a23s a24s a25s a26s a27s a28s a29s a30s a31s = - zipWith - ( \a0 - ( a1 - , a2 - , a3 - , a4 - , a5 - , a6 - , a7 - , a8 - , a9 - , a10 - , a11 - , a12 - , a13 - , a14 - , a15 - , a16 - , a17 - , a18 - , a19 - , a20 - , a21 - , a22 - , a23 - , a24 - , a25 - , a26 - , a27 - , a28 - , a29 - , a30 - , a31 - ) -> - f - a0 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 - a20 - a21 - a22 - a23 - a24 - a25 - a26 - a27 - a28 - a29 - a30 - a31 - ) - a0s - ( zip31 - a1s - a2s - a3s - a4s - a5s - a6s - a7s - a8s - a9s - a10s - a11s - a12s - a13s - a14s - a15s - a16s - a17s - a18s - a19s - a20s - a21s - a22s - a23s - a24s - a25s - a26s - a27s - a28s - a29s - a30s - a31s - ) -{-# INLINE zipWith32 #-} diff --git a/bittide-extra/src/Numeric/Extra.hs b/bittide-extra/src/Numeric/Extra.hs deleted file mode 100644 index 6263dc1f4..000000000 --- a/bittide-extra/src/Numeric/Extra.hs +++ /dev/null @@ -1,30 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Numeric.Extra where - -import Clash.Prelude - -import Control.Monad (foldM) -import Data.Char (digitToInt, isHexDigit) - -{- | Parse a hexadecimal string into a 'BitPack'able type. - -Note that this function does not handle types that do not use the full range -of their bit size. For example, 'Index' will return an 'XException' if the -parsed value is out of range of the @'Index' n@, but in range of -@'BitVector' ('BitSize' ('Index' n))@. To fix this properly, we need a version -of 'unpack' that returns a 'Maybe' value. --} -parseHex :: forall a. (BitPack a) => String -> Either String a -parseHex "" = Left "Empty string" -parseHex s = do - result <- foldM parseDigit (0 :: Integer) s - if result > natToNum @(2 ^ BitSize a - 1) - then Left $ "Value is out of range: " <> show result - else Right $ unpack (fromInteger result) - where - parseDigit !a c - | not (isHexDigit c) = Left $ "Non-hexadecimal digit: " <> [c] <> " in " <> s - | otherwise = Right $ shift a 4 .|. toInteger (digitToInt c) diff --git a/bittide-extra/tests/doctests/Main.hs b/bittide-extra/tests/doctests/Main.hs deleted file mode 100644 index c3cfcfa01..000000000 --- a/bittide-extra/tests/doctests/Main.hs +++ /dev/null @@ -1,14 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import System.Environment (getArgs) -import Test.DocTest (mainFromCabal) - -main :: IO () -main = do - -- We use Nix to setup tooling, not to provide GHC packages so we need to set --no-nix - args <- getArgs - mainFromCabal "bittide-extra" ("--no-nix" : args) diff --git a/bittide-extra/tests/unittests/Main.hs b/bittide-extra/tests/unittests/Main.hs deleted file mode 100644 index 7a7f038bc..000000000 --- a/bittide-extra/tests/unittests/Main.hs +++ /dev/null @@ -1,30 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import Prelude - -import Test.Tasty - -import Test.Tasty.Hedgehog (HedgehogTestLimit (..)) -import qualified Tests.Numeric.Extra - -setDefaultHedgehogTestLimit :: HedgehogTestLimit -> HedgehogTestLimit -setDefaultHedgehogTestLimit (HedgehogTestLimit Nothing) = HedgehogTestLimit (Just 10000) -setDefaultHedgehogTestLimit opt = opt - -tests :: TestTree -tests = - testGroup - "tests" - [ Tests.Numeric.Extra.tests - ] - -main :: IO () -main = - defaultMain $ - adjustOption - setDefaultHedgehogTestLimit - tests diff --git a/bittide-extra/tests/unittests/Tests/Numeric/Extra.hs b/bittide-extra/tests/unittests/Tests/Numeric/Extra.hs deleted file mode 100644 index 09705af65..000000000 --- a/bittide-extra/tests/unittests/Tests/Numeric/Extra.hs +++ /dev/null @@ -1,91 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} - -module Tests.Numeric.Extra where - -import Clash.Prelude hiding (someNatVal) - -import Clash.Hedgehog.Sized.Unsigned (genUnsigned) -import Data.Typeable (Typeable, typeRep) -import GHC.TypeNats (someNatVal) -import Hedgehog -import Numeric (showHex) -import Numeric.Extra (parseHex) -import Test.Tasty -import Test.Tasty.Hedgehog - -import Data.Proxy -import qualified Hedgehog.Internal.Gen as Gen -import qualified Hedgehog.Range as Range - -{- | Generate an in range value, convert it to hex, and parse it back. The result -should be the same as the original value. --} -parseHexInRange :: - forall a m. - (Monad m, BitPack a, Eq a, Show a, Bounded a, Integral a) => - Gen a -> - PropertyT m () -parseHexInRange genA = do - a0 <- forAll genA - let a0hex = showHex (toInteger a0) "" - a1 <- evalEither (parseHex a0hex) - a0 === a1 - --- | 'parseHexInRange', but specialized to 'Unsigned'. -parseHexInRangeUnsigned :: Property -parseHexInRangeUnsigned = property $ do - n <- forAll $ Gen.integral (Range.linear 0 128) - case someNatVal n of - SomeNat (Proxy :: Proxy n) -> - parseHexInRange (genUnsigned @_ @n Range.constantBounded) - -{- | Generate an in range value, add @maxBound + 1@ to it, convert it to hex, and -parse it back. The result should yield a parse error. --} -parseHexOutOfRange :: - forall a m. - (Monad m, BitPack a, Eq a, Show a, Bounded a, Integral a, Typeable a) => - Gen a -> - PropertyT m () -parseHexOutOfRange genA = do - a0 <- forAll genA - let - a1 = toInteger a0 + toInteger (maxBound @a) + 1 - a1hex = showHex a1 "" - case parseHex @a a1hex of - Left _ -> success - Right res -> do - footnote ("type: " <> show (typeRep (Proxy @a))) - footnote ("a0: " <> show a0) - footnote ("a1: " <> show a1) - footnote ("a1hex: " <> a1hex) - footnote ("res: " <> show res) - failure - --- | 'parseHexOutOfRange', but specialized to 'Unsigned'. -parseHexOutOfRangeUnsigned :: Property -parseHexOutOfRangeUnsigned = property $ do - n <- forAll $ Gen.integral (Range.linear 0 128) - case someNatVal n of - SomeNat (Proxy :: Proxy n) -> - parseHexOutOfRange (genUnsigned @_ @n Range.constantBounded) - -tests :: TestTree -tests = - testGroup - "Tests.Numeric.Extra" - [ testGroup - "parseHexRountTrip" - [ testPropertyNamed - "Unsigned in range" - "parseHexInRangeUnsigned" - parseHexInRangeUnsigned - , testPropertyNamed - "Unsigned out of range" - "parseHexOutOfRangeUnsigned" - parseHexOutOfRangeUnsigned - ] - ] diff --git a/bittide-instances/README.md b/bittide-instances/README.md deleted file mode 100644 index 987455e35..000000000 --- a/bittide-instances/README.md +++ /dev/null @@ -1,25 +0,0 @@ - - -# bittide-instances -Collection of monomorphic instances of realistic Bittide components. These instances are meant -to be handled by bittide-shake. - -This collection contains instances with various purposes: -* CI tests that ensure all components will meet timing. -* Instances that can be loaded onto the KCU105 target board. -* Hardware in the loop tests. - -## General approach -We synthesize each component in the package `Bittide` and their composites on their own. Each should meet the target frequency of 200 MHz. (Note that when two components meet timing, their composite might not.) Not every component is suitable for top-level integration - due to the sheer number of pins required for some. To get timing information still, we can either synthesize using [Out of Context commands](https://docs.xilinx.com/r/2021.2-English/ug905-vivado-hierarchical-design/Synthesis?tocId=vkakVL_suw7wlNgcaeVIYQ) (TODO: Implement) or go through the whole flow using `Bittide.Instances.Hacks.reducePins`. This wrapper makes sure that a design with any number of pins is mapped to a single input and a single output pin - while making sure synthesis can't eliminate any logic due to this. This way, we can run Vivado all the way up to implementation: - -![reducePins architecture](imgs/reducePins.svg) - -The build system automatically generates _false path_ constraints for all input and output pins. Hence, any paths from the input pin to the shift registers, and any paths from the output pin to the output of a flipflop are dismissed from timing analysis. This makes sure only the design under test is analyzed, not the logic inserted to map to a single input/output pin. - -**Note that false path constraints are also generated for any reset lines. You therefore need to make sure to properly synchronize your resets before feeding them to a circuit.** - -To learn more about using these instances, go to the [`README.md` in `bittide-shake`](../bittide-shake/README.md). diff --git a/bittide-instances/bittide-instances.cabal b/bittide-instances/bittide-instances.cabal deleted file mode 100644 index edc32ef12..000000000 --- a/bittide-instances/bittide-instances.cabal +++ /dev/null @@ -1,284 +0,0 @@ -cabal-version: 2.4 -name: bittide-instances -synopsis: - Top-entities for synthesis fixing target specific - parameters and connecting independent components to - architectures - -version: 0.1 -license: Apache-2.0 -license-file: LICENSE -author: QBayLogic B.V. -maintainer: devops@qbaylogic.com -copyright: Copyright © 2022-2024 Google LLC -data-files: - -- Cabal is very picky about wildcards in `data-files`: - -- https://cabal.readthedocs.io/en/3.6/cabal-package.html#pkg-field-data-files - data/**/*.gdb - data/**/*.sh - data/**/*.xdc - data/**/*.yml - -common common-options - default-extensions: - -- TemplateHaskell is used to support convenience functions such as - -- 'listToVecTH' and 'bLit'. - -- - -- `NoImplicitPrelude` is used because Clash offers Clash.Prelude - BangPatterns - BinaryLiterals - ConstraintKinds - DataKinds - DefaultSignatures - DeriveAnyClass - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveLift - DeriveTraversable - DerivingStrategies - InstanceSigs - KindSignatures - LambdaCase - NoImplicitPrelude - NoStarIsType - PolyKinds - QuasiQuotes - RankNTypes - ScopedTypeVariables - StandaloneDeriving - TemplateHaskell - TupleSections - TypeApplications - TypeFamilies - TypeOperators - ViewPatterns - - ghc-options: - -- Plugins to support type-level constraint solving on naturals: - -- - GHC.TypeLits.Extra.Solver - -- - GHC.TypeLits.Normalise - -- - GHC.TypeLits.KnownNat.Solver - -- Clash needs access to the source code in compiled modules: - -- -fexpose-all-unfoldings - -- Worker wrappers introduce unstable names for functions that might have - -- blackboxes attached for them. You can disable this, but be sure to add - -- a no-specialize pragma to every function with a blackbox. - -- -fno-worker-wrapper - -Wall - -Wcompat - -fplugin=GHC.TypeLits.Extra.Solver - -fplugin=GHC.TypeLits.Normalise - -fplugin=GHC.TypeLits.KnownNat.Solver - -fexpose-all-unfoldings - -fno-worker-wrapper - - default-language: Haskell2010 - build-depends: - MissingH, - aeson, - base, - base16-bytestring, - bittide, - bittide-experiments, - bittide-extra, - bytestring, - cassava, - clash-cores, - clash-lib, - clash-prelude, - clash-protocols, - clash-vexriscv, - constraints >=0.13.3 && <0.15, - containers, - cryptohash-sha256, - directory, - extra, - filepath, - ghc-typelits-extra, - ghc-typelits-knownnat, - ghc-typelits-natnormalise, - lift-type, - pretty-simple, - process, - shake, - split, - string-interpolate, - tasty, - tasty-hunit, - tasty-th, - template-haskell, - temporary, - text, - unix, - vector, - -library - import: common-options - hs-source-dirs: src - exposed-modules: - Bittide.Instances.Domains - Bittide.Instances.Hacks - Bittide.Instances.Hitl.BoardTest - Bittide.Instances.Hitl.Ethernet - Bittide.Instances.Hitl.FincFdec - Bittide.Instances.Hitl.FullMeshHwCc - Bittide.Instances.Hitl.FullMeshSwCc - Bittide.Instances.Hitl.HwCcTopologies - Bittide.Instances.Hitl.IlaPlot - Bittide.Instances.Hitl.LinkConfiguration - Bittide.Instances.Hitl.Post.BoardTestExtended - Bittide.Instances.Hitl.Post.PostProcess - Bittide.Instances.Hitl.Setup - Bittide.Instances.Hitl.SyncInSyncOut - Bittide.Instances.Hitl.TemperatureMonitor - Bittide.Instances.Hitl.Tests - Bittide.Instances.Hitl.Transceivers - Bittide.Instances.Hitl.VexRiscv - Bittide.Instances.Pnr.Calendar - Bittide.Instances.Pnr.ClockControl - Bittide.Instances.Pnr.Counter - Bittide.Instances.Pnr.ElasticBuffer - Bittide.Instances.Pnr.Ethernet - Bittide.Instances.Pnr.ProcessingElement - Bittide.Instances.Pnr.ScatterGather - Bittide.Instances.Pnr.Si539xSpi - Bittide.Instances.Pnr.StabilityChecker - Bittide.Instances.Pnr.Synchronizer - Paths.Bittide.Instances - Project.FilePath - Project.Handle - Project.Programs - - other-modules: - Paths_bittide_instances - - autogen-modules: - Paths_bittide_instances - -test-suite doctests - type: exitcode-stdio-1.0 - hs-source-dirs: tests - main-is: doctests.hs - ghc-options: - -Wall - -Wcompat - -threaded - - build-depends: - base, - bittide-instances, - doctest-parallel >=0.3.0.1, - - default-language: Haskell2010 - -test-suite unittests - import: common-options - hs-source-dirs: tests - type: exitcode-stdio-1.0 - main-is: unittests.hs - ghc-options: - -Wall - -Wcompat - -threaded - - other-modules: - Tests.OverflowResistantDiff - Wishbone.Axi - Wishbone.DnaPortE2 - Wishbone.Time - - build-depends: - HUnit, - bittide-instances, - clash-prelude-hedgehog >=1.6 && <1.10, - filepath, - hedgehog >=1.0 && <1.5, - parsec, - pretty-simple, - tasty, - tasty-golden, - tasty-hedgehog >=1.2 && <1.5, - tasty-th, - -executable clash - import: common-options - ghc-options: - -Wall - -Wcompat - -threaded - - main-is: exe/clash/Main.hs - build-depends: - bittide-instances, - clash-ghc, - vivado-hs, - -executable post-board-test-extended - import: common-options - ghc-options: - -Wall - -Wcompat - -threaded - - main-is: exe/post-board-test-extended/Main.hs - build-depends: - Glob, - bittide-instances, - filepath, - -executable post-vex-riscv-test - import: common-options - ghc-options: - -Wall - -Wcompat - -threaded - - main-is: exe/post-vex-riscv-test/Main.hs - build-depends: - bittide-instances, - extra, - process, - tasty, - tasty-hunit, - tasty-th, - temporary, - - other-modules: Paths_bittide_instances - -executable post-vex-riscv-tcp-test - import: common-options - ghc-options: - -Wall - -Wcompat - -threaded - - main-is: exe/post-vex-riscv-tcp-test/Main.hs - build-depends: - bittide-instances, - extra, - process, - tasty, - tasty-hunit, - tasty-th, - temporary, - - other-modules: Paths_bittide_instances - -executable post-fullMeshSwCcTest - import: common-options - ghc-options: - -Wall - -Wcompat - -threaded - - main-is: exe/post-fullMeshSwCcTest/Main.hs - build-depends: - base, - bittide-instances, - bytestring, - cassava, - deepseq, - filepath, - vector, diff --git a/bittide-instances/bittide-instances.cabal.license b/bittide-instances/bittide-instances.cabal.license deleted file mode 100644 index 848612f0e..000000000 --- a/bittide-instances/bittide-instances.cabal.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/bittide-instances/data/constraints/boardTestExtended.xdc b/bittide-instances/data/constraints/boardTestExtended.xdc deleted file mode 100644 index 4ff318e78..000000000 --- a/bittide-instances/data/constraints/boardTestExtended.xdc +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# CLK_125MHZ_P -set_property PACKAGE_PIN G10 [get_ports "CLK_125MHZ_p"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_p"] -# CLK_125MHZ_P -set_property PACKAGE_PIN F10 [get_ports "CLK_125MHZ_n"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_n"] - - -# GPIO_LED_0_LS -set_property PACKAGE_PIN AP8 [get_ports "done"] -set_property IOSTANDARD LVCMOS18 [get_ports "done"] -# GPIO_LED_1_LS -set_property PACKAGE_PIN H23 [get_ports "success"] -set_property IOSTANDARD LVCMOS18 [get_ports "success"] diff --git a/bittide-instances/data/constraints/boardTestSimple.xdc b/bittide-instances/data/constraints/boardTestSimple.xdc deleted file mode 100644 index 4ff318e78..000000000 --- a/bittide-instances/data/constraints/boardTestSimple.xdc +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# CLK_125MHZ_P -set_property PACKAGE_PIN G10 [get_ports "CLK_125MHZ_p"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_p"] -# CLK_125MHZ_P -set_property PACKAGE_PIN F10 [get_ports "CLK_125MHZ_n"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_n"] - - -# GPIO_LED_0_LS -set_property PACKAGE_PIN AP8 [get_ports "done"] -set_property IOSTANDARD LVCMOS18 [get_ports "done"] -# GPIO_LED_1_LS -set_property PACKAGE_PIN H23 [get_ports "success"] -set_property IOSTANDARD LVCMOS18 [get_ports "success"] diff --git a/bittide-instances/data/constraints/extraProbesTest.xdc b/bittide-instances/data/constraints/extraProbesTest.xdc deleted file mode 100644 index 735c9211c..000000000 --- a/bittide-instances/data/constraints/extraProbesTest.xdc +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# CLK_125MHZ_P -set_property PACKAGE_PIN G10 [get_ports "CLK_125MHZ_p"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_p"] -# CLK_125MHZ_P -set_property PACKAGE_PIN F10 [get_ports "CLK_125MHZ_n"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_n"] - -# GPIO_LED_1_LS -set_property PACKAGE_PIN H23 [get_ports "success"] -set_property IOSTANDARD LVCMOS18 [get_ports "success"] diff --git a/bittide-instances/data/constraints/fincFdecTests.xdc b/bittide-instances/data/constraints/fincFdecTests.xdc deleted file mode 100644 index a7b456651..000000000 --- a/bittide-instances/data/constraints/fincFdecTests.xdc +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -# -# NOTE: This configuration is only valid for the leftmost FPGA in the demo rack. -# -# Color | FPGA pin | LVLShift | Connection -# --------|---------------|---------------|--------- -# Grey | PMOD0_0 | IO1 | SWDIO -# Blue | PMOD0_1 | IO2 | FINC -# Yellow | PMOD0_2 | IO3 | MOSI/SDIO -# Red | PMOD0_3 | IO4 | SCLK -# White | PMOD0_4 | IO5 | SWCLK -# Purple | PMOD0_5 | IO6 | FDEC -# Green | PMOD0_6 | IO7 | CSB -# Orange | PMOD0_7 | IO8 | MISO/SDO -# Black | Not connected | Not connected | GND (SWD) -# Brown | PMOD_GND | GND | GND (SPI) -# -# The data wire of the external reset button is connected to PMOD1_3. - - -# CLK_125MHZ -set_property BOARD_PART_PIN sysclk_125_p [get_ports {CLK_125MHZ_p}] -set_property BOARD_PART_PIN sysclk_125_n [get_ports {CLK_125MHZ_n}] - -# USER_SMA_CLOCK -set_property -dict {IOSTANDARD LVDS PACKAGE_PIN D23} [get_ports {USER_SMA_CLOCK_p}] -set_property -dict {IOSTANDARD LVDS PACKAGE_PIN C23} [get_ports {USER_SMA_CLOCK_n}] - -# Vivado marks all clocks as related by default. Our external clocks are not -# though, which means that we need to explicitly mark them as unrelated (or -# "asynchronous"). -set_clock_groups \ - -asynchronous \ - -group [get_clocks -include_generated_clocks {CLK_125MHZ_p}] \ - -group [get_clocks -include_generated_clocks {USER_SMA_CLOCK_p}] - -# GPIO_LED_0_LS -set_property BOARD_PART_PIN GPIO_LED_0_LS [get_ports {done}] -# GPIO_LED_1_LS -set_property BOARD_PART_PIN GPIO_LED_1_LS [get_ports {success}] - -# PMOD0_[0..7] -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AK25} [get_ports {SWDIO}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AN21} [get_ports {FINC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AH18} [get_ports {MOSI}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {SCLK}] -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE26} [get_ports {SWCLK}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AF25} [get_ports {FDEC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE21} [get_ports {CSB}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM17} [get_ports {MISO}] diff --git a/bittide-instances/data/constraints/fullMeshHwCcTest.xdc b/bittide-instances/data/constraints/fullMeshHwCcTest.xdc deleted file mode 100644 index b1776818f..000000000 --- a/bittide-instances/data/constraints/fullMeshHwCcTest.xdc +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set_property BOARD_PART_PIN sysclk_125_n [get_ports SYSCLK_125_n] -set_property BOARD_PART_PIN sysclk_125_p [get_ports SYSCLK_125_p] -set_property BOARD_PART_PIN sma_mgt_refclk_n [get_ports SMA_MGT_REFCLK_C_n] -set_property BOARD_PART_PIN sma_mgt_refclk_p [get_ports SMA_MGT_REFCLK_C_p] - -set_property BOARD_PART_PIN GPIO_LED_0_LS [get_ports spiDone] - -set_clock_groups \ - -asynchronous \ - -group [get_clocks -include_generated_clocks {SYSCLK_125_p}] \ - -group [get_clocks -include_generated_clocks {SMA_MGT_REFCLK_C_p}] - -# Color | FPGA pin | LVLSHFT | Connection -# --------|---------------|---------------|------------------ -# Grey | PMOD0_0 | IO1 | SYNC_OUT (legacy) -# Blue | PMOD0_1 | IO2 | FINC -# Yellow | PMOD0_2 | IO3 | MOSI/SDIO -# Red | PMOD0_3 | IO4 | SCLK -# White | PMOD0_4 | IO5 | SYNC_IN (legacy) -# Purple | PMOD0_5 | IO6 | FDEC -# Green | PMOD0_6 | IO7 | CSB -# Orange | PMOD0_7 | IO8 | MISO/SDO -# Black | Not connected | Not connected | -# Brown | PMOD_GND | GND | GND (SPI) - -# PMOD1_[0..7] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AN21} [get_ports {FINC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AH18} [get_ports {MOSI}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {SCLK}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AF25} [get_ports {FDEC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE21} [get_ports {CSB}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM17} [get_ports {MISO}] - -# PMOD0_3 -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {shared_reset_btn}] - -# USER SMA GPIO_P -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN H27} [get_ports {SYNC_IN}] -# USER_SMA_GPIO_N (connected on node 0 to SYNC_IN of all nodes) -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN G27} [get_ports {SYNC_OUT}] diff --git a/bittide-instances/data/constraints/fullMeshHwCcWithRiscvTest.xdc b/bittide-instances/data/constraints/fullMeshHwCcWithRiscvTest.xdc deleted file mode 120000 index 54029713e..000000000 --- a/bittide-instances/data/constraints/fullMeshHwCcWithRiscvTest.xdc +++ /dev/null @@ -1 +0,0 @@ -fullMeshHwCcTest.xdc \ No newline at end of file diff --git a/bittide-instances/data/constraints/fullMeshSwCcTest.xdc b/bittide-instances/data/constraints/fullMeshSwCcTest.xdc deleted file mode 120000 index 54029713e..000000000 --- a/bittide-instances/data/constraints/fullMeshSwCcTest.xdc +++ /dev/null @@ -1 +0,0 @@ -fullMeshHwCcTest.xdc \ No newline at end of file diff --git a/bittide-instances/data/constraints/hwCcTopologyTest.xdc b/bittide-instances/data/constraints/hwCcTopologyTest.xdc deleted file mode 120000 index 54029713e..000000000 --- a/bittide-instances/data/constraints/hwCcTopologyTest.xdc +++ /dev/null @@ -1 +0,0 @@ -fullMeshHwCcTest.xdc \ No newline at end of file diff --git a/bittide-instances/data/constraints/jtag_config.xdc b/bittide-instances/data/constraints/jtag_config.xdc deleted file mode 100644 index e58f097ff..000000000 --- a/bittide-instances/data/constraints/jtag_config.xdc +++ /dev/null @@ -1,249 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -## JTAG CONFIG. Stolen from Intel documentation. -# -# Search "---customize here---" for the few decisions you need to make -# -# By default, the most challenging timing spec is applied to work in -# many JTAG chain setup situations - -# This is the main entry point called at the end of this SDC file. -proc set_jtag_timing_constraints { } { - # If the timing characteristic outside of FPGA is well understood, and - # there is a need to provide more slack to allow flexible placement of - # JTAG logic in the FPGA core, use the timing constraints for both - # timing analysis and fitter; otherwise, use the default fitter timing - # constraints. - - # ---customize here--- - set use_fitter_specific_constraint 0 - - if { $use_fitter_specific_constraint && [string equal quartus_fit $::TimingAnalyzerInfo(nameofexecutable)] } { - # Define a different set of timing spec to influence place-and-route - # result in the jtag clock domain. The slacks outside of FPGA are - # maximized. - - set_default_quartus_fit_timing_directive - } else { - # Define a set of timing constraints that describe the JTAG paths - # for the Timing Analyzer to analyze. The Timing Analyzer timing reports show whether - # the JTAG logic in the FPGA core will operates in this setup. - - set_jtag_timing_spec_for_timing_analysis - } -} - -proc set_default_quartus_fit_timing_directive { } { - # A10 supports max 33.3Mhz clock - set jtag_33Mhz_t_period 30 - - create_clock -name {JTAG_TCK} -period $jtag_33Mhz_t_period [get_ports {JTAG_TCK}] - set_clock_groups -asynchronous -group {JTAG_TCK} - # Force fitter to place register driving TDO pin to be as close to - # the JTAG controller as possible to maximize the slack outside of FPGA. - set_max_delay -to [get_ports { JTAG_TDO } ] 0 -} - -proc set_jtag_timing_spec_for_timing_analysis { } { - # TODO: Intel specific: - # derive_clock_uncertainty - - # There are few possible JTAG chain configurations: - # a. This device is the only device in the JTAG chain - # b. This device is the first one in the JTAG chain - # c. This device is in the middle of the JTAG chain - # d. This device is the last one in the JTAG chain - - - # No matter where the device is in the chain. The tck and tms are driven - # directly from JTAG hardware. - set_tck_timing_spec - set_tms_timing_spec - - # Depending on where the device is located along the chain, tdi can be - # either driven by blaster hw (a. b.) or driven by another device in the - # chain(c. d.) - # ---customize here--- - set tdi_is_driven_by_blaster 1 - - if { $tdi_is_driven_by_blaster } { - set_tdi_timing_spec_when_driven_by_blaster - } else { - set_tdi_timing_spec_when_driven_by_device - } - - # Depending on where the device is located along the chain, tdo can - # drive either blaster hw (a. d.) or another device in the chain (b. c.) - # ---customize here--- - set tdo_drive_blaster 1 - - if { $tdo_drive_blaster } { - set_tdo_timing_spec_when_drive_blaster - } else { - set_tdo_timing_spec_when_drive_device - } - - # Cut a few timing paths that are not related to JTAG logic in - # the FPGA core, such as security mode. - set_false_path -from [get_ports {JTAG_TDI}] -to [get_ports {JTAG_TDO}] - - # TODO: Intel specific - # if { [get_collection_size [get_registers -nowarn *~jtag_reg]] > 0 } { - # set_false_path -from [get_registers *~jtag_reg] -to [get_ports {JTAG_TDO}] - # } - -} - -proc set_tck_timing_spec { } { - # USB Blaster 1 uses 6 MHz clock = 166.666 ns period - set ub1_t_period 166.666 - # USB Blaster 2 uses 24 MHz clock = 41.666 ns period - set ub2_default_t_period 41.666 - # USB Blaster 2 running at 16 MHz clock safe mode = 62.5 ns period - set ub2_safe_t_period 62.5 - - # ---customize here--- - set tck_t_period $ub2_default_t_period - - create_clock -name {JTAG_TCK} -period $tck_t_period [get_ports {JTAG_TCK}] - set_clock_groups -asynchronous -group {JTAG_TCK} -} - -proc get_tck_delay_max { } { - set tck_blaster_tco_max 14.603 - set tck_cable_max 2.000 - - # tck delay on the PCB depends on the trace length from JTAG 10-pin - # header to FPGA on board. In general on the PCB, the signal travels - # at the speed of ~160 ps/inch (1000 mils = 1 inch). - # ---customize here--- - set tck_header_trace_max 0.5 - - return [expr $tck_blaster_tco_max + $tck_cable_max + $tck_header_trace_max] -} - -proc get_tck_delay_min { } { - set tck_blaster_tco_min 14.603 - set tck_cable_min 0.373 - - # tck delay on the PCB depends on the trace length from JTAG 10-pin - # header to FPGA on board. In general on the PCB, the signal travels - # at the speed of ~160 ps/inch (1000 mils = 1 inch). - # ---customize here--- - set tck_header_trace_min 0.1 - - return [expr $tck_blaster_tco_min + $tck_cable_min + $tck_header_trace_min] -} - -proc set_tms_timing_spec { } { - set tms_blaster_tco_max 9.468 - set tms_blaster_tco_min 9.468 - - set tms_cable_max 2.000 - set tms_cable_min 0.373 - - # tms delay on the PCB depends on the trace length from JTAG 10-pin - # header to FPGA on board. In general on the PCB, the signal travels - # at the speed of ~160 ps/inch (1000 mils = 1 inch). - # ---customize here--- - set tms_header_trace_max 0.5 - set tms_header_trace_min 0.1 - - set tms_in_max [expr $tms_cable_max + $tms_header_trace_max + $tms_blaster_tco_max - [get_tck_delay_min]] - set tms_in_min [expr $tms_cable_min + $tms_header_trace_min + $tms_blaster_tco_min - [get_tck_delay_max]] - - set_input_delay -add_delay -clock_fall -clock JTAG_TCK -max $tms_in_max [get_ports {JTAG_TMS}] - set_input_delay -add_delay -clock_fall -clock JTAG_TCK -min $tms_in_min [get_ports {JTAG_TMS}] -} - -proc set_tdi_timing_spec_when_driven_by_blaster { } { - set tdi_blaster_tco_max 8.551 - set tdi_blaster_tco_min 8.551 - - set tdi_cable_max 2.000 - set tdi_cable_min 0.373 - - # tms delay on the PCB depends on the trace length from JTAG 10-pin - # header to FPGA on board. In general on the PCB, the signal travels - # at the speed of ~160 ps/inch (1000 mils = 1 inch). - # ---customize here--- - set tdi_header_trace_max 0.5 - set tdi_header_trace_min 0.1 - - set tdi_in_max [expr $tdi_cable_max + $tdi_header_trace_max + $tdi_blaster_tco_max - [get_tck_delay_min]] - set tdi_in_min [expr $tdi_cable_min + $tdi_header_trace_min + $tdi_blaster_tco_min - [get_tck_delay_max]] - - #TDI launches at the falling edge of TCK per standard - set_input_delay -add_delay -clock_fall -clock JTAG_TCK -max $tdi_in_max [get_ports {JTAG_TDI}] - set_input_delay -add_delay -clock_fall -clock JTAG_TCK -min $tdi_in_min [get_ports {JTAG_TDI}] -} - -proc set_tdi_timing_spec_when_driven_by_device { } { - # TCO timing spec of tdo on the device driving this tdi input - # ---customize here--- - set previous_device_tdo_tco_max 10.0 - set previous_device_tdo_tco_min 10.0 - - # tdi delay on the PCB depends on the trace length from JTAG 10-pin - # header to FPGA on board. In general on the PCB, the signal travels - # at the speed of ~160 ps/inch (1000 mils = 1 inch). - # ---customize here--- - set tdi_trace_max 0.5 - set tdi_trace_min 0.1 - - set tdi_in_max [expr $previous_device_tdo_tco_max + $tdi_trace_max - [get_tck_delay_min]] - set tdi_in_min [expr $previous_device_tdo_tco_min + $tdi_trace_min - [get_tck_delay_max]] - - #TDI launches at the falling edge of TCK per standard - set_input_delay -add_delay -clock_fall -clock JTAG_TCK -max $tdi_in_max [get_ports {JTAG_TDI}] - set_input_delay -add_delay -clock_fall -clock JTAG_TCK -min $tdi_in_min [get_ports {JTAG_TDI}] -} - -proc set_tdo_timing_spec_when_drive_blaster { } { - set tdo_blaster_tsu 5.831 - set tdo_blaster_th -1.651 - - set tdo_cable_max 2.000 - set tdo_cable_min 0.373 - - # tdi delay on the PCB depends on the trace length from JTAG 10-pin - # header to FPGA on board. In general on the PCB, the signal travels - # at the speed of ~160 ps/inch (1000 mils = 1 inch). - # ---customize here--- - set tdo_header_trace_max 0.5 - set tdo_header_trace_min 0.1 - - set tdo_out_max [expr $tdo_cable_max + $tdo_header_trace_max + $tdo_blaster_tsu + [get_tck_delay_max]] - set tdo_out_min [expr $tdo_cable_min + $tdo_header_trace_min - $tdo_blaster_th + [get_tck_delay_min]] - - #TDO does not latch inside the USB Blaster II at the rising edge of TCK, - # it actually is latched one half cycle later in packed mode - # (equivalent to 1 JTAG fall-to-fall cycles) - set_output_delay -add_delay -clock_fall -clock JTAG_TCK -max $tdo_out_max [get_ports {JTAG_TDO}] - set_output_delay -add_delay -clock_fall -clock JTAG_TCK -min $tdo_out_min [get_ports {JTAG_TDO}] -} - -proc set_tdo_timing_spec_when_drive_device { } { - # TCO timing spec of tdi on the device driven by this tdo output - # ---customize here--- - set next_device_tdi_tco_max 10.0 - set next_device_tdi_tco_min 10.0 - - # tdi delay on the PCB depends on the trace length from JTAG 10-pin - # header to FPGA on board. In general on the PCB, the signal travels - # at the speed of ~160 ps/inch (1000 mils = 1 inch). - # ---customize here--- - set tdo_trace_max 0.5 - set tdo_trace_min 0.1 - - set tdo_out_max [expr $next_device_tdi_tco_max + $tdo_trace_max + [get_tck_delay_max]] - set tdo_out_min [expr $next_device_tdi_tco_min + $tdo_trace_min + [get_tck_delay_min]] - - #TDO latches at the rising edge of TCK per standard - set_output_delay -add_delay -clock JTAG_TCK -max $tdo_out_max [get_ports {JTAG_TDO}] - set_output_delay -add_delay -clock JTAG_TCK -min $tdo_out_min [get_ports {JTAG_TDO}] -} - -set_jtag_timing_constraints diff --git a/bittide-instances/data/constraints/jtag_pmod0.xdc b/bittide-instances/data/constraints/jtag_pmod0.xdc deleted file mode 100644 index 371230543..000000000 --- a/bittide-instances/data/constraints/jtag_pmod0.xdc +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# PMOD0_[0..7] -# Note that AH18 is a global clock capable pin -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AK25} [get_ports {JTAG_UART_RXD}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AN21} [get_ports {JTAG_UART_TXD}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AH18} [get_ports {JTAG_TCK}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {JTAG_TDI}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE26} [get_ports {JTAG_RST}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AF25} [get_ports {JTAG_TMS}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE21} [get_ports {JTAG_TDO}] diff --git a/bittide-instances/data/constraints/jtag_pmod1.xdc b/bittide-instances/data/constraints/jtag_pmod1.xdc deleted file mode 100644 index 980bdf0ea..000000000 --- a/bittide-instances/data/constraints/jtag_pmod1.xdc +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# PMOD1_[0..7] -# Note that there are no clock capable pins in this list -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AL14} [get_ports {USB_UART_RXD}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM14} [get_ports {USB_UART_TXD}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AP16} [get_ports {JTAG_TCK}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AP15} [get_ports {JTAG_TDI}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM16} [get_ports {JTAG_RST}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM15} [get_ports {JTAG_TMS}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AN18} [get_ports {JTAG_TDO}] - -# PMOD1 does not have a clock capable pin. To Vivado's credit, it refuses to -# produce a bitstream if we try to use a non-clock capable pin as a clock. With -# the following line, we tell Vivado to ignore this warning. -set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets JTAG_TCK] diff --git a/bittide-instances/data/constraints/linkConfigurationTest.xdc b/bittide-instances/data/constraints/linkConfigurationTest.xdc deleted file mode 100644 index 7af3efd26..000000000 --- a/bittide-instances/data/constraints/linkConfigurationTest.xdc +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set_property BOARD_PART_PIN sysclk_125_n [get_ports SYSCLK_125_n] -set_property BOARD_PART_PIN sysclk_125_p [get_ports SYSCLK_125_p] -set_property BOARD_PART_PIN sma_mgt_refclk_n [get_ports SMA_MGT_REFCLK_C_n] -set_property BOARD_PART_PIN sma_mgt_refclk_p [get_ports SMA_MGT_REFCLK_C_p] - -set_property BOARD_PART_PIN GPIO_LED_0_LS [get_ports spiDone] - -set_clock_groups \ - -asynchronous \ - -group [get_clocks -include_generated_clocks {SYSCLK_125_p}] \ - -group [get_clocks -include_generated_clocks {SMA_MGT_REFCLK_C_p}] - -# Color | FPGA pin | LVLSHFT | Connection -# --------|---------------|---------------|------------------ -# Grey | PMOD0_0 | IO1 | SYNC_OUT (legacy) -# Blue | PMOD0_1 | IO2 | FINC -# Yellow | PMOD0_2 | IO3 | MOSI/SDIO -# Red | PMOD0_3 | IO4 | SCLK -# White | PMOD0_4 | IO5 | SYNC_IN (legacy) -# Purple | PMOD0_5 | IO6 | FDEC -# Green | PMOD0_6 | IO7 | CSB -# Orange | PMOD0_7 | IO8 | MISO/SDO -# Black | Not connected | Not connected | -# Brown | PMOD_GND | GND | GND (SPI) - -# PMOD1_[0..7] -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AN21} [get_ports {FINC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AH18} [get_ports {MOSI}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {SCLK}] -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AF25} [get_ports {FDEC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE21} [get_ports {CSB}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM17} [get_ports {MISO}] - -# PMOD0_3 -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {shared_reset_btn}] - -# USER SMA GPIO_P -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN H27} [get_ports {SYNC_IN}] -# USER_SMA_GPIO_N (connected on node 0 to SYNC_IN of all nodes) -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN G27} [get_ports {SYNC_OUT}] diff --git a/bittide-instances/data/constraints/sgmii.xdc b/bittide-instances/data/constraints/sgmii.xdc deleted file mode 100644 index e3e75bf2e..000000000 --- a/bittide-instances/data/constraints/sgmii.xdc +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set_property BOARD_PIN {SGMII_RX_P} [get_ports SGMII_RX_p] -set_property BOARD_PIN {SGMII_RX_N} [get_ports SGMII_RX_n] -set_property BOARD_PIN {SGMII_TX_P} [get_ports SGMII_TX_p] -set_property BOARD_PIN {SGMII_TX_N} [get_ports SGMII_TX_n] -set_property BOARD_PIN {SGMIICLK_P} [get_ports SGMIICLK_p] -set_property BOARD_PIN {SGMIICLK_N} [get_ports SGMIICLK_n] diff --git a/bittide-instances/data/constraints/syncInSyncOut.xdc b/bittide-instances/data/constraints/syncInSyncOut.xdc deleted file mode 100644 index f290986f6..000000000 --- a/bittide-instances/data/constraints/syncInSyncOut.xdc +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set_property BOARD_PART_PIN sysclk_125_n [get_ports SYSCLK_125_n] -set_property BOARD_PART_PIN sysclk_125_p [get_ports SYSCLK_125_p] - -# USER SMA GPIO_P -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN H27} [get_ports {SYNC_IN}] -# USER_SMA_GPIO_N (connected on node 0 to SYNC_IN of all nodes) -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN G27} [get_ports {SYNC_OUT}] diff --git a/bittide-instances/data/constraints/temperatureMonitor.xdc b/bittide-instances/data/constraints/temperatureMonitor.xdc deleted file mode 100644 index 851aeed3e..000000000 --- a/bittide-instances/data/constraints/temperatureMonitor.xdc +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# CLK_125MHZ_P -set_property PACKAGE_PIN G10 [get_ports "CLK_125MHZ_p"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_p"] -# CLK_125MHZ_P -set_property PACKAGE_PIN F10 [get_ports "CLK_125MHZ_n"] -set_property IOSTANDARD LVDS [get_ports "CLK_125MHZ_n"] - -# GPIO_LED_0_LS -set_property PACKAGE_PIN AP8 [get_ports "done"] -set_property IOSTANDARD LVCMOS18 [get_ports "done"] -# GPIO_LED_1_LS -set_property PACKAGE_PIN H23 [get_ports "success"] -set_property IOSTANDARD LVCMOS18 [get_ports "success"] diff --git a/bittide-instances/data/constraints/transceiversUpTest.xdc b/bittide-instances/data/constraints/transceiversUpTest.xdc deleted file mode 100644 index 7af3efd26..000000000 --- a/bittide-instances/data/constraints/transceiversUpTest.xdc +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set_property BOARD_PART_PIN sysclk_125_n [get_ports SYSCLK_125_n] -set_property BOARD_PART_PIN sysclk_125_p [get_ports SYSCLK_125_p] -set_property BOARD_PART_PIN sma_mgt_refclk_n [get_ports SMA_MGT_REFCLK_C_n] -set_property BOARD_PART_PIN sma_mgt_refclk_p [get_ports SMA_MGT_REFCLK_C_p] - -set_property BOARD_PART_PIN GPIO_LED_0_LS [get_ports spiDone] - -set_clock_groups \ - -asynchronous \ - -group [get_clocks -include_generated_clocks {SYSCLK_125_p}] \ - -group [get_clocks -include_generated_clocks {SMA_MGT_REFCLK_C_p}] - -# Color | FPGA pin | LVLSHFT | Connection -# --------|---------------|---------------|------------------ -# Grey | PMOD0_0 | IO1 | SYNC_OUT (legacy) -# Blue | PMOD0_1 | IO2 | FINC -# Yellow | PMOD0_2 | IO3 | MOSI/SDIO -# Red | PMOD0_3 | IO4 | SCLK -# White | PMOD0_4 | IO5 | SYNC_IN (legacy) -# Purple | PMOD0_5 | IO6 | FDEC -# Green | PMOD0_6 | IO7 | CSB -# Orange | PMOD0_7 | IO8 | MISO/SDO -# Black | Not connected | Not connected | -# Brown | PMOD_GND | GND | GND (SPI) - -# PMOD1_[0..7] -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AN21} [get_ports {FINC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AH18} [get_ports {MOSI}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {SCLK}] -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AF25} [get_ports {FDEC}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE21} [get_ports {CSB}] -set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM17} [get_ports {MISO}] - -# PMOD0_3 -# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {shared_reset_btn}] - -# USER SMA GPIO_P -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN H27} [get_ports {SYNC_IN}] -# USER_SMA_GPIO_N (connected on node 0 to SYNC_IN of all nodes) -set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN G27} [get_ports {SYNC_OUT}] diff --git a/bittide-instances/data/constraints/vexRiscEthernetTop.xdc b/bittide-instances/data/constraints/vexRiscEthernetTop.xdc deleted file mode 100644 index 2071a764b..000000000 --- a/bittide-instances/data/constraints/vexRiscEthernetTop.xdc +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set_clock_groups -asynchronous -group [get_clocks -include_generated_clocks CLK_125MHZ_p] -group [get_clocks -include_generated_clocks SGMIICLK_p] - -set_property BOARD_PIN {CPU_RESET} [get_ports CPU_RESET] -set_property BOARD_PIN {sysclk_125_p} [get_ports CLK_125MHZ_p] -set_property BOARD_PIN {sysclk_125_n} [get_ports CLK_125MHZ_n] diff --git a/bittide-instances/data/constraints/vexRiscvTcpTest.xdc b/bittide-instances/data/constraints/vexRiscvTcpTest.xdc deleted file mode 100644 index 2071a764b..000000000 --- a/bittide-instances/data/constraints/vexRiscvTcpTest.xdc +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set_clock_groups -asynchronous -group [get_clocks -include_generated_clocks CLK_125MHZ_p] -group [get_clocks -include_generated_clocks SGMIICLK_p] - -set_property BOARD_PIN {CPU_RESET} [get_ports CPU_RESET] -set_property BOARD_PIN {sysclk_125_p} [get_ports CLK_125MHZ_p] -set_property BOARD_PIN {sysclk_125_n} [get_ports CLK_125MHZ_n] diff --git a/bittide-instances/data/constraints/vexRiscvTest.xdc b/bittide-instances/data/constraints/vexRiscvTest.xdc deleted file mode 100644 index 6e8db747f..000000000 --- a/bittide-instances/data/constraints/vexRiscvTest.xdc +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - - -# CLK_125MHZ -set_property BOARD_PART_PIN sysclk_125_p [get_ports {CLK_125MHZ_p}] -set_property BOARD_PART_PIN sysclk_125_n [get_ports {CLK_125MHZ_n}] - -# GPIO_LED_0_LS -set_property BOARD_PART_PIN GPIO_LED_0_LS [get_ports {done}] -# GPIO_LED_1_LS -set_property BOARD_PART_PIN GPIO_LED_1_LS [get_ports {success}] - -set_clock_groups \ - -asynchronous \ - -group [get_clocks -include_generated_clocks {CLK_125MHZ_p}] diff --git a/bittide-instances/data/gdb/smoltcp-hitl-prog.gdb b/bittide-instances/data/gdb/smoltcp-hitl-prog.gdb deleted file mode 100644 index e11d33afa..000000000 --- a/bittide-instances/data/gdb/smoltcp-hitl-prog.gdb +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -file "./_build/cargo/firmware-binaries/riscv32imc-unknown-none-elf/release/smoltcp_echo" -target extended-remote :3333 - -# Load binary on CPU and start execution -load -start diff --git a/bittide-instances/data/gdb/test-gdb-prog.gdb b/bittide-instances/data/gdb/test-gdb-prog.gdb deleted file mode 100644 index f87d505c7..000000000 --- a/bittide-instances/data/gdb/test-gdb-prog.gdb +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -file "./_build/cargo/firmware-binaries/riscv32imc-unknown-none-elf/debug/hello" -target extended-remote :3333 - -# Load binary on CPU -load - -# Set a breakpoint at 'test_success' (note the CPU starts at '_start') -break hello::test_success - -# Reset program, should run until 'test_success' is hit -jump _start - -# List register values -i r - -# Show stack trace -bt - -# Show available functions -info functions - -# Remove breakpoint -disable 1 - -# Ask super process to terminate us -continue diff --git a/bittide-instances/data/openocd/ports.tcl b/bittide-instances/data/openocd/ports.tcl deleted file mode 100644 index 80bb8b862..000000000 --- a/bittide-instances/data/openocd/ports.tcl +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -bindto 0.0.0.0 -gdb_port 3333 -tcl_port 6666 -telnet_port 4444 diff --git a/bittide-instances/data/openocd/sipeed.tcl b/bittide-instances/data/openocd/sipeed.tcl deleted file mode 100644 index af04d0731..000000000 --- a/bittide-instances/data/openocd/sipeed.tcl +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# Tell OpenOCD to use the ftdi interface -interface ftdi - -# Find the device based on the USB vendor/product ID -ftdi_vid_pid 0x0403 0x6010 -adapter usb location 1-5.1:1 - -# FT2232C IO bits per schematic: -# 0: TCK, Output -# 1: TDO, Output -# 2: TDO, Input -# 3: TMS, Output -# 4: Not connected -# 5: RST, Output -# -# The first 16bit value is the initial IO state. Just make TMS and RST high -# The second 16bit value is data direction for each pin, 1 = Output -ftdi_layout_init 0x0028 0x2b - -# We'll use RST for the system reset, not the JTAG reset. Thus, disable nTRST -# by setting data and enable mask to 0. If you want to use -# the RST pin for nTRST instead, switch this and the nSRST line. -ftdi_layout_signal nTRST -data 0x0 -oe 0x0 - -# RST is on bit 5, so the mask is 0x20. The pin is directly connected, so -# we don't have an output-enable pin -> set the same mask -ftdi_layout_signal nSRST -data 0x0020 -oe 0x0020 - -# JTAG mode -adapter speed 6000 -transport select jtag diff --git a/bittide-instances/data/openocd/start.sh b/bittide-instances/data/openocd/start.sh deleted file mode 100755 index 84880eaa0..000000000 --- a/bittide-instances/data/openocd/start.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -cd $(dirname $0) -exec openocd-vexriscv -f ports.tcl -f sipeed.tcl -f vexriscv_init.tcl $@ diff --git a/bittide-instances/data/openocd/vexriscv_init.tcl b/bittide-instances/data/openocd/vexriscv_init.tcl deleted file mode 100644 index 36b9a0716..000000000 --- a/bittide-instances/data/openocd/vexriscv_init.tcl +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -set _ENDIAN little -set _TAP_TYPE 1234 - -if { [info exists CPUTAPID] } { - set _CPUTAPID $CPUTAPID -} else { - # set useful default - set _CPUTAPID 0x10001fff -} - -set _CHIPNAME vexrisc_ocd - -# The JTAG TAP itself is given the name "bridge", because it refers to the -# JtagBridge that's part of the VexRiscv/SpinalHDL debug infrastructure. -# In the example design, there is the JtagBridge controls a single CPU, but -# the capability is there for 1 JTAG TAP + JtagBridge to control multiple -# VexRiscv CPUs. -jtag newtap $_CHIPNAME bridge -expected-id $_CPUTAPID -irlen 4 -ircapture 0x1 -irmask 0xF - -# There is 1 CPU controlled by the "bridge" JTAG TAP, "cpu0" -target create $_CHIPNAME.cpu0 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.bridge - -# The JtagBridge/SystemDebugger receives commands in a serialized way. It gets synchronized into -# a parallel bus, and a response is received. Along the way, there may be various clock domain -# crossings or pipeline delays. -# readWaitCycles instructs OpenOCD to insert idle JTAG clock cycles before shifting out -# the response. -# There aren't many transactions where read-back throughput is important, so there's little -# points in lowballing this number. -vexriscv readWaitCycles 10 - -# When the Verilog of a SpinalHDL design with one or more VexRiscv CPUs is created, the system -# also creates a .yaml file with information that's sideband information that's important for -# OpenOCD to control the CPU correctly. -# A good example of this are the number of hardware breakpoints that are supported by the CPU. -set git_top_level [string trim [exec git rev-parse --show-toplevel]] -vexriscv cpuConfigFile [file join $git_top_level clash-vexriscv clash-vexriscv example-cpu ExampleCpu.yaml] - -# The rate at which OpenOCD polls active JTAG TAPs to check if there has been a notable -# event. (E.g. to check if the CPU has hit a breakpoint.) -# For some reason, making this number really low has an impact on the CPU while semihosting is -# enabled? -poll_period 50 - -# Initialize all JTAG TAPs and targets. -init - -echo "Halting processor" - -# Halts the CPU -halt - -# If you also want to reset the CPU, use: -# soft_reset_halt - -sleep 1000 diff --git a/bittide-instances/data/picocom/start.sh b/bittide-instances/data/picocom/start.sh deleted file mode 100755 index 3f0dfe34e..000000000 --- a/bittide-instances/data/picocom/start.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -# -# Start picocom with defaults suitable for our instances. Pass in a USB device, -# for example: -# -# /dev/serial/by-id/usb-FTDI_Dual_RS232-if01-port0 -# -picocom --baud 921600 --imap lfcrlf --omap lfcrlf $@ diff --git a/bittide-instances/data/tcpspray/start.sh b/bittide-instances/data/tcpspray/start.sh deleted file mode 100755 index 04df6dbce..000000000 --- a/bittide-instances/data/tcpspray/start.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -# -# Start tcpspray on a target IP address. -tcpspray 10.0.0.10 -e -n 10000 diff --git a/bittide-instances/data/test_configs/extraProbesTest.yml b/bittide-instances/data/test_configs/extraProbesTest.yml deleted file mode 100644 index 9250806a5..000000000 --- a/bittide-instances/data/test_configs/extraProbesTest.yml +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -defaults: - probes: - testState: 0 - extraProbe: 0 - -tests: - testDefaultProbes: null - testSpecificProbes: - probes: - testState: 1 - extraProbe: 0xDEADABBA - testFpgaSpecificProbes: - # We set the extraProbes to the DNA identifier of the respective FPGA - probes: - testState: 2 - targets: - - target: { index: 0 } - probes: - extraProbe: 0x00000000400200010169c040044164c5 - - target: { index: 1 } - probes: - extraProbe: 0x0000000040020001815160e805108285 - - target: { index: 2 } - probes: - extraProbe: 0x000000004002000101695ce72c808445 - - target: { index: 3 } - probes: - extraProbe: 0x000000004002000101695ce72c702305 - - target: { index: 4 } - probes: - extraProbe: 0x0000000040020001016ba8e52581a285 - - target: { index: 5 } - probes: - extraProbe: 0x00000000400200010157f4862d01c345 - - target: { index: 6 } - probes: - extraProbe: 0x00000000400200010169c04004308185 - - target: { index: 7 } - probes: - extraProbe: 0x0000000040020001015664862d20e405 diff --git a/bittide-instances/exe/clash/Main.hs b/bittide-instances/exe/clash/Main.hs deleted file mode 100644 index 95c2721f2..000000000 --- a/bittide-instances/exe/clash/Main.hs +++ /dev/null @@ -1,10 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -import Clash.Main (defaultMain) -import System.Environment (getArgs) -import Prelude - -main :: IO () -main = defaultMain =<< getArgs diff --git a/bittide-instances/exe/post-board-test-extended/Main.hs b/bittide-instances/exe/post-board-test-extended/Main.hs deleted file mode 100644 index d9b6f3b19..000000000 --- a/bittide-instances/exe/post-board-test-extended/Main.hs +++ /dev/null @@ -1,32 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -import Prelude - -import Control.Exception -import System.Environment (getArgs) -import System.FilePath (()) -import System.FilePath.Glob (glob) - -import Bittide.Instances.Hitl.Post.BoardTestExtended -import Bittide.Instances.Hitl.Post.PostProcess - -main :: IO () -main = do - args <- getArgs - case args of - ilaDataDir : [testExitCode] -> do - csvPaths <- glob (ilaDataDir "*" "*" "*.csv") - let ilaCsvPaths = toFlattenedIlaCsvPathList ilaDataDir csvPaths - let exitCode = read testExitCode - postBoardTestExtended exitCode ilaCsvPaths - [] -> - throwIO - (userError "Expected 2 arguments (ILA data dir and HITL test exit code), got none") - _ -> - throwIO - ( userError $ - "Expected 2 arguments (ILA data dir and HITL test exit code), got: " <> unwords args - ) - pure () diff --git a/bittide-instances/exe/post-fullMeshSwCcTest/Main.hs b/bittide-instances/exe/post-fullMeshSwCcTest/Main.hs deleted file mode 100644 index 8f9511d2c..000000000 --- a/bittide-instances/exe/post-fullMeshSwCcTest/Main.hs +++ /dev/null @@ -1,132 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeApplications #-} - -{- | This program extracts the UGNs from the last sample of the ila dumps of the fullMeshSwCcTest, -And combines them from both sides of each link to calculate the IGNs. --} -module Main where - -import qualified Clash.Prelude as C -import Prelude - -import Control.DeepSeq (deepseq) -import Control.Monad (filterM, forM, forM_, join) -import qualified Data.ByteString.Lazy as ByteString -import Data.Csv -import Data.Int (Int64) -import Data.List (intercalate, sort) -import Data.Maybe (catMaybes) -import qualified Data.Vector as Vector -import System.Directory (doesDirectoryExist, listDirectory) -import System.Environment (getArgs) -import System.Exit (ExitCode (..)) -import System.FilePath (()) -import System.IO (IOMode (WriteMode), hPutStr, hPutStrLn, stderr, withFile) -import Text.Read (readMaybe) - -import Bittide.Instances.Hitl.Setup (fpgaSetup) - -type FpgaNo = Int -type FpgaId = String -type TestNo = Int -type LinkNo = Int - -data Ugns a = Ugns - { probe_ugn0 :: a - , probe_ugn1 :: a - , probe_ugn2 :: a - , probe_ugn3 :: a - , probe_ugn4 :: a - , probe_ugn5 :: a - , probe_ugn6 :: a - } - deriving (Show, Functor) - -ugnsToList :: Ugns a -> [a] -ugnsToList ugns = - [ probe_ugn0 ugns - , probe_ugn1 ugns - , probe_ugn2 ugns - , probe_ugn3 ugns - , probe_ugn4 ugns - , probe_ugn5 ugns - , probe_ugn6 ugns - ] - -instance (FromField a) => FromNamedRecord (Ugns a) where - parseNamedRecord m = - Ugns - <$> m .: "probe_ugn0" - <*> m .: "probe_ugn1" - <*> m .: "probe_ugn2" - <*> m .: "probe_ugn3" - <*> m .: "probe_ugn4" - <*> m .: "probe_ugn5" - <*> m .: "probe_ugn6" - -fpgaIds :: [FpgaId] -fpgaIds = C.toList $ fmap fst fpgaSetup - -fpgas :: [(FpgaNo, FpgaId)] -fpgas = zip [(0 :: FpgaNo) ..] fpgaIds - -fpgaLinks :: [[Int]] -fpgaLinks = C.toList $ fmap (C.toList . fmap fromIntegral . snd) fpgaSetup - -main :: IO () -main = do - args <- getArgs - case args of - [ilaDir, exitCode0] -> case readMaybe @ExitCode exitCode0 of - Nothing -> error $ "Couldn't parse second argument (" <> show exitCode0 <> ") as ExitCode" - Just exitCode - | exitCode /= ExitSuccess -> error $ "Test run failed, got exit code " <> show exitCode - | otherwise -> doIt ilaDir - ilaDir : _ -> doIt ilaDir - [] -> error "I need the path to the ila-data" - --- | Parses "CCn" to Just n, otherwise Nothing -parseCCnum :: String -> Maybe TestNo -parseCCnum str = case splitAt 2 str of - ("CC", n) -> readMaybe n - _ -> Nothing - -doIt :: FilePath -> IO () -doIt ilaDir = do - dirs :: [FilePath] <- - join $ (filterM (\d -> doesDirectoryExist (ilaDir d))) <$> listDirectory ilaDir - let testNos = sort $ catMaybes $ map parseCCnum dirs - - tests :: [[[Int64]]] <- forM testNos $ \testNo -> - forM fpgas $ \(fpgaNo, fpgaId) -> do - let filename = ilaDir "CC" ++ show testNo show fpgaNo ++ "_" ++ fpgaId "fincFdecIla.csv" - hPutStrLn stderr $ "Reading " <> filename - Right (_, csv0) <- decodeByName @(Ugns String) <$> ByteString.readFile filename - let results = ugnsToList $ read @Int64 . ("0x" <>) <$> Vector.last csv0 - results `deepseq` pure results - - ugns <- forM tests $ \test -> do - forM (zip [(0 :: FpgaNo) ..] test) $ \(fpgano, ugns) -> do - fmap (fpgano,) $ - forM (zip [(0 :: LinkNo) ..] ugns) $ \(linkno, ugn) -> do - let otherUgn = test !! (fpgaLinks !! fpgano !! linkno) !! linkno - pure (linkno, ugn, ugn + otherUgn) - - forM_ fpgas $ \(fpgaNo, _) -> do - withFile (ilaDir "results_fpga_" <> show fpgaNo <> ".csv") WriteMode $ \h -> do - let ugns1 = (!! fpgaNo) <$> ugns - hPutStr h "testno," - y <- forM [(0 :: LinkNo) .. 6] $ \linkNo -> - pure $ "ugn" <> show linkNo <> "," <> "ign" <> show linkNo - hPutStrLn h (intercalate "," y) - - forM_ (zip testNos ugns1) $ \(testNo, (_fpgaNo, rows)) -> do - hPutStr h $ show testNo <> "," - x <- forM rows $ \(_linkNo, ugn, ign) -> do - pure $ show ugn <> "," <> show ign - hPutStrLn h $ intercalate "," x diff --git a/bittide-instances/exe/post-vex-riscv-tcp-test/Main.hs b/bittide-instances/exe/post-vex-riscv-tcp-test/Main.hs deleted file mode 100644 index a3811097d..000000000 --- a/bittide-instances/exe/post-vex-riscv-tcp-test/Main.hs +++ /dev/null @@ -1,130 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} - -import Prelude - -import Data.List.Extra -import Data.Maybe -import Paths_bittide_instances -import Project.Handle -import Project.Programs -import System.Environment (withArgs) -import System.IO -import System.Process -import Test.Tasty.HUnit -import Test.Tasty.TH - -getGdbScriptPath :: IO FilePath -getGdbScriptPath = getDataFileName "data/gdb/smoltcp-hitl-prog.gdb" - -{- | Return the beginning of a string until you detect a certain substring -That substring is not included in the result. --} -readUntil :: String -> String -> String -readUntil end inp - | isPrefixOf end inp = "" - | otherwise = case uncons inp of - Just (h, t) -> h : readUntil end t - Nothing -> "" - -{- | Test that the GDB program works as expected. This test will start OpenOCD, -Picocom, and GDB, and will wait for the GDB program to execute specific -commands. This test will fail if any of the processes fail, or if the GDB -program does not execute the expected commands. - -OpenOCD: A program that communicates with the FPGA over JTAG. When it starts - it will \"interrogate\" the JTAG chain - making sure it can read our - CPU's ID. After that, it will open a GDB server on port 3333. - -Picocom: A program that communicates with the FPGA over UART. - -GDB: GNU Debugger. This program will connect to the OpenOCD server and is able - to, amongst other things, load programs, set break points, and step - through code. --} -case_testGdbProgram :: Assertion -case_testGdbProgram = do - startOpenOcdPath <- getOpenOcdStartPath - startPicocomPath <- getPicocomStartPath - startTcpPrayhPath <- getTcpSprayPath - uartDev <- getUartDev - gdbScriptPath <- getGdbScriptPath - - putStrLn "Starting Gdb" - withAnnotatedGdbScriptPath gdbScriptPath $ \gdbProgPath -> do - let - openOcdProc = (proc startOpenOcdPath []){std_err = CreatePipe} - tcpSprayProc = (proc startTcpPrayhPath []){std_out = CreatePipe, std_err = CreatePipe} - gdbProc = (proc "gdb" ["--command", gdbProgPath]){std_out = CreatePipe, std_err = CreatePipe} - picocomProc = (proc startPicocomPath [uartDev]){std_out = CreatePipe, std_in = CreatePipe} - - -- Wait until we see "Halting processor", fail if we see an error - waitForHalt s - | "Error:" `isPrefixOf` s = Stop (Error ("Found error in OpenOCD output: " <> s)) - | "Halting processor" `isPrefixOf` s = Stop Ok - | otherwise = Continue - - putStrLn "Starting OpenOcd..." - withCreateProcess openOcdProc $ \_ _ (fromJust -> openOcdStdErr) _ -> do - hSetBuffering openOcdStdErr LineBuffering - - putStr "Waiting for halt..." - expectLine openOcdStdErr waitForHalt - putStrLn " Done" - - putStrLn "Starting Picocom..." - withCreateProcess picocomProc $ \maybePicocomStdIn maybePicocomStdOut maybePicocomStdErr _ -> do - let - picocomStdInHandle = fromJust maybePicocomStdIn - picocomStdOutHandle = fromJust maybePicocomStdOut - - hSetBuffering picocomStdInHandle LineBuffering - hSetBuffering picocomStdOutHandle LineBuffering - - putStr "Waiting for Picocom to be ready..." - hFlush stdout - waitForLine picocomStdOutHandle "Terminal ready" - putStrLn " Done" - - putStrLn "Starting GDB..." - withCreateProcess gdbProc $ \_ (fromJust -> gdbStdOut) _ _ -> do - -- Wait for GDB to program the FPGA. If successful, we should see - -- "going in echo mode" in the picocom output. - hSetBuffering gdbStdOut LineBuffering - - putStr "Waiting for GDB to program the FPGA..." - hFlush stdout - waitForLine picocomStdOutHandle "listening" - putStrLn " Done" - - putStrLn "Starting TCP Spray..." - -- Test TCP echo server - withCreateProcess tcpSprayProc $ \_ (fromJust -> tcpStdOut) (fromJust -> tcpStdErr) _ -> do - (stdOutContents, stdErrContents) <- do - stdOutContents' <- hGetContents' tcpStdOut - stdErrContents' <- hGetContents' tcpStdErr - pure (stdOutContents', stdErrContents') - picocomOut <- hGetContents picocomStdOutHandle - - case maybePicocomStdErr of - Nothing -> pure () - Just h -> do - putStrLn "Picocom StdErr" - readRemainingChars h >>= putStrLn - - putStrLn "" - putStrLn "Picocom stdout" - putStrLn $ readUntil "listening" picocomOut - - putStrLn "" - putStrLn "tcpSpray StdOut" - putStrLn stdOutContents - - if not (null stdErrContents) - then assertFailure $ "tcpspray failed with stderr:\n" <> stdErrContents - else pure () - -main :: IO () -main = withArgs ["--timeout", "2m"] $(defaultMainGenerator) diff --git a/bittide-instances/exe/post-vex-riscv-test/Main.hs b/bittide-instances/exe/post-vex-riscv-test/Main.hs deleted file mode 100644 index ec1605ece..000000000 --- a/bittide-instances/exe/post-vex-riscv-test/Main.hs +++ /dev/null @@ -1,88 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -import Prelude - -import Data.List.Extra (isPrefixOf) -import Data.Maybe (fromJust) -import Paths_bittide_instances -import Project.Handle -import Project.Programs -import System.Environment (withArgs) -import System.IO -import System.Process - -import Test.Tasty.HUnit -import Test.Tasty.TH - -getGdbScriptPath :: IO FilePath -getGdbScriptPath = getDataFileName "data/gdb/test-gdb-prog.gdb" - -{- | Test that the GDB program works as expected. This test will start OpenOCD, -Picocom, and GDB, and will wait for the GDB program to execute specific -commands. This test will fail if any of the processes fail, or if the GDB -program does not execute the expected commands. - -OpenOCD: A program that communicates with the FPGA over JTAG. When it starts - it will \"interrogate\" the JTAG chain - making sure it can read our - CPU's ID. After that, it will open a GDB server on port 3333. - -Picocom: A program that communicates with the FPGA over UART. - -GDB: GNU Debugger. This program will connect to the OpenOCD server and is able - to, amongst other things, load programs, set break points, and step - through code. --} -case_testGdbProgram :: Assertion -case_testGdbProgram = do - startOpenOcdPath <- getOpenOcdStartPath - startPicocomPath <- getPicocomStartPath - uartDev <- getUartDev - gdbScriptPath <- getGdbScriptPath - - withAnnotatedGdbScriptPath gdbScriptPath $ \gdbProgPath -> do - let - openOcdProc = (proc startOpenOcdPath []){std_err = CreatePipe} - picocomProc = (proc startPicocomPath [uartDev]){std_out = CreatePipe, std_in = CreatePipe} - gdbProc = (proc "gdb" ["--command", gdbProgPath]){std_out = CreatePipe, std_err = CreatePipe} - - -- Wait until we see "Halting processor", fail if we see an error - waitForHalt s - | "Error:" `isPrefixOf` s = Stop (Error ("Found error in OpenOCD output: " <> s)) - | "Halting processor" `isPrefixOf` s = Stop Ok - | otherwise = Continue - - withCreateProcess openOcdProc $ \_ _ (fromJust -> openOcdStdErr) _ -> do - hSetBuffering openOcdStdErr LineBuffering - expectLine openOcdStdErr waitForHalt - - -- XXX: Picocom doesn't immediately clean up after closing, because it - -- spawns as a child of the shell (start.sh). We could use 'exec' to - -- make sure the intermediate shell doesn't exist, but this causes - -- the whole test program to exit with signal 15 (??????). - withCreateProcess picocomProc $ \maybePicocomStdIn maybePicocomStdOut _ _ -> do - let - picocomStdIn = fromJust maybePicocomStdIn - picocomStdOut = fromJust maybePicocomStdOut - - hSetBuffering picocomStdIn LineBuffering - hSetBuffering picocomStdOut LineBuffering - - waitForLine picocomStdOut "Terminal ready" - - withCreateProcess gdbProc $ \_ (fromJust -> gdbStdOut) _ _ -> do - -- Wait for GDB to program the FPGA. If successful, we should see - -- "going in echo mode" in the picocom output. - hSetBuffering gdbStdOut LineBuffering - waitForLine picocomStdOut "Going in echo mode!" - - -- Wait for GDB to reach its last command - where it will wait indefinitely - waitForLine gdbStdOut "> continue" - - -- Test UART echo - hPutStrLn picocomStdIn "Hello, UART!" - waitForLine picocomStdOut "Hello, UART!" - -main :: IO () -main = withArgs ["--timeout", "2m"] $(defaultMainGenerator) diff --git a/bittide-instances/imgs/reducePins.drawio b/bittide-instances/imgs/reducePins.drawio deleted file mode 100644 index bb39a90b8..000000000 --- a/bittide-instances/imgs/reducePins.drawio +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bittide-instances/imgs/reducePins.svg b/bittide-instances/imgs/reducePins.svg deleted file mode 100644 index f21909829..000000000 --- a/bittide-instances/imgs/reducePins.svg +++ /dev/null @@ -1 +0,0 @@ -
shift register
shift register
design under test
design under test
shift register
shift register
xor tree
xor tree
reg
reg
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/bittide-instances/src/Bittide/Instances/Domains.hs b/bittide-instances/src/Bittide/Instances/Domains.hs deleted file mode 100644 index 8ea5ddfda..000000000 --- a/bittide-instances/src/Bittide/Instances/Domains.hs +++ /dev/null @@ -1,66 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# OPTIONS_GHC -Wno-orphans #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-} - -module Bittide.Instances.Domains where - -import Clash.Explicit.Prelude hiding (PeriodToCycles) - -import Bittide.Arithmetic.Time -import Bittide.ClockControl -import Data.Proxy - -{- ORMOLU_DISABLE -} -createDomain vXilinxSystem{vName="Basic50", vPeriod= hzToPeriod 50e6} -createDomain vXilinxSystem{vName="Basic125", vPeriod= hzToPeriod 125e6} -createDomain vXilinxSystem{vName="Basic125A", vPeriod= hzToPeriod 125e6} -createDomain vXilinxSystem{vName="Basic125B", vPeriod= hzToPeriod 125e6} -createDomain vXilinxSystem{vName="Basic199", vPeriod=hzToPeriod 199e6} -createDomain vXilinxSystem{vName="Basic200", vPeriod=hzToPeriod 200e6} -createDomain vXilinxSystem{vName="Basic625", vPeriod=hzToPeriod 625e6, vResetKind=Asynchronous} - -createDomain vXilinxSystem{vName="Ext125", vPeriod= hzToPeriod 125e6, vResetKind=Asynchronous} -createDomain vXilinxSystem{vName="Ext200", vPeriod=hzToPeriod 200e6, vResetKind=Asynchronous} - -createDomain vXilinxSystem{vName="GthRx", vPeriod=hzToPeriod 125e6} -createDomain vXilinxSystem{vName="GthTx", vPeriod= hzToPeriod 125e6} -createDomain vXilinxSystem{vName="GthRxS", vPeriod=hzToPeriod 10e9} -createDomain vXilinxSystem{vName="GthTxS", vPeriod= hzToPeriod 10e9} -{- ORMOLU_ENABLE -} - -type CccBufferSize = 25 :: Nat -type CccStabilityCheckerMargin = 25 :: Nat -type CccStabilityCheckerFramesize dom = PeriodToCycles dom (Seconds 2) -type CccReframingWaitTime dom = PeriodToCycles dom (Seconds 5) - -{- | Clock configuration used for instances. - -Compared to 'defClockConfig' this configuration has an increased -buffer size ('CccBufferSize'), disables reframing and uses more "human" -values for framesize and wait time. --} -instancesClockConfig :: - forall dom. - (KnownDomain dom) => - Proxy dom -> - ClockControlConfig - dom - CccBufferSize - CccStabilityCheckerMargin - (CccStabilityCheckerFramesize dom) -instancesClockConfig Proxy = - ClockControlConfig - { cccSettleCycles = settleCycles self - , cccSettlePeriod = microseconds 1 - , cccBufferSize = SNat - , cccStabilityCheckerMargin = SNat - , cccStabilityCheckerFramesize = SNat - , cccEnableReframing = False - , -- changed from defClockConfig, which uses a fixed number of cycles independent - -- the clock speed of the domain - cccReframingWaitTime = natToNum @(PeriodToCycles dom (Seconds 1)) - } - where - self = instancesClockConfig (Proxy @dom) diff --git a/bittide-instances/src/Bittide/Instances/Hacks.hs b/bittide-instances/src/Bittide/Instances/Hacks.hs deleted file mode 100644 index 6d07d2d23..000000000 --- a/bittide-instances/src/Bittide/Instances/Hacks.hs +++ /dev/null @@ -1,78 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE MultiParamTypeClasses #-} - -module Bittide.Instances.Hacks where - -import qualified Clash.Explicit.Prelude as E -import Clash.Prelude - -{- | Chaotically distributes a single incoming bit to input bits of the given -function. Similarly, it chaotically reduces the all output bits of a function -into a single bit. Both the input and output of the function will be directly -connected to a register after applying 'reducePins'. - -This function is useful for synthesis analysis: although it feeds the circuit -with garbage values, it only needs one input and one output pin. Because all -the in and output pins of the circuit under test are directly connected to -a register, synthesis timing results will (somewhat) realistically reflect -timing capabilities. - -See 'reducePins' for a 'BitPack' version of this function. --} -reducePins# :: - forall dom m n. - (HiddenClock dom, KnownNat m, KnownNat n) => - (Signal dom (BitVector m) -> Signal dom (BitVector n)) -> - Signal dom Bit -> - Signal dom Bit -reducePins# f pin = out - where - -- Keeps a shift register of n+m bits, where at each clock cycle the output - -- of this function and the input of this function are shifted in. The upper - -- @m@ bits of the shift register are fed to the given function, while the - -- lower @n@ bits are 'xor'd with the output of the given function, and - -- 'reduceXor'd to a single bit. - -- - -- This should be enough to throw off any optimization a synthesis tool might - -- try to do. - -- - shiftRegIn = liftA2 (.<<+) shiftReg (xor <$> pin <*> out) - shiftReg = E.delay @dom @(BitVector (m + n)) clk ena 0 shiftRegIn - out = go <$> outs <*> E.delay clk ena 0 (f ins) - (ins, outs) = unbundle (split @_ @m @n <$> shiftReg) - go a b = reduceXor (a `xor` b) - - clk = hasClock - ena = enableGen - -{- | Chaotically distributes a single incoming bit to input bits of the given -function. Similarly, it chaotically reduces the all output bits of a function -into a single bit. Both the input and output of the function will be directly -connected to a register after applying 'reducePins'. - -This function is useful for synthesis analysis: although it feeds the circuit -with garbage values, it only needs one input and one output pin. Because all -the in and output pins of the circuit under test are directly connected to -a register, synthesis timing results will (somewhat) realistically reflect -timing capabilities. --} -reducePins :: - forall dom a b. - (HiddenClock dom, BitPack a, BitPack b) => - (Signal dom a -> Signal dom b) -> - Signal dom Bit -> - Signal dom Bit -reducePins f = reducePins# (fmap pack . f . fmap unpack) - --- | Bundled version of 'reducePins'. -reducePinsB :: - forall dom a b. - (HiddenClock dom, Bundle a, BitPack a, BitPack b, Bundle b) => - (Unbundled dom a -> Unbundled dom b) -> - Signal dom Bit -> - Signal dom Bit -reducePinsB f = reducePins (bundle . f . unbundle) diff --git a/bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs b/bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs deleted file mode 100644 index c62a586ff..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs +++ /dev/null @@ -1,189 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} - -{- | Checks whether `+` and `-` work as expected, though its real purpose is to -check whether we can run hardware-in-the-loop tests. --} -module Bittide.Instances.Hitl.BoardTest where - -import Clash.Explicit.Prelude - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Cores.Xilinx.Extra (ibufds) -import Clash.Cores.Xilinx.Ila - -import Bittide.Hitl ( - HitlTestCase (HitlTestCase), - HitlTestGroup (..), - hitlVio, - hitlVioBool, - paramForHwTargets, - testCasesFromEnum, - ) -import Bittide.Instances.Domains (Ext125) -import Bittide.Instances.Hitl.Setup (allHwTargets) - -type TestStart = Bool -data TestState = Busy | Done TestSuccess -data TestSuccess = TestFailed | TestSuccess deriving (Generic, Eq, NFDataX) - -data Test = A | B deriving (Generic, Eq, Show, BitPack, Bounded, Enum, ShowX) - -toDoneSuccess :: TestState -> (Bool, Bool) -toDoneSuccess Busy = (False, False) -toDoneSuccess (Done s) = (True, s == TestSuccess) - -data CheckState n - = CsChecking (Index n) - | CsDone TestSuccess - deriving (Generic, NFDataX) - -check :: - forall n a b c dom. - ( KnownDomain dom - , KnownNat n - , Eq c - ) => - Clock dom -> - Reset dom -> - (a -> b -> c) -> - Vec n (a, b, c) -> - Signal dom TestState -check clk rst dut stimuli = - mealy clk rst enableGen go (CsChecking 0 :: CheckState n) (pure ()) - where - go :: CheckState n -> () -> (CheckState n, TestState) - go s@(CsDone testSuccess) () = (s, Done testSuccess) - go (CsChecking n) () = (s, Busy) - where - (a, b, c) = stimuli !! n - testResult = dut a b == c - s - | not testResult = CsDone TestFailed - | n == maxBound = CsDone TestSuccess - | otherwise = CsChecking (n + 1) - -{- | Testing circuit for `plus`. Feeds the circuit with inputs and checks -the received output against the expected output. --} -boardTestSimple :: - "CLK_125MHZ" ::: DiffClock Ext125 -> - "" - ::: Signal - Ext125 - ( "done" ::: Bool - , "success" ::: Bool - ) -boardTestSimple diffClk = bundle (testDone, testSuccess) - where - clk = ibufds diffClk - rst = unsafeFromActiveLow testStart - - testState = check clk rst (+) stimuli - (testDone, testSuccess) = unbundle $ toDoneSuccess <$> testState - - testStart = hitlVioBool clk testDone testSuccess - - stimuli :: Vec 4 (Unsigned 8, Unsigned 8, Unsigned 8) - stimuli = - ( (0, 0, 0) - :> (1, 2, 3) - :> (255, 0, 255) - :> (255, 1, 0) - :> Nil - ) - -makeTopEntity 'boardTestSimple - -{- | Testing circuit for `plus` and `minus`. Feeds the circuit with inputs and -checks the received output against the expected output. --} -boardTestExtended :: - "CLK_125MHZ" ::: DiffClock Ext125 -> - "" - ::: Signal - Ext125 - ( "done" ::: Bool - , "success" ::: Bool - ) -boardTestExtended diffClk = hwSeqX boardTestIla $ bundle (testDone, testSuccess) - where - clk = ibufds diffClk - rstA = unsafeFromActiveLow testStartA - rstB = unsafeFromActiveLow testStartB - - testStateA = check clk rstA (+) stimuliA - testStateB = check clk rstB (-) stimuliB - - (testDone, testSuccess) = - unbundle $ toDoneSuccess <$> mux testStartA testStateA testStateB - - testStartA = testStartAB .==. pure (Just A) - testStartB = testStartAB .==. pure (Just B) - - testStartAB = hitlVio A clk testDone testSuccess - - boardTestIla :: Signal Ext125 () - boardTestIla = - setName @"boardTestIla" - $ ila - ( ilaConfig - $ "trigger_AorB" - :> "capture" - :> "ilaTestStartA" - :> "ilaTestStartB" - :> "ilaTestDone" - :> "ilaTestSuccess" - :> Nil - ) - clk - -- Trigger when starting either test - (testStartA .||. testStartB) - -- Always capture - (pure True :: Signal Ext125 Bool) - -- Debug probes - testStartA - testStartB - testDone - testSuccess - - stimuliA :: Vec 4 (Unsigned 8, Unsigned 8, Unsigned 8) - stimuliA = - ( (0, 0, 0) - :> (1, 2, 3) - :> (255, 0, 255) - :> (255, 1, 0) - :> Nil - ) - stimuliB :: Vec 4 (Unsigned 8, Unsigned 8, Unsigned 8) - stimuliB = - ( (0, 0, 0) - :> (3, 2, 1) - :> (255, 0, 255) - :> (0, 1, 255) - :> Nil - ) - -makeTopEntity 'boardTestExtended - -testSimple :: HitlTestGroup -testSimple = - HitlTestGroup - { topEntity = 'boardTestSimple - , extraXdcFiles = [] - , externalHdl = [] - , testCases = [HitlTestCase "Simple" (paramForHwTargets allHwTargets ()) ()] - , mPostProc = Nothing - } - -testExtended :: HitlTestGroup -testExtended = - HitlTestGroup - { topEntity = 'boardTestExtended - , extraXdcFiles = [] - , externalHdl = [] - , testCases = testCasesFromEnum @Test allHwTargets () - , mPostProc = Just "post-board-test-extended" - } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Ethernet.hs b/bittide-instances/src/Bittide/Instances/Hitl/Ethernet.hs deleted file mode 100644 index f818ed43f..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/Ethernet.hs +++ /dev/null @@ -1,94 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} - -module Bittide.Instances.Hitl.Ethernet where - -import Bittide.Hitl -import Bittide.Instances.Domains -import Bittide.Instances.Pnr.Ethernet -import Clash.Cores.Xilinx.Ethernet.Gmii -import Clash.Explicit.Prelude -import Clash.Xilinx.ClockGen -import VexRiscv - -vexRiscvTcpTest :: - DiffClock Ext125 -> - Reset Ext125 -> - DiffClock Basic625 -> - ( Signal Basic125B JtagIn - , Signal Basic125B Bit - , Signal Basic625 Lvds - ) -> - ( Signal Basic125B JtagOut - , Signal Basic125B Bit - , Signal Basic625 Lvds - ) -vexRiscvTcpTest diffClk cpuReset sgmiiClk inp = testStarted `hwSeqX` (j, u, s) - where - (sysClk, sysRst) = clockWizardDifferential diffClk cpuReset - (j, u, s, _) = vexRiscEthernet sysClk sysRst sgmiiClk inp - - testStarted :: Signal Basic125B Bool - testStarted = hitlVioBool sysClk testStarted (pure True) -{-# OPAQUE vexRiscvTcpTest #-} -{-# ANN - vexRiscvTcpTest - ( Synthesize - { t_name = "vexRiscEthernet" - , t_inputs = - [ PortProduct - "CLK_125MHZ" - [PortName "p", PortName "n"] - , PortName "CPU_RESET" - , PortProduct - "SGMIICLK" - [PortName "p", PortName "n"] - , PortProduct - "" - [ PortProduct - "JTAG" - [PortName "TCK", PortName "TMS", PortName "TDI"] - , PortName "USB_UART_TXD" - , PortProduct - "SGMII" - [PortName "RX_p", PortName "RX_n"] - ] - ] - , t_output = - PortProduct - "" - [ PortProduct - "JTAG" - [ PortName "TDO" - , PortName "RST" - ] - , PortName "USB_UART_RXD" - , PortProduct - "SGMII" - [PortName "TX_p", PortName "TX_n"] - ] - } - ) - #-} - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'vexRiscvTcpTest - , extraXdcFiles = - ["jtag_config.xdc", "jtag_pmod1.xdc", "sgmii.xdc"] - , externalHdl = - [ "$env(VERILOG_ETHERNET_SRC)/rtl/*.v" - , "$env(VERILOG_ETHERNET_SRC)/lib/axis/rtl/*.v" - ] - , testCases = - [ HitlTestCase - { name = "VexRiscvTcp" - , parameters = paramForSingleHwTarget (HwTargetByIndex 7) () - , postProcData = () - } - ] - , mPostProc = Just "post-vex-riscv-tcp-test" - } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs b/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs deleted file mode 100644 index f9c617255..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs +++ /dev/null @@ -1,226 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} - -{- | A couple of tests testing clock board programming, and subsequently the -FINC and FDEC pins. --} -module Bittide.Instances.Hitl.FincFdec where - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Cores.Xilinx.Extra (ibufds) -import Clash.Explicit.Prelude -import Clash.Prelude (withClockResetEnable) -import Clash.Xilinx.ClockGen (clockWizardDifferential) - -import Bittide.ClockControl ( - SpeedChange (NoChange, SlowDown, SpeedUp), - speedChangeToFincFdec, - ) -import Bittide.ClockControl.Si539xSpi (ConfigState (Finished), si539xSpi) -import Bittide.Counter (domainDiffCounter) -import Bittide.Hitl ( - HitlTestGroup (..), - HwTargetRef (HwTargetByIndex), - hitlVio, - testCasesFromEnum, - ) -import Bittide.Instances.Domains - -import Data.Maybe (isJust) - -import qualified Bittide.ClockControl.Si5395J as Si5395J - -data TestState = Busy | Fail | Success -data Test - = -- | Keep pressing FDEC, see if counter falls below certain threshold - FDec - | -- | Keep pressing FINC, see if counter exceeds certain threshold - FInc - | -- | 'FDec' test followed by an 'FInc' one - FDecInc - | -- | 'FInc' test followed by an 'FDec' one - FIncDec - deriving (Enum, Generic, NFDataX, Bounded, BitPack, ShowX, Show) - -{- | Counter threshold after which a test is considered passed/failed. In theory -clocks can diverge at +-20 kHz (at 200 MHz), which gives the tests 500 ms to -adjust their clocks - which should be plenty. --} -threshold :: Signed 32 -threshold = 20_000 - -testStateToDoneSuccess :: TestState -> (Bool, Bool) -testStateToDoneSuccess = \case - Busy -> (False, False) - Fail -> (True, False) - Success -> (True, True) - -goFincFdecTests :: - Clock Basic200 -> - Reset Basic200 -> - Clock Ext200 -> - Signal Basic200 Test -> - "MISO" ::: Signal Basic200 Bit -> -- SPI - "" - ::: ( Signal Basic200 TestState - , -- Freq increase / freq decrease request to clock board - "" - ::: ( "FINC" ::: Signal Basic200 Bool - , "FDEC" ::: Signal Basic200 Bool - ) - , -- SPI to clock board: - "" - ::: ( "SCLK" ::: Signal Basic200 Bool - , "MOSI" ::: Signal Basic200 Bit - , "CSB" ::: Signal Basic200 Bool - ) - , -- Debug signals: - "" - ::: ( "SPI_BUSY" ::: Signal Basic200 Bool - , "SPI_STATE" ::: Signal Basic200 (BitVector 40) - , "SI_LOCKED" ::: Signal Basic200 Bool - , "COUNTER_ACTIVE" ::: Signal Basic200 Bool - , "COUNTER" ::: Signal Basic200 (Signed 32) - ) - ) -goFincFdecTests clk rst clkControlled testSelect miso = - (testResult, fIncDec, spiOut, debugSignals) - where - debugSignals = (spiBusy, pack <$> spiState, siClkLocked, counterActive, counter) - - (_, spiBusy, spiState@(fmap (== Finished) -> siClkLocked), spiOut) = - withClockResetEnable clk rst enableGen - $ si539xSpi - Si5395J.testConfig6_200_on_0a_10ppb_and_1 - (SNat @(Microseconds 1)) - (pure Nothing) - miso - - rstTest = unsafeFromActiveLow siClkLocked - rstControlled = convertReset clk clkControlled rst - - (counter, counterActive) = - unbundle - $ - -- Note that in a "real" Bittide system the clocks would be wired up the - -- other way around: the controlled domain would be the target domain. We - -- don't do that here because we know 'rstControlled' will come out of - -- reset much earlier than 'rstTest'. Doing it the "proper" way would - -- therefore introduce extra complexity, without adding to the test's - -- coverage. - domainDiffCounter clkControlled rstControlled clk rstTest - - fIncDec = unbundle $ speedChangeToFincFdec clk rstTest fIncDecRequest - - (fIncDecRequest, testResult) = - unbundle - $ (!!) - <$> bundle (fDecResult :> fIncResult :> fDecIncResult :> fIncDecResult :> Nil) - <*> fmap fromEnum testSelect - - fDecResult = goFdec <$> counter - fIncResult = goFinc <$> counter - fDecIncResult = mealy clk rstTest enableGen goFdecFinc FDec counter - fIncDecResult = mealy clk rstTest enableGen goFincFdec FInc counter - - -- Keep pressing FDEC, expect counter to go below -@threshold@ - goFdec :: Signed 32 -> (SpeedChange, TestState) - goFdec n - | n > threshold = (NoChange, Fail) - | n < -threshold = (NoChange, Success) - | otherwise = (SlowDown, Busy) - - -- Keep pressing FINC, expect counter to go above @threshold@ - goFinc :: Signed 32 -> (SpeedChange, TestState) - goFinc n - | n > threshold = (NoChange, Success) - | n < -threshold = (NoChange, Fail) - | otherwise = (SpeedUp, Busy) - - -- Keep pressing FDEC, expect counter to go below -@threshold@, then keep pressing - -- FINC, expect counter to go above 0. - goFdecFinc :: Test -> Signed 32 -> (Test, (SpeedChange, TestState)) - goFdecFinc FDec n - | n > threshold = (FDec, (NoChange, Fail)) - | n < -threshold = (FInc, (NoChange, Busy)) - | otherwise = (FDec, (SlowDown, Busy)) - goFdecFinc FInc n - | n > 0 = (FInc, (NoChange, Success)) - | n < -(3 * threshold) = (FInc, (NoChange, Fail)) - | otherwise = (FInc, (SpeedUp, Busy)) - goFdecFinc s _ = (s, (NoChange, Fail)) -- Illegal state - - -- Keep pressing FINC, expect counter to go above @threshold@, then keep pressing - -- FDEC, expect counter to go below 0. - goFincFdec :: Test -> Signed 32 -> (Test, (SpeedChange, TestState)) - goFincFdec FInc n - | n > threshold = (FDec, (NoChange, Busy)) - | n < -threshold = (FInc, (NoChange, Fail)) - | otherwise = (FInc, (SpeedUp, Busy)) - goFincFdec FDec n - | n > (3 * threshold) = (FDec, (NoChange, Fail)) - | n < 0 = (FDec, (NoChange, Success)) - | otherwise = (FDec, (SlowDown, Busy)) - goFincFdec s _ = (s, (NoChange, Fail)) -- Illegal state - -fincFdecTests :: - -- Pins from internal oscillator: - "CLK_125MHZ" ::: DiffClock Ext125 -> - -- Pins from clock board: - "USER_SMA_CLOCK" ::: DiffClock Ext200 -> - "MISO" ::: Signal Basic200 Bit -> -- SPI - "" - ::: ( "" - ::: ( "done" ::: Signal Basic200 Bool - , "success" ::: Signal Basic200 Bool - ) - , -- Freq increase / freq decrease request to clock board - "" - ::: ( "FINC" ::: Signal Basic200 Bool - , "FDEC" ::: Signal Basic200 Bool - ) - , -- SPI to clock board: - "" - ::: ( "SCLK" ::: Signal Basic200 Bool - , "MOSI" ::: Signal Basic200 Bit - , "CSB" ::: Signal Basic200 Bool - ) - ) -fincFdecTests diffClk controlledDiffClock spiIn = - ((testDone, testSuccess), fIncDec, spiOut) - where - clkControlled = ibufds controlledDiffClock - - (clk, clkStableRst) = clockWizardDifferential diffClk noReset - - started = isJust <$> testInput - testRst = orReset clkStableRst (unsafeFromActiveLow started) - - (testResult, fIncDec, spiOut, _debugSignals) = - goFincFdecTests clk testRst clkControlled (fromJustX <$> testInput) spiIn - - (testDone, testSuccess) = unbundle $ testStateToDoneSuccess <$> testResult - - -- For debugging, add to separate VIO? - -- clkStable1 = unsafeToActiveLow clkStableRst - -- testRstBool = unsafeToActiveHigh testRst - -- (fInc, fDec) = fIncDec - -- (spiBusy, spiState, siClkLocked, counterActive, counter) = debugSignals - - testInput :: Signal Basic200 (Maybe Test) - testInput = hitlVio FDec clk testDone testSuccess -{-# NOINLINE fincFdecTests #-} -makeTopEntity 'fincFdecTests - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'fincFdecTests - , extraXdcFiles = [] - , externalHdl = [] - , testCases = testCasesFromEnum @Test [HwTargetByIndex 7] () - , mPostProc = Nothing - } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs deleted file mode 100644 index d10db92a8..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs +++ /dev/null @@ -1,533 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -{- | Test whether clock boards are configurable and transceiver links come -online. If they do, run clock control and wait for the clocks to stabilize. -This assumes to run on a fully connected mesh of 8 FPGAs. Also see -'c_CHANNEL_NAMES' and 'c_CLOCK_PATHS'. It has two tricks up its sleeve: - - 1. It uses @SYNC_IN@/@SYNC_OUT@ to make sure each board starts programming - its clock boards at the same time. - - 2. It keeps track of how many times the GTH's reset manager had to reset - the connection and how often it lost connections after establishing - them. - -This test will succeed if all clocks have been stable for 5 seconds. Note: -this doesn't test reframing yet. --} -module Bittide.Instances.Hitl.FullMeshHwCc ( - fullMeshHwCcWithRiscvTest, - fullMeshHwCcTest, - clockControlConfig, - fullMeshHwCcWithRiscvTest', - fullMeshHwCcTest', -) where - -import Clash.Explicit.Prelude hiding (PeriodToCycles) -import qualified Clash.Explicit.Prelude as E -import Clash.Prelude (withClockResetEnable) - -import Data.Maybe (fromMaybe) -import Data.Proxy -import Language.Haskell.TH (runIO) -import LiftType (liftTypeQ) -import System.FilePath - -import Bittide.Arithmetic.Time -import Bittide.ClockControl -import Bittide.ClockControl.Callisto -import Bittide.ClockControl.Callisto.Util (FDEC, FINC, speedChangeToPins, stickyBits) -import Bittide.ClockControl.Registers (clockControlWb) -import Bittide.ClockControl.Si539xSpi (ConfigState (Error, Finished), si539xSpi) -import Bittide.Counter -import Bittide.DoubleBufferedRam ( - ContentType (Blob), - InitialContent (Reloadable), - RegisterWritePriority (CircuitPriority), - registerWb, - ) -import Bittide.ElasticBuffer (sticky) -import Bittide.Hitl -import Bittide.Instances.Domains -import Bittide.ProcessingElement (PeConfig (..), processingElement) -import Bittide.ProcessingElement.Util (memBlobsFromElf) -import Bittide.SharedTypes (ByteOrder (BigEndian), Bytes) -import Bittide.Simulate.Config (CcConf (..)) -import Bittide.Topology (TopologyType (..)) -import Bittide.Transceiver (transceiverPrbsN) - -import Bittide.Instances.Hitl.HwCcTopologies (commonSpiConfig) -import Bittide.Instances.Hitl.IlaPlot -import Bittide.Instances.Hitl.Setup -import Project.FilePath - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Class.Counter -import Clash.Cores.Xilinx.GTH -import Clash.Cores.Xilinx.Ila (Depth (..), IlaConfig (..), ila, ilaConfig) -import Clash.Sized.Extra (unsignedToSigned) -import Clash.Xilinx.ClockGen - -import Protocols -import Protocols.Wishbone -import VexRiscv - -import qualified Bittide.Transceiver as Transceiver -import qualified Bittide.Transceiver.ResetManager as ResetManager - -clockControlConfig :: - $(case (instancesClockConfig (Proxy @Basic125)) of (_ :: t) -> liftTypeQ @t) -clockControlConfig = - $(lift (instancesClockConfig (Proxy @Basic125))) - -{- | Instantiates a RiscV core that copies instructions coming from a hardware -implementation of Callisto (see 'fullMeshHwTest') and copies it to a register -tied to FINC/FDEC. --} -fullMeshRiscvCopyTest :: - forall dom. - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Signal dom (CallistoResult LinkCount) -> - Vec LinkCount (Signal dom (RelDataCount 32)) -> - -- Freq increase / freq decrease request to clock board - ( "FINC" ::: Signal dom Bool - , "FDEC" ::: Signal dom Bool - ) -fullMeshRiscvCopyTest clk rst callistoResult dataCounts = unbundle fIncDec - where - (_, fIncDec) = - toSignals - ( circuit $ \jtag -> do - [wbA, wbB] <- - withClockResetEnable clk rst enableGen $ processingElement @dom peConfig -< jtag - fIncDecCallisto -< wbA - (fIncDec, _allStable) <- - withClockResetEnable clk rst enableGen - $ clockControlWb margin framesize (pure $ complement 0) dataCounts - -< wbB - idC -< fIncDec - ) - (pure $ JtagIn low low low, pure ()) - - fIncDecCallisto :: - forall aw nBytes. - (KnownNat aw, nBytes ~ 4) => - Circuit - (Wishbone dom 'Standard aw (Bytes nBytes)) - () - fIncDecCallisto = Circuit goFIncDecCallisto - where - goFIncDecCallisto (wbM2S, _) = (wbS2M, ()) - where - (_, wbS2M) = - withClockResetEnable clk rst enableGen - $ registerWb - CircuitPriority - (0 :: Bytes nBytes, 0 :: Bytes nBytes) - wbM2S - (fmap (fmap ((,0) . extend . pack)) fincfdec) - - fincfdec :: Signal dom (Maybe SpeedChange) - fincfdec = - clearOnAck - <$> fmap acknowledge wbS2M - <*> fmap maybeSpeedChange callistoResult - - -- Clear register if register is read from (or written to, but we assume - -- this doesn't happen). This makes sure the RiscV doesn't read the same - -- result from the hardware clock control twice. - clearOnAck :: ("ACK" ::: Bool) -> Maybe SpeedChange -> Maybe SpeedChange - clearOnAck False maybeSpeedChange = maybeSpeedChange - clearOnAck True (Just speedChange) = Just speedChange - clearOnAck True Nothing = Just NoChange - - margin = d2 - - framesize = SNat @(PeriodToCycles dom (Seconds 1)) - - (iMem, dMem) = - $( do - root <- runIO $ findParentContaining "cabal.project" - let - elfDir = root firmwareBinariesDir "riscv32imc-unknown-none-elf" Release - elfPath = elfDir "clock-control-reg-cpy" - iSize = 64 * 1024 -- 64 KB - dSize = 64 * 1024 -- 64 KB - memBlobsFromElf BigEndian (Just iSize, Just dSize) elfPath Nothing - ) - - {- - 0b10xxxxx_xxxxxxxx 0b10 0x8x instruction memory - 0b01xxxxx_xxxxxxxx 0b01 0x4x data memory - 0b00xxxxx_xxxxxxxx 0b00 0x0x FINC/FDEC register - 0b11xxxxx_xxxxxxxx 0b11 0xCx memory mapped hardware clock control - -} - peConfig = - PeConfig - (0b10 :> 0b01 :> 0b00 :> 0b11 :> Nil) - (Reloadable $ Blob iMem) - (Reloadable $ Blob dMem) - -{- | Instantiates a hardware implementation of Callisto and exports its results. Can -be used to drive FINC/FDEC directly (see @FINC_FDEC@ result) or to tie the -results to a RiscV core (see 'fullMeshRiscvCopyTest') --} -fullMeshHwTest :: - "SMA_MGT_REFCLK_C" ::: Clock Ext200 -> - "SYSCLK" ::: Clock Basic125 -> - "ILA_CTRL" ::: IlaControl Basic125 -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "FINC_FDEC" ::: Signal Basic125 (FINC, FDEC) - , "CALLISTO_RESULT" ::: Signal Basic125 (CallistoResult LinkCount) - , "CALLISTO_RESET" ::: Reset Basic125 - , "DATA_COUNTERS" ::: Vec LinkCount (Signal Basic125 (RelDataCount 32)) - , "stats" ::: Vec LinkCount (Signal Basic125 ResetManager.Statistics) - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - , "transceiversFailedAfterUp" ::: Signal Basic125 Bool - , "ALL_READY" ::: Signal Basic125 Bool - , "ALL_STABLE" ::: Signal Basic125 Bool - ) -fullMeshHwTest refClk sysClk IlaControl{syncRst = rst, ..} rxNs rxPs miso = - fincFdecIla - `hwSeqX` ( transceivers.txNs - , transceivers.txPs - , frequencyAdjustments - , callistoResult - , clockControlReset - , domainDiffs - , transceivers.stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable0 - ) - where - syncRst = rst `orReset` (unsafeFromActiveLow (fmap not spiErr)) - - -- Clock programming - spiDone = E.dflipflop sysClk $ (== Finished) <$> spiState - spiErr = E.dflipflop sysClk $ isErr <$> spiState - - isErr (Error _) = True - isErr _ = False - - (_, _, spiState, spiOut) = - withClockResetEnable sysClk syncRst enableGen - $ si539xSpi commonSpiConfig (SNat @(Microseconds 10)) (pure Nothing) miso - - -- Transceiver setup - gthAllReset = unsafeFromActiveLow spiDone - - transceivers = - transceiverPrbsN - @GthTx - @GthRx - @Ext200 - @Basic125 - @GthTxS - @GthRxS - Transceiver.defConfig - Transceiver.Inputs - { clock = sysClk - , reset = gthAllReset - , refClock = refClk - , channelNames - , clockPaths - , rxNs - , rxPs - , txDatas = repeat (pure 0) - , txReadys = repeat (pure False) - , rxReadys = repeat (pure True) - } - - allReady = - trueFor (SNat @(Milliseconds 500)) sysClk syncRst (and <$> bundle transceivers.linkReadys) - transceiversFailedAfterUp = - sticky sysClk syncRst (isFalling sysClk syncRst enableGen False allReady) - - timeSucc = countSucc @(Unsigned 16, Index (PeriodToCycles Basic125 (Milliseconds 1))) - timer = register sysClk syncRst enableGen (0, 0) (timeSucc <$> timer) - milliseconds1 = fst <$> timer - - -- Clock control - clockControlReset = - orReset (unsafeFromActiveLow allReady) - $ orReset - (unsafeFromActiveHigh transceiversFailedAfterUp) - (unsafeFromActiveLow syncStart) - - availableLinkMask = pure maxBound - - (clockMod, _stabilities, allStable0, _allCentered) = - unbundle - $ fmap - (\CallistoResult{..} -> (maybeSpeedChange, stability, allStable, allSettled)) - callistoResult - - callistoResult = - callistoClockControlWithIla @LinkCount @CccBufferSize - (head transceivers.txClocks) - sysClk - clockControlReset - clockControlConfig - IlaControl{..} - availableLinkMask - (fmap (fmap resize) domainDiffs) - - -- Capture every 100 microseconds - this should give us a window of about 5 - -- seconds. Or: when we're in reset. If we don't do the latter, the VCDs get - -- very confusing. - capture = (captureFlag .&&. allReady) .||. unsafeToActiveHigh syncRst - - fincFdecIla :: Signal Basic125 () - fincFdecIla = - setName @"fincFdecIla" - $ ila - ( ilaConfig - $ "trigger_0" - :> "capture_0" - :> "probe_milliseconds" - :> "probe_allStable0" - :> "probe_transceiversFailedAfterUp" - :> "probe_nFincs" - :> "probe_nFdecs" - :> "probe_net_nFincs" - :> Nil - ) - { depth = D16384 - } - sysClk - -- Trigger as soon as we come out of reset - (unsafeToActiveLow syncRst) - capture - -- Debug probes - milliseconds1 - allStable0 - transceiversFailedAfterUp - nFincs - nFdecs - (fmap unsignedToSigned nFincs - fmap unsignedToSigned nFdecs) - - captureFlag = - riseEvery - sysClk - syncRst - enableGen - (SNat @(PeriodToCycles Basic125 (Milliseconds 1))) - - nFincs = - regEn - sysClk - clockControlReset - enableGen - (0 :: Unsigned 32) - ((== Just SpeedUp) <$> clockMod) - (satSucc SatBound <$> nFincs) - - nFdecs = - regEn - sysClk - clockControlReset - enableGen - (0 :: Unsigned 32) - ((== Just SlowDown) <$> clockMod) - (satSucc SatBound <$> nFdecs) - - frequencyAdjustments :: Signal Basic125 (FINC, FDEC) - frequencyAdjustments = - E.delay sysClk enableGen minBound {- glitch filter -} - $ withClockResetEnable sysClk clockControlReset enableGen - $ stickyBits @Basic125 d20 (speedChangeToPins . fromMaybe NoChange <$> clockMod) - - domainDiffs = - domainDiffCounterExt sysClk clockControlReset - <$> transceivers.rxClocks - <*> transceivers.txClocks - --- | Top entity for this test. See module documentation for more information. -fullMeshHwCcWithRiscvTest :: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "" - ::: ( "FINC" ::: Signal Basic125 Bool - , "FDEC" ::: Signal Basic125 Bool - ) - , "SYNC_OUT" ::: Signal Basic125 Bool - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -fullMeshHwCcWithRiscvTest refClkDiff sysClkDiff syncIn rxns rxps miso = - (txns, txps, (riscvFinc, riscvFdec), syncOut, spiDone, spiOut) - where - refClk = ibufds_gte3 refClkDiff :: Clock Ext200 - - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - - ilaControl@IlaControl{..} = ilaPlotSetup IlaPlotSetup{..} - - ( txns - , txps - , _hwFincFdecs - , callistoResult - , callistoReset - , dataCounts - , _stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable - ) = fullMeshHwTest refClk sysClk ilaControl rxns rxps miso - - (riscvFinc, riscvFdec) = - fullMeshRiscvCopyTest sysClk callistoReset callistoResult dataCounts - - -- check that tests are not synchronously start before all - -- transceivers are up - startBeforeAllReady = - sticky - sysClk - syncRst - (startTest .&&. syncStart .&&. ((not <$> allReady) .||. transceiversFailedAfterUp)) - - endSuccess :: Signal Basic125 Bool - endSuccess = trueFor (SNat @(Seconds 5)) sysClk syncRst allStable - - done = endSuccess .||. transceiversFailedAfterUp .||. startBeforeAllReady - success = not <$> (transceiversFailedAfterUp .||. startBeforeAllReady) - - startTest :: Signal Basic125 Bool - startTest = hitlVioBool sysClk done success - -makeTopEntity 'fullMeshHwCcWithRiscvTest - --- | Top entity for this test. See module documentation for more information. -fullMeshHwCcTest :: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "" - ::: ( "FINC" ::: Signal Basic125 Bool - , "FDEC" ::: Signal Basic125 Bool - ) - , "SYNC_OUT" ::: Signal Basic125 Bool - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -fullMeshHwCcTest refClkDiff sysClkDiff syncIn rxns rxps miso = - (txns, txps, unbundle hwFincFdecs, syncOut, spiDone, spiOut) - where - refClk = ibufds_gte3 refClkDiff :: Clock Ext200 - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - ilaControl@IlaControl{..} = ilaPlotSetup IlaPlotSetup{..} - - ( txns - , txps - , hwFincFdecs - , _callistoResult - , _callistoReset - , _dataCounts - , _stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable - ) = fullMeshHwTest refClk sysClk ilaControl rxns rxps miso - - -- check that tests are not synchronously start before all - -- transceivers are up - startBeforeAllReady = - sticky - sysClk - syncRst - (syncStart .&&. ((not <$> allReady) .||. transceiversFailedAfterUp)) - - endSuccess :: Signal Basic125 Bool - endSuccess = trueFor (SNat @(Seconds 5)) sysClk syncRst allStable - - startTest :: Signal Basic125 Bool - startTest = - hitlVioBool - sysClk - -- done - (endSuccess .||. transceiversFailedAfterUp .||. startBeforeAllReady) - -- success - (not <$> (transceiversFailedAfterUp .||. startBeforeAllReady)) - -makeTopEntity 'fullMeshHwCcTest - -mkTest :: ClashTargetName -> HitlTestGroup -mkTest topEntity = - HitlTestGroup - { topEntity = topEntity - , extraXdcFiles = [] - , externalHdl = [] - , testCases = - [ HitlTestCase - { name = "CC" - , parameters = paramForHwTargets allHwTargets () - , postProcData = - def - { ccTopologyType = Complete (natToInteger @FpgaCount) - , samples = 1000 - , duration = natToNum @(PeriodToCycles Basic125 (Seconds 60)) - , stabilityMargin = snatToNum cccStabilityCheckerMargin - , stabilityFrameSize = snatToNum cccStabilityCheckerFramesize - , reframe = cccEnableReframing - , waitTime = fromEnum cccReframingWaitTime - , clockOffsets = Nothing - , startupDelays = toList $ repeat @FpgaCount 0 - } - } - ] - , mPostProc = Nothing - } - where - ClockControlConfig{..} = clockControlConfig - -fullMeshHwCcWithRiscvTest' :: HitlTestGroup -fullMeshHwCcWithRiscvTest' = mkTest 'fullMeshHwCcWithRiscvTest - -fullMeshHwCcTest' :: HitlTestGroup -fullMeshHwCcTest' = mkTest 'fullMeshHwCcTest diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs deleted file mode 100644 index 85e200de9..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs +++ /dev/null @@ -1,697 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -{- | Test whether clock boards are configurable and transceiver links come -online. If they do, run clock control in software and wait for the clocks to -stabilize. This assumes to run on a fully connected mesh of 8 FPGAs. Also see -'Bittide.Instances.Hitl.Setup'. It has two tricks up its sleeve: - - 1. It uses @SYNC_IN@/@SYNC_OUT@ to make sure each board starts programming - its clock boards at the same time. - - 2. It keeps track of how many times the GTH's reset manager had to reset - the connection and how often it lost connections after establishing - them. - -This test will succeed if all clocks have been stable for 5 seconds. Note: -this doesn't test reframing yet. --} -module Bittide.Instances.Hitl.FullMeshSwCc ( - fullMeshSwCcTest, - clockControlConfig, - tests, -) where - -import Clash.Explicit.Prelude hiding (PeriodToCycles) -import qualified Clash.Explicit.Prelude as E -import Clash.Prelude (withClockResetEnable) - -import Data.Maybe (fromMaybe) -import Data.Proxy -import Data.String (fromString) -import Language.Haskell.TH (runIO) -import LiftType (liftTypeQ) -import System.FilePath - -import Bittide.Arithmetic.Time -import Bittide.ClockControl -import Bittide.ClockControl.Callisto -import Bittide.ClockControl.Callisto.Util (FDEC, FINC, speedChangeToPins, stickyBits) -import Bittide.ClockControl.Registers (clockControlWb) -import Bittide.ClockControl.Si539xSpi (ConfigState (Error, Finished), si539xSpi) -import Bittide.Counter -import Bittide.DoubleBufferedRam (ContentType (Blob), InitialContent (Reloadable)) -import Bittide.ElasticBuffer (Overflow, Underflow, resettableXilinxElasticBuffer, sticky) -import Bittide.Hitl -import Bittide.Instances.Domains -import Bittide.ProcessingElement (PeConfig (..), processingElement) -import Bittide.ProcessingElement.Util (memBlobsFromElf) -import Bittide.SharedTypes (ByteOrder (BigEndian)) -import Bittide.Simulate.Config (CcConf (..)) -import Bittide.Topology (TopologyType (..)) -import Bittide.Transceiver (transceiverPrbsN) - -import Bittide.Instances.Hitl.HwCcTopologies (commonSpiConfig) -import Bittide.Instances.Hitl.IlaPlot -import Bittide.Instances.Hitl.Setup hiding (FpgaCount, LinkCount) -import Project.FilePath - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Class.Counter -import Clash.Cores.Xilinx.GTH -import Clash.Cores.Xilinx.Ila (Depth (..), IlaConfig (..), ila, ilaConfig) -import Clash.Cores.Xilinx.Xpm.Cdc (xpmCdcSingle) -import Clash.Cores.Xilinx.Xpm.Cdc.Handshake.Extra (xpmCdcMaybeLossy) -import Clash.Sized.Extra (unsignedToSigned) -import Clash.Sized.Vector.ToTuple (vecToTuple) -import Clash.Xilinx.ClockGen - -import Protocols -import VexRiscv - -import qualified Bittide.Transceiver as Transceiver -import qualified Bittide.Transceiver.ResetManager as ResetManager - -type FpgaCount = 8 -type LinkCount = FpgaCount - 1 - -clockControlConfig :: - $(case (instancesClockConfig (Proxy @Basic125)) of (_ :: t) -> liftTypeQ @t) -clockControlConfig = - $(lift (instancesClockConfig (Proxy @Basic125))) - --- | Instantiates a RiscV core -fullMeshRiscvTest :: - forall dom. - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Vec LinkCount (Signal dom (RelDataCount 32)) -> - -- Freq increase / freq decrease request to clock board - ( "FINC" ::: Signal dom Bool - , "FDEC" ::: Signal dom Bool - ) -fullMeshRiscvTest clk rst dataCounts = unbundle fIncDec - where - (_, fIncDec) = - toSignals - ( circuit $ \jtag -> do - [wbB] <- withClockResetEnable clk rst enableGen $ processingElement @dom peConfig -< jtag - (fIncDec, _allStable) <- - withClockResetEnable clk rst enableGen - $ clockControlWb margin framesize (pure $ complement 0) dataCounts - -< wbB - idC -< fIncDec - ) - (pure $ JtagIn low low low, pure ()) - - margin = d2 - - framesize = SNat @(PeriodToCycles dom (Seconds 1)) - - (iMem, dMem) = - $( do - root <- runIO $ findParentContaining "cabal.project" - let - elfDir = root firmwareBinariesDir "riscv32imc-unknown-none-elf" Release - elfPath = elfDir "clock-control" - iSize = 64 * 1024 -- 64 KB - dSize = 64 * 1024 -- 64 KB - memBlobsFromElf BigEndian (Just iSize, Just dSize) elfPath Nothing - ) - - {- - 0b10xxxxx_xxxxxxxx 0b10 0x8x instruction memory - 0b01xxxxx_xxxxxxxx 0b01 0x4x data memory - 0b11xxxxx_xxxxxxxx 0b11 0xCx memory mapped hardware clock control - -} - peConfig = - PeConfig - (0b10 :> 0b01 :> 0b11 :> Nil) - (Reloadable $ Blob iMem) - (Reloadable $ Blob dMem) - -type FifoSize = 5 -- = 2^5 = 32 - --- | Instantiates a hardware implementation of Callisto and exports its results. -fullMeshHwTest :: - "SMA_MGT_REFCLK_C" ::: Clock Ext200 -> - "SYSCLK" ::: Clock Basic125 -> - "ILA_CTRL" ::: IlaControl Basic125 -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "FINC_FDEC" ::: Signal Basic125 (FINC, FDEC) - , "CALLISTO_RESULT" ::: Signal Basic125 (CallistoResult LinkCount) - , "CALLISTO_RESET" ::: Reset Basic125 - , "DATA_COUNTERS" ::: Vec LinkCount (Signal Basic125 (RelDataCount 32)) - , "stats" ::: Vec LinkCount (Signal Basic125 ResetManager.Statistics) - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - , "transceiversFailedAfterUp" ::: Signal Basic125 Bool - , "ALL_UP" ::: Signal Basic125 Bool - , "ALL_STABLE" ::: Signal Basic125 Bool - , "ugnsStable" ::: Vec LinkCount (Signal Basic125 Bool) - ) -fullMeshHwTest refClk sysClk IlaControl{syncRst = rst, ..} rxNs rxPs miso = - fincFdecIla - `hwSeqX` ( transceivers.txNs - , transceivers.txPs - , frequencyAdjustments - , callistoResult - , clockControlReset - , domainDiffs - , transceivers.stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable1 - , map (fmap (\(_, _, x, _) -> x)) freeUgnDatas - ) - where - syncRst = rst `orReset` (unsafeFromActiveLow (fmap not spiErr)) - - -- Clock programming - spiDone = E.dflipflop sysClk $ (== Finished) <$> spiState - spiErr = E.dflipflop sysClk $ isErr <$> spiState - - isErr (Error _) = True - isErr _ = False - - (_, _, spiState, spiOut) = - withClockResetEnable sysClk syncRst enableGen - $ si539xSpi commonSpiConfig (SNat @(Microseconds 10)) (pure Nothing) miso - - -- Transceiver setup - gthAllReset = unsafeFromActiveLow spiDone - - transceivers = - transceiverPrbsN - @GthTx - @GthRx - @Ext200 - @Basic125 - @GthTxS - @GthRxS - @LinkCount - Transceiver.defConfig - Transceiver.Inputs - { clock = sysClk - , reset = gthAllReset - , refClock = refClk - , channelNames = takeI channelNames - , clockPaths = takeI clockPaths - , rxNs - , rxPs - , txDatas = txCounters - , txReadys = txAllStables - , rxReadys = repeat (pure True) - } - txAllStables = zipWith (xpmCdcSingle sysClk) transceivers.txClocks (repeat allStable1) - allStable1 = sticky sysClk syncRst allStable0 - txResets2 = - zipWith - orReset - transceivers.txResets - (map unsafeFromActiveLow txAllStables) - - txCounters = zipWith txCounter transceivers.txClocks txResets2 - txCounter txClk txRst = result - where - result = register txClk txRst enableGen (0xaabbccddeeff1234 :: BitVector 64) (result + 1) - -- see NOTE [magic start values] - - -- rxFifos :: Vec LinkCount (_, _, _, _, _Signal GthRx (Maybe (BitVector 64))) - rxFifos = - zipWith4 - go - transceivers.txClocks - transceivers.rxClocks - txResets2 - transceivers.rxDatas - where - go rClk wClk rRst = - resettableXilinxElasticBuffer @FifoSize @_ @_ @(Maybe (BitVector 64)) rClk wClk rRst - - (fillLvls, fifoUnderflowsTx, fifoOverflowsTx, _ebMode, rxCntrs) = unzip5 rxFifos - - fifoOverflowsFree :: Vec LinkCount (Signal Basic125 Overflow) - fifoOverflowsFree = zipWith (flip xpmCdcSingle sysClk) transceivers.txClocks fifoOverflowsTx - fifoUnderflowsFree :: Vec LinkCount (Signal Basic125 Underflow) - fifoUnderflowsFree = zipWith (flip xpmCdcSingle sysClk) transceivers.txClocks fifoUnderflowsTx - - ugns :: Vec LinkCount (Signal GthTx (BitVector 64)) - ugns = - zipWith - (-) - txCounters - (map (fmap (fromMaybe 0x1122334411223344)) rxCntrs) - -- see NOTE [magic start values] - - -- NOTE [magic start values] - -- These values could be anything, but are chosen to be recognisable and help debugging. - -- 0xaabbccddeeff1234 - 0x1122334411223344 = 0x99999999dddcdef0 - -- If you ever see the ugn being a constant 0x99999999dddcdef0 - -- then you know the your counter isn't running and you're receiving 'Nothing', - -- If you see 0x99999999.......... and it's counting up, then you're receiving Nothing, - -- but your counter is running. - - ugnStable1sec = zipWith3 (stableForMs (SNat @1000)) transceivers.txClocks transceivers.txResets ugns - - freeUgnDatas = zipWith5 go transceivers.txClocks (repeat sysClk) ugns fillLvls ugnStable1sec - where - go clkIn clkOut ugn fillLvl stable = - regMaybe - clkOut - noReset - enableGen - (0, 0, False, unpack 0) - (xpmCdcMaybeLossy clkIn clkOut inp) - where - fillStat = fillStats clkIn noReset fillLvl - inp = (fmap Just $ bundle (ugn, fillLvl, stable, fillStat)) - - ( ugnD0 - , ugnD1 - , ugnD2 - , ugnD3 - , ugnD4 - , ugnD5 - , ugnD6 - ) = vecToTuple freeUgnDatas - (ugn0, fill0, ugnStable0, fillStats0) = unbundle ugnD0 - (ugn1, fill1, ugnStable1, fillStats1) = unbundle ugnD1 - (ugn2, fill2, ugnStable2, fillStats2) = unbundle ugnD2 - (ugn3, fill3, ugnStable3, fillStats3) = unbundle ugnD3 - (ugn4, fill4, ugnStable4, fillStats4) = unbundle ugnD4 - (ugn5, fill5, ugnStable5, fillStats5) = unbundle ugnD5 - (ugn6, fill6, ugnStable6, fillStats6) = unbundle ugnD6 - - FillStats fillMin0 fillMax0 = unbundle fillStats0 - FillStats fillMin1 fillMax1 = unbundle fillStats1 - FillStats fillMin2 fillMax2 = unbundle fillStats2 - FillStats fillMin3 fillMax3 = unbundle fillStats3 - FillStats fillMin4 fillMax4 = unbundle fillStats4 - FillStats fillMin5 fillMax5 = unbundle fillStats5 - FillStats fillMin6 fillMax6 = unbundle fillStats6 - - allReady = - trueFor (SNat @(Milliseconds 500)) sysClk syncRst (and <$> bundle transceivers.linkReadys) - transceiversFailedAfterUp = - sticky sysClk syncRst (isFalling sysClk syncRst enableGen False allReady) - - timeSucc = countSucc @(Unsigned 16, Index (PeriodToCycles Basic125 (Milliseconds 1))) - timer = register sysClk syncRst enableGen (0, 0) (timeSucc <$> timer) - milliseconds1 = fst <$> timer - - -- Clock control - clockControlReset = - orReset (unsafeFromActiveLow allReady) - $ orReset - (unsafeFromActiveHigh transceiversFailedAfterUp) - (unsafeFromActiveLow syncStart) - - availableLinkMask = pure maxBound - - (clockMod, stabilities, allStable0, _allCentered) = - unbundle - $ fmap - (\CallistoResult{..} -> (maybeSpeedChange, stability, allStable, allSettled)) - callistoResult - ( stability0 - , stability1 - , stability2 - , stability3 - , stability4 - , stability5 - , stability6 - ) = vecToTuple $ unbundle stabilities - - callistoResult = - callistoClockControlWithIla @LinkCount @CccBufferSize - (head transceivers.txClocks) - sysClk - clockControlReset - clockControlConfig - IlaControl{..} - availableLinkMask - (fmap (fmap resize) domainDiffs) - - -- Capture every 100 microseconds - this should give us a window of about 5 - -- seconds. Or: when we're in reset. If we don't do the latter, the VCDs get - -- very confusing. - capture = (captureFlag .&&. allReady) .||. unsafeToActiveHigh syncRst - - fincFdecIla :: Signal Basic125 () - fincFdecIla = - setName @"fincFdecIla" - $ ila - ( ilaConfig - $ "trigger_0" - :> "capture_0" - :> "probe_milliseconds" - :> "probe_allStable0" - :> "probe_allStable1" - :> "probe_transceiversFailedAfterUp" - :> "probe_nFincs" - :> "probe_nFdecs" - :> "probe_net_nFincs" - :> "probe_ugn0" - :> "probe_ugn1" - :> "probe_ugn2" - :> "probe_ugn3" - :> "probe_ugn4" - :> "probe_ugn5" - :> "probe_ugn6" - :> "probe_fill0" - :> "probe_fill2" - :> "probe_fill1" - :> "probe_fill3" - :> "probe_fill4" - :> "probe_fill5" - :> "probe_fill6" - :> "probe_fillMin0" - :> "probe_fillMin2" - :> "probe_fillMin1" - :> "probe_fillMin3" - :> "probe_fillMin4" - :> "probe_fillMin5" - :> "probe_fillMin6" - :> "probe_fillMax0" - :> "probe_fillMax2" - :> "probe_fillMax1" - :> "probe_fillMax3" - :> "probe_fillMax4" - :> "probe_fillMax5" - :> "probe_fillMax6" - :> "stability0" - :> "stability2" - :> "stability1" - :> "stability3" - :> "stability4" - :> "stability5" - :> "stability6" - :> "ugnStable0" - :> "ugnStable1" - :> "ugnStable2" - :> "ugnStable3" - :> "ugnStable4" - :> "ugnStable5" - :> "ugnStable6" - :> "probe_linkReadys" - :> "probe_linkUps" - :> "fifoUnderflows" - :> "fifoOverflows" - :> Nil - ) - { depth = D16384 - } - sysClk - -- Trigger as soon as we come out of reset - (unsafeToActiveLow syncRst) - capture - -- Debug probes - milliseconds1 - allStable0 - allStable1 - transceiversFailedAfterUp - nFincs - nFdecs - (fmap unsignedToSigned nFincs - fmap unsignedToSigned nFdecs) - ugn0 - ugn1 - ugn2 - ugn3 - ugn4 - ugn5 - ugn6 - fill0 - fill1 - fill2 - fill3 - fill4 - fill5 - fill6 - fillMin0 - fillMin1 - fillMin2 - fillMin3 - fillMin4 - fillMin5 - fillMin6 - fillMax0 - fillMax1 - fillMax2 - fillMax3 - fillMax4 - fillMax5 - fillMax6 - stability0 - stability1 - stability2 - stability3 - stability4 - stability5 - stability6 - ugnStable0 - ugnStable1 - ugnStable2 - ugnStable3 - ugnStable4 - ugnStable5 - ugnStable6 - (bundle transceivers.linkReadys) - (bundle transceivers.linkUps) - ((pack . reverse) <$> bundle fifoUnderflowsFree) - ((pack . reverse) <$> bundle fifoOverflowsFree) - - captureFlag = - riseEvery - sysClk - syncRst - enableGen - (SNat @(PeriodToCycles Basic125 (Milliseconds 1))) - - nFincs = - regEn - sysClk - clockControlReset - enableGen - (0 :: Unsigned 32) - ((== Just SpeedUp) <$> clockMod) - (satSucc SatBound <$> nFincs) - - nFdecs = - regEn - sysClk - clockControlReset - enableGen - (0 :: Unsigned 32) - ((== Just SlowDown) <$> clockMod) - (satSucc SatBound <$> nFdecs) - - frequencyAdjustments :: Signal Basic125 (FINC, FDEC) - frequencyAdjustments = - E.delay sysClk enableGen minBound {- glitch filter -} - $ withClockResetEnable sysClk clockControlReset enableGen - $ stickyBits @Basic125 d20 (speedChangeToPins . fromMaybe NoChange <$> clockMod) - - domainDiffs = - domainDiffCounterExt sysClk clockControlReset - <$> transceivers.rxClocks - <*> transceivers.txClocks - -{- | Tracks the min/max values of the input during the last milliseconds - -Updates once per millisecond. --} -fillStats :: - forall dom a. - (KnownDomain dom, Ord a, Num a, Bounded a, NFDataX a) => - Clock dom -> - Reset dom -> - Signal dom a -> - Signal dom (FillStats a) -fillStats clk rst = moore clk rst enableGen go (\(_, _, x) -> x) (maxBound, mempty, mempty) - where - go :: - (IndexMs dom 1, FillStats a, FillStats a) -> - a -> - (IndexMs dom 1, FillStats a, FillStats a) - go (cntr, prevStats, out) inp - | cntr == 0 = (maxBound, mempty, new) - | otherwise = (cntr - 1, new, out) - where - new = mappend prevStats (FillStats inp inp) - -data FillStats a = FillStats {fillMin :: a, fillMax :: a} - deriving (Generic, NFDataX, BitPack) - -instance Bundle (FillStats a) where - type Unbundled dom (FillStats a) = FillStats (Signal dom a) - bundle (FillStats sigMin sigMax) = liftA2 FillStats sigMin sigMax - unbundle x = FillStats{fillMin = fmap fillMin x, fillMax = fmap fillMax x} - -instance (Ord a) => Semigroup (FillStats a) where - a <> b = - FillStats - { fillMin = min (fillMin a) (fillMin b) - , fillMax = max (fillMax a) (fillMax b) - } - -instance (Bounded a, Ord a) => Monoid (FillStats a) where - mempty = FillStats{fillMin = maxBound, fillMax = minBound} - -{- | Counts how many cycles the input signal has been stable - -Stable means equal to its previous value according to the 'Eq' instance. -The 'BitPack' instance is only used as a convenient way of intialization, -it resets to a previous value of @unpack 0@. --} -stableFor :: - forall n dom a. - (KnownNat n, KnownDomain dom, Eq a, BitPack a, NFDataX a) => - Clock dom -> - Reset dom -> - Signal dom a -> - Signal dom (Unsigned n) -stableFor clk rst = moore clk rst enableGen go snd (unpack 0, 0) - where - go :: (a, Unsigned n) -> a -> (a, Unsigned n) - go (prev, cntr) inp - | inp == prev = (prev, satSucc SatBound cntr) - | otherwise = (inp, 0) - --- | Wrapper around 'stableFor' that checks the input has been stable for atleast @ms@ milliseconds -stableForMs :: - forall ms dom a. - (KnownNat ms, KnownDomain dom, Eq a, BitPack a, NFDataX a) => - SNat ms -> - Clock dom -> - Reset dom -> - Signal dom a -> - Signal dom Bool -stableForMs SNat clk rst inp = - liftA2 (>=) stable (snatToNum (SNat @(PeriodToCycles dom (Milliseconds ms)))) - where - stable = stableFor @(CLog 2 (PeriodToCycles dom (Milliseconds ms))) clk rst inp - --- | Top entity for this test. See module documentation for more information. -fullMeshSwCcTest :: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "" - ::: ( "FINC" ::: Signal Basic125 Bool - , "FDEC" ::: Signal Basic125 Bool - ) - , "SYNC_OUT" ::: Signal Basic125 Bool - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -fullMeshSwCcTest refClkDiff sysClkDiff syncIn rxns rxps miso = - (txns, txps, (riscvFinc, riscvFdec), syncOut, spiDone, spiOut) - where - refClk = ibufds_gte3 refClkDiff :: Clock Ext200 - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - ilaControl@IlaControl{syncRst, syncOut, syncStart} = ilaPlotSetup IlaPlotSetup{..} - - ( txns - , txps - , _hwFincFdecs - , _callistoResult - , callistoReset - , dataCounts - , _stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable - , ugnsStable - ) = fullMeshHwTest refClk sysClk ilaControl rxns rxps miso - - (riscvFinc, riscvFdec) = - fullMeshRiscvTest sysClk callistoReset dataCounts - - allUgnsStable = fmap and $ bundle ugnsStable - - -- checks that tests are not synchronously start before all - -- transceivers are up - startBeforeAllReady = - sticky - sysClk - syncRst - (syncStart .&&. ((not <$> allReady) .||. transceiversFailedAfterUp)) - - endSuccess :: Signal Basic125 Bool - endSuccess = trueFor (SNat @(Seconds 5)) sysClk syncRst $ allStable .&&. allUgnsStable - - startTest :: Signal Basic125 Bool - startTest = - hitlVioBool - sysClk - -- done - (endSuccess .||. transceiversFailedAfterUp .||. startBeforeAllReady) - -- success - (allUgnsStable .&&. not <$> (transceiversFailedAfterUp .||. startBeforeAllReady)) - -makeTopEntity 'fullMeshSwCcTest - -testsToRun :: Int -testsToRun = 1 - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'fullMeshSwCcTest - , extraXdcFiles = [] - , externalHdl = [] - , testCases = - [ HitlTestCase - { name = "CC" <> fromString (show n) - , parameters = paramForHwTargets allHwTargets () - , postProcData = - def - { ccTopologyType = Complete (natToInteger @FpgaCount) - , samples = 1000 - , duration = natToNum @(PeriodToCycles Basic125 (Seconds 60)) - , stabilityMargin = snatToNum cccStabilityCheckerMargin - , stabilityFrameSize = snatToNum cccStabilityCheckerFramesize - , reframe = cccEnableReframing - , waitTime = fromEnum cccReframingWaitTime - , clockOffsets = Nothing - , startupDelays = toList $ repeat @FpgaCount 0 - } - } - | n <- [0 .. testsToRun - 1] - ] - , mPostProc = Nothing - } - where - ClockControlConfig{..} = clockControlConfig diff --git a/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs b/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs deleted file mode 100644 index 55688f4a7..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs +++ /dev/null @@ -1,924 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -{- | Test whether clock boards are configurable and transceiver links come -online. If they do, run clock control and wait for the clocks to stabilize. -Also see 'Bittide.Instances.Hitl.Setup'. It has two tricks up its -sleeve: - - 1. It uses @SYNC_IN@/@SYNC_OUT@ to make sure each board starts programming - its clock boards at the same time. - - 2. It keeps track of how many times the GTH's reset manager had to reset - the connection and how often it lost connections after establishing - them. - -This test will succeed if all clocks have been stable for 5 seconds. Note: -this doesn't test reframing yet. --} -module Bittide.Instances.Hitl.HwCcTopologies ( - hwCcTopologyWithRiscvTest, - hwCcTopologyTest, - clockControlConfig, - commonSpiConfig, - tests, -) where - -import Clash.Explicit.Prelude hiding (PeriodToCycles) -import qualified Clash.Explicit.Prelude as E -import Clash.Prelude (withClockResetEnable) - -import Data.Bifunctor (bimap) -import Data.Functor ((<&>)) -import Data.Maybe (fromMaybe, isJust) -import Data.Proxy -import GHC.Float.RealFracMethods (roundFloatInteger) -import Language.Haskell.TH (runIO) -import LiftType (liftTypeQ) -import System.FilePath - -import Bittide.Arithmetic.PartsPer (PartsPer, ppm) -import Bittide.Arithmetic.Time -import Bittide.ClockControl -import Bittide.ClockControl.Callisto -import Bittide.ClockControl.Callisto.Util (FDEC, FINC, speedChangeToPins, stickyBits) -import Bittide.ClockControl.Registers (clockControlWb) -import Bittide.ClockControl.Si5395J -import Bittide.ClockControl.Si539xSpi (ConfigState (Error, Finished), si539xSpi) -import Bittide.Counter -import Bittide.DoubleBufferedRam ( - ContentType (Blob), - InitialContent (Reloadable), - RegisterWritePriority (CircuitPriority), - registerWb, - ) -import Bittide.ElasticBuffer (sticky) -import Bittide.Instances.Domains -import Bittide.ProcessingElement (PeConfig (..), processingElement) -import Bittide.ProcessingElement.Util (memBlobsFromElf) -import Bittide.SharedTypes (ByteOrder (BigEndian), Bytes) -import Bittide.Simulate.Config (CcConf (..)) -import Bittide.Topology -import Bittide.Transceiver (transceiverPrbsN) - -import Bittide.Hitl - -import Bittide.Instances.Hitl.IlaPlot -import Bittide.Instances.Hitl.Setup -import Project.FilePath - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Class.Counter -import Clash.Cores.Xilinx.GTH -import Clash.Cores.Xilinx.Ila (Depth (..), IlaConfig (..), ila, ilaConfig) -import Clash.Sized.Extra (unsignedToSigned) -import Clash.Xilinx.ClockGen - -import Protocols hiding (SimulationConfig) -import Protocols.Wishbone -import VexRiscv - -import qualified Bittide.Arithmetic.PartsPer as PartsPer -import qualified Bittide.Transceiver as Transceiver -import qualified Bittide.Transceiver.ResetManager as ResetManager -import qualified Data.Map.Strict as Map (fromList) - -type AllStablePeriod = Seconds 5 - -{- | The number of FINCs (if positive) or FDECs (if negative) applied -prior to the test start leading to some desired initial clock -offset. --} -type FincFdecCount = Signed 32 - -{- | The number of clock cycles to wait before starting clock control -according to the local, but stable system clock of a node. --} -type StartupDelay = Unsigned 32 - --- | Availabe step size configurations. -data StepSizeSelect - = PPB_1 - | PPB_10 - | PPB_100 - | PPB_500 - | PPM_1 - deriving (Generic, NFDataX, BitPack, Eq, Enum, Bounded, Show) - --- | Calibration stages -data CCCalibrationStage - = -- | Apply previously measured clock offsets. - NoCCCalibration - | -- | Measure \"natural\" clock offsets. - CCCalibrate - | -- | Verify that the clocks still have similar \"natural\" offsets as when - -- they were calibrated. If this is not the case, the clock frequencies - -- shifted during the test, invalidating the results. Also see - -- 'acceptableNoiseLevel'. - CCCalibrationValidation - deriving (Generic, NFDataX, BitPack, Eq, Enum, Bounded, Show) - -{- | The step size, as it is used by all tests. Note that changing the -step size for individual tests requires recalibration of the clock -offsets, which is why we fix it to a single and common value here. --} -commonStepSizeSelect :: StepSizeSelect -commonStepSizeSelect = - -- Don't forget to update the value of f_step this value in "Callisto.hs" and - -- "callisto.rs". - PPB_100 - -commonStepSizePartsPer :: PartsPer -commonStepSizePartsPer = case commonStepSizeSelect of - PPB_1 -> PartsPer.ppb 1 - PPB_10 -> PartsPer.ppb 10 - PPB_100 -> PartsPer.ppb 100 - PPB_500 -> PartsPer.ppb 500 - PPM_1 -> PartsPer.ppm 1 - -partsPerToSteps :: PartsPer -> FincFdecCount -partsPerToSteps = - fromIntegral . roundFloatInteger . PartsPer.toSteps commonStepSizePartsPer - -commonSpiConfig :: TestConfig6_200_on_0a_RegisterMap -commonSpiConfig = case commonStepSizeSelect of - PPB_1 -> testConfig6_200_on_0a_1ppb - PPB_10 -> testConfig6_200_on_0a_10ppb - PPB_100 -> testConfig6_200_on_0a_100ppb - PPB_500 -> testConfig6_200_on_0a_500ppb - PPM_1 -> testConfig6_200_on_0a_1ppm - -{- | Accepted noise between the inital clock control calibration run -and the last calibration verifiction run. --} -acceptableNoiseLevel :: FincFdecCount -acceptableNoiseLevel = 6 - -disabled :: TestConfig -disabled = - TestConfig - { fpgaEnabled = False - , calibrate = NoCCCalibration - , initialClockShift = Nothing - , startupDelay = 0 - , mask = 0 - } - --- | The test configuration. -data TestConfig = TestConfig - { fpgaEnabled :: Bool - -- ^ Enables or disables an FPGA depending on the selected - -- topology. Disabled FPGAs immediediatly succeed after the test - -- start. - -- - -- Also note that the flag only disables clock control, while - -- other functionality, as for example SYNC_IN/SYNC_OUT time - -- synchronization, needs to stay alive. - , calibrate :: CCCalibrationStage - -- ^ Indicates the selected calibration stage. - , initialClockShift :: Maybe FincFdecCount - -- ^ Artificical clock shift applied prior to the test start, expressed as - -- number of FINCs (if positive) or FDECs (if negative). - , startupDelay :: StartupDelay - -- ^ Some intial startup delay given in the number of clock - -- cycles of the stable clock. - , mask :: BitVector LinkCount - -- ^ The link mask depending on the selected topology. - } - deriving (Generic, NFDataX, BitPack, Show) - -clockControlConfig :: - $(case (instancesClockConfig (Proxy @Basic125)) of (_ :: t) -> liftTypeQ @t) -clockControlConfig = - $(lift (instancesClockConfig (Proxy @Basic125))) - -{- | Instantiates a RiscV core that copies instructions coming from a hardware -implementation of Callisto (see 'topologyTest') and copies it to a register -tied to FINC/FDEC. --} -riscvCopyTest :: - forall dom. - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Signal dom (CallistoResult LinkCount) -> - Vec LinkCount (Signal dom (RelDataCount 32)) -> - -- Freq increase / freq decrease request to clock board - ( "FINC" ::: Signal dom Bool - , "FDEC" ::: Signal dom Bool - ) -riscvCopyTest clk rst callistoResult dataCounts = unbundle fIncDec - where - (_, fIncDec) = - toSignals - ( circuit $ \jtag -> do - [wbA, wbB] <- - withClockResetEnable clk rst enableGen $ processingElement @dom peConfig -< jtag - fIncDecCallisto -< wbA - (fIncDec, _allStable) <- - withClockResetEnable clk rst enableGen - $ clockControlWb margin framesize (pure $ complement 0) dataCounts - -< wbB - idC -< fIncDec - ) - (pure $ JtagIn low low low, pure ()) - - fIncDecCallisto :: - forall aw nBytes. - (KnownNat aw, nBytes ~ 4) => - Circuit - (Wishbone dom 'Standard aw (Bytes nBytes)) - () - fIncDecCallisto = Circuit goFIncDecCallisto - where - goFIncDecCallisto (wbM2S, _) = (wbS2M, ()) - where - (_, wbS2M) = - withClockResetEnable clk rst enableGen - $ registerWb - CircuitPriority - (0 :: Bytes nBytes, 0 :: Bytes nBytes) - wbM2S - (fmap (fmap ((,0) . extend . pack)) fincfdec) - - fincfdec :: Signal dom (Maybe SpeedChange) - fincfdec = - clearOnAck - <$> fmap acknowledge wbS2M - <*> fmap maybeSpeedChange callistoResult - - -- Clear register if register is read from (or written to, but we assume - -- this doesn't happen). This makes sure the RiscV doesn't read the same - -- result from the hardware clock control twice. - clearOnAck :: ("ACK" ::: Bool) -> Maybe SpeedChange -> Maybe SpeedChange - clearOnAck False maybeSpeedChange = maybeSpeedChange - clearOnAck True (Just speedChange) = Just speedChange - clearOnAck True Nothing = Just NoChange - - margin = d2 - - framesize = SNat @(PeriodToCycles dom (Seconds 1)) - - (iMem, dMem) = - $( do - root <- runIO $ findParentContaining "cabal.project" - let - elfDir = root firmwareBinariesDir "riscv32imc-unknown-none-elf" Release - elfPath = elfDir "clock-control-reg-cpy" - memBlobsFromElf BigEndian (Nothing, Nothing) elfPath Nothing - ) - - {- - 0b10xxxxx_xxxxxxxx 0b10 0x8x instruction memory - 0b01xxxxx_xxxxxxxx 0b01 0x4x data memory - 0b00xxxxx_xxxxxxxx 0b00 0x0x FINC/FDEC register - 0b11xxxxx_xxxxxxxx 0b11 0xCx memory mapped hardware clock control - -} - peConfig = - PeConfig - (0b10 :> 0b01 :> 0b00 :> 0b11 :> Nil) - (Reloadable $ Blob iMem) - (Reloadable $ Blob dMem) - -{- | Instantiates a hardware implementation of Callisto and exports its results. Can -be used to drive FINC/FDEC directly (see @FINC_FDEC@ result) or to tie the -results to a RiscV core (see 'riscvCopyTest') --} -topologyTest :: - "SMA_MGT_REFCLK_C" ::: Clock Ext200 -> - "SYSCLK" ::: Clock Basic125 -> - "SYSRST" ::: Reset Basic125 -> - "ILA_CTRL" ::: IlaControl Basic125 -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - "TEST_CFG" ::: Signal Basic125 TestConfig -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "FINC_FDEC" ::: Signal Basic125 (FINC, FDEC) - , "CALLISTO_RESULT" ::: Signal Basic125 (CallistoResult LinkCount) - , "CALLISTO_RESET" ::: Reset Basic125 - , "DATA_COUNTERS" ::: Vec LinkCount (Signal Basic125 (RelDataCount 32)) - , "stats" ::: Vec LinkCount (Signal Basic125 ResetManager.Statistics) - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - , "transceiversFailedAfterUp" ::: Signal Basic125 Bool - , "ALL_READY" ::: Signal Basic125 Bool - , "ALL_STABLE" ::: Signal Basic125 Bool - , "CALIB_I" ::: Signal Basic125 FincFdecCount - , "CALIB_E" ::: Signal Basic125 FincFdecCount - ) -topologyTest refClk sysClk sysRst IlaControl{syncRst = rst, ..} rxNs rxPs miso cfg = - fincFdecIla - `hwSeqX` ( transceivers.txNs - , transceivers.txPs - , frequencyAdjustments - , callistoResult - , clockControlReset - , domainDiffs - , transceivers.stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable0 - , calibratedClockShift - , validationClockShift - ) - where - syncRst = rst `orReset` unsafeFromActiveHigh spiErr - - -- Clock board programming - spiDone = E.dflipflop sysClk $ (== Finished) <$> spiState - spiErr = E.dflipflop sysClk $ isErr <$> spiState - - isErr (Error _) = True - isErr _ = False - - (_, _, spiState, spiOut) = - withClockResetEnable sysClk syncRst enableGen - $ si539xSpi commonSpiConfig (SNat @(Microseconds 10)) (pure Nothing) miso - - -- Transceiver setup - gthAllReset = unsafeFromActiveLow clocksAdjusted - - transceivers = - transceiverPrbsN - @GthTx - @GthRx - @Ext200 - @Basic125 - @GthTxS - @GthRxS - Transceiver.defConfig - Transceiver.Inputs - { clock = sysClk - , reset = gthAllReset - , refClock = refClk - , channelNames - , clockPaths - , rxNs - , rxPs - , txDatas = repeat (pure 0) - , txReadys = repeat (pure False) - , rxReadys = repeat (pure True) - } - - allReady = - trueFor (SNat @(Milliseconds 500)) sysClk syncRst (and <$> bundle transceivers.linkReadys) - transceiversFailedAfterUp = - sticky sysClk syncRst (isFalling sysClk syncRst enableGen False allReady) - - timeSucc = countSucc @(Unsigned 16, Index (PeriodToCycles Basic125 (Milliseconds 1))) - timer = register sysClk syncRst enableGen (0, 0) (timeSucc <$> timer) - milliseconds1 = fst <$> timer - - -- Startup delay - startupDelayRst = - orReset (unsafeFromActiveLow clocksAdjusted) - $ orReset (unsafeFromActiveLow allReady) - $ orReset - (unsafeFromActiveHigh transceiversFailedAfterUp) - (unsafeFromActiveLow syncStart) - - delayCount = - register sysClk startupDelayRst enableGen (0 :: StartupDelay) - $ (\c s -> if c < s then satSucc SatBound c else c) - <$> delayCount - <*> (startupDelay <$> cfg) - - -- Clock control - clockControlReset = - startupDelayRst - `orReset` unsafeFromActiveLow ((==) <$> delayCount <*> (startupDelay <$> cfg)) - - (clockMod, _stabilities, allStable0, _allCentered) = - unbundle - $ fmap - (\CallistoResult{..} -> (maybeSpeedChange, stability, allStable, allSettled)) - callistoResult - - callistoResult = - callistoClockControlWithIla @LinkCount @CccBufferSize - (head transceivers.txClocks) - sysClk - clockControlReset - clockControlConfig - IlaControl{..} - (mask <$> cfg) - (fmap (fmap resize) domainDiffs) - - -- Capture every 100 microseconds - this should give us a window of about 5 - -- seconds. Or: when we're in reset. If we don't do the latter, the VCDs get - -- very confusing. - capture = (captureFlag .&&. allReady) .||. unsafeToActiveHigh syncRst - - fincFdecIla :: Signal Basic125 () - fincFdecIla = - setName @"fincFdecIla" - ila - ( ilaConfig - $ "trigger_0" - :> "capture_0" - :> "probe_milliseconds" - :> "probe_allStable0" - :> "probe_transceiversFailedAfterUp" - :> "probe_nFincs" - :> "probe_nFdecs" - :> "probe_net_nFincs" - :> Nil - ) - { depth = D16384 - } - sysClk - -- Trigger as soon as we come out of reset - (unsafeToActiveLow syncRst) - capture - -- Debug probes - milliseconds1 - allStable0 - transceiversFailedAfterUp - nFincs - nFdecs - (fmap unsignedToSigned nFincs - fmap unsignedToSigned nFdecs) - - captureFlag = - riseEvery - sysClk - syncRst - enableGen - (SNat @(PeriodToCycles Basic125 (Milliseconds 1))) - - nFincs = - regEn - sysClk - clockControlReset - enableGen - (0 :: Unsigned 32) - ((== Just SpeedUp) <$> clockMod) - (satSucc SatBound <$> nFincs) - - nFdecs = - regEn - sysClk - clockControlReset - enableGen - (0 :: Unsigned 32) - ((== Just SlowDown) <$> clockMod) - (satSucc SatBound <$> nFdecs) - - -- Clock calibration - - clockShiftUpd = \case - Just SpeedUp -> satSucc SatBound - Just SlowDown -> satPred SatBound - _ -> id - - notInCCReset = unsafeToActiveLow clockControlReset - - clockShift = - regEn - sysClk - sysRst - enableGen - (0 :: FincFdecCount) - (notInCCReset .&&. (== CCCalibrate) . calibrate <$> cfg) - (clockShiftUpd <$> clockMod <*> clockShift) - - calibratedClockShift = - register sysClk sysRst enableGen 0 - $ mux - ( isFalling sysClk sysRst enableGen False - $ (== CCCalibrate) - . calibrate - <$> cfg - ) - clockShift - calibratedClockShift - - validationClockShift = - regEn - sysClk - sysRst - enableGen - (0 :: FincFdecCount) - (notInCCReset .&&. (== CCCalibrationValidation) . calibrate <$> cfg) - (clockShiftUpd <$> clockMod <*> validationClockShift) - - -- Initial Clock adjustment - - -- without the additional delay of 1 second here, some of the - -- initial FINC/FDECs prior to test start will be lost. - adjustStart = trueFor (SNat @(Seconds 1)) sysClk syncRst spiDone - clocksAdjusted = - spiDone - .&&. ( (/= NoCCCalibration) - . calibrate - <$> cfg - .||. (==) - <$> initialAdjust - <*> adjustCount - ) - adjusting = adjustStart .&&. (not <$> clocksAdjusted) - adjustRst = unsafeFromActiveLow adjustStart - - initialAdjust = (+) <$> calibratedClockShift <*> (fromMaybe 0 . initialClockShift <$> cfg) - - adjustCount = - regEn - sysClk - adjustRst - enableGen - (0 :: FincFdecCount) - adjusting - $ flip upd - <$> adjustCount - <*> let f = isFalling sysClk adjustRst enableGen False - in bundle $ bimap f f $ unbundle frequencyAdjustments - where - upd (True, _) = satSucc SatBound - upd (_, True) = satPred SatBound - upd _ = id - - frequencyAdjustments :: Signal Basic125 (FINC, FDEC) - frequencyAdjustments = - E.delay sysClk enableGen minBound {- glitch filter -} - $ mux - adjusting - ( speedChangeToFincFdec sysClk adjustRst - $ opSelect - <$> initialAdjust - <*> adjustCount - ) - ( withClockResetEnable sysClk clockControlReset enableGen - $ stickyBits @Basic125 d20 - $ speedChangeToPins - . fromMaybe NoChange - <$> clockMod - ) - where - opSelect calib adjust = case compare calib adjust of - LT -> SlowDown - EQ -> NoChange - GT -> SpeedUp - - domainDiffs = - domainDiffCounterExt sysClk clockControlReset - <$> transceivers.rxClocks - <*> transceivers.txClocks - --- | Top entity for this test. See module documentation for more information. -hwCcTopologyWithRiscvTest :: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "" - ::: ( "FINC" ::: Signal Basic125 Bool - , "FDEC" ::: Signal Basic125 Bool - ) - , "SYNC_OUT" ::: Signal Basic125 Bool - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -hwCcTopologyWithRiscvTest refClkDiff sysClkDiff syncIn rxns rxps miso = - (txns, txps, (riscvFinc, riscvFdec), syncOut, spiDone, spiOut) - where - refClk = ibufds_gte3 refClkDiff :: Clock Ext200 - - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - - ilaControl@IlaControl{..} = ilaPlotSetup IlaPlotSetup{..} - startTest = isJust <$> testConfig - - cfg = fromMaybe disabled <$> testConfig - - ( txns - , txps - , hwFincFdecs - , callistoResult - , callistoReset - , dataCounts - , _stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable - , calibI - , calibE - ) = - topologyTest - refClk - sysClk - sysRst - ilaControl{skipTest = skip} - rxns - rxps - miso - cfg - - (riscvFinc, riscvFdec) = - unbundle - $ mux (unsafeToActiveHigh callistoReset) hwFincFdecs - $ bundle - $ riscvCopyTest sysClk callistoReset callistoResult dataCounts - - -- check that tests are not synchronously start before all - -- transceivers are up - startBeforeAllReady = - sticky - sysClk - syncRst - (startTest .&&. syncStart .&&. ((not <$> allReady) .||. transceiversFailedAfterUp)) - - endSuccess :: Signal Basic125 Bool - endSuccess = - trueFor (SNat @AllStablePeriod) sysClk syncRst allStable - .&&. ( (/= CCCalibrationValidation) - . calibrate - <$> cfg - .||. (\i e -> abs (i - e) < acceptableNoiseLevel) - <$> calibI - <*> calibE - ) - - done = endSuccess .||. transceiversFailedAfterUp .||. startBeforeAllReady - success = not <$> (transceiversFailedAfterUp .||. startBeforeAllReady) - - skip = - register - sysClk - sysRst - enableGen - False - (maybe False (not . fpgaEnabled) <$> testConfig) - - testConfig :: Signal Basic125 (Maybe TestConfig) - testConfig = hitlVio disabled sysClk done success - -makeTopEntity 'hwCcTopologyWithRiscvTest - --- | Top entity for this test. See module documentation for more information. -hwCcTopologyTest :: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "" - ::: ( "FINC" ::: Signal Basic125 Bool - , "FDEC" ::: Signal Basic125 Bool - ) - , "SYNC_OUT" ::: Signal Basic125 Bool - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -hwCcTopologyTest refClkDiff sysClkDiff syncIn rxns rxps miso = - (txns, txps, unbundle hwFincFdecs, syncOut, spiDone, spiOut) - where - refClk = ibufds_gte3 refClkDiff :: Clock Ext200 - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - ilaControl@IlaControl{..} = ilaPlotSetup IlaPlotSetup{..} - startTest = isJust <$> testConfig - - cfg = fromMaybe disabled <$> testConfig - - ( txns - , txps - , hwFincFdecs - , _callistoResult - , _callistoReset - , _dataCounts - , _stats - , spiDone - , spiOut - , transceiversFailedAfterUp - , allReady - , allStable - , calibI - , calibE - ) = - topologyTest - refClk - sysClk - sysRst - ilaControl{skipTest = skip} - rxns - rxps - miso - cfg - - -- check that tests are not synchronously start before all - -- transceivers are up - startBeforeAllReady = - sticky - sysClk - syncRst - (syncStart .&&. ((not <$> allReady) .||. transceiversFailedAfterUp)) - - endSuccess :: Signal Basic125 Bool - endSuccess = - trueFor (SNat @(Seconds 5)) sysClk syncRst allStable - .&&. ( (/= CCCalibrationValidation) - . calibrate - <$> cfg - .||. (\i e -> abs (i - e) < acceptableNoiseLevel) - <$> calibI - <*> calibE - ) - - skip = maybe False (not . fpgaEnabled) <$> testConfig - - testConfig :: Signal Basic125 (Maybe TestConfig) - testConfig = - hitlVio - disabled - sysClk - -- done - ( startTest - .&&. (skip .||. endSuccess .||. transceiversFailedAfterUp .||. startBeforeAllReady) - ) - -- success - ( skip - .||. (allStable .&&. (not <$> (transceiversFailedAfterUp .||. startBeforeAllReady))) - ) - -makeTopEntity 'hwCcTopologyTest - -tests :: HitlTestGroup -tests = testGroup - where - m = 1_000_000 - - icsDiamond = map ppm $ -10 :> -5 :> 20 :> 30 :> Nil - sdDiamond = 0 :> 10 :> 200 :> 3 :> Nil - - icsComplete = map ppm $ -100 :> 0 :> 100 :> Nil - sdComplete = 200 :> 0 :> 200 :> Nil - - icsCyclic = map ppm $ 0 :> 5 :> 10 :> 15 :> 20 :> Nil - sdCyclic = 0 :> 10 :> 0 :> 100 :> 0 :> Nil - - icsTorus = map ppm $ -30 :> -35 :> -40 :> 40 :> 35 :> 30 :> Nil - sdTorus = 0 :> 0 :> 0 :> 100 :> 100 :> 100 :> Nil - - icsStar = map ppm $ 0 :> 10 :> -10 :> 20 :> -20 :> 30 :> -30 :> 40 :> Nil - sdStar = 0 :> 40 :> 80 :> 120 :> 160 :> 200 :> 240 :> 280 :> Nil - - icsLine = map ppm $ 100 :> 0 :> 0 :> -100 :> Nil - sdLine = 200 :> 0 :> 0 :> 200 :> Nil - - icsHourglass = map ppm $ -100 :> 100 :> -100 :> 100 :> -100 :> 100 :> Nil - sdHourglass = 0 :> 200 :> 0 :> 200 :> 0 :> 200 :> Nil - - ClockControlConfig{..} = clockControlConfig - - defSimCfg = - def - { samples = 1000 - , duration = natToNum @(PeriodToCycles Basic125 (Seconds 60)) - , stabilityMargin = snatToNum cccStabilityCheckerMargin - , stabilityFrameSize = snatToNum cccStabilityCheckerFramesize - , reframe = cccEnableReframing - , waitTime = fromEnum cccReframingWaitTime - , stopAfterStable = - Just - $ natToNum @(PeriodToCycles Basic125 AllStablePeriod) - } - - calibrateClockOffsets = calibrateCC False - validateClockOffsetCalibration = calibrateCC True - - calibrateCC :: Bool -> HitlTestCase HwTargetRef TestConfig CcConf - calibrateCC validate = - HitlTestCase - { name = (if validate then "zzz_validate" else "0_calibrate") <> "_clock_offsets" - , parameters = - Map.fromList $ allHwTargets - <&> (,TestConfig - { fpgaEnabled = True - , calibrate = - if validate - then CCCalibrationValidation - else CCCalibrate - , initialClockShift = Nothing - , startupDelay = 0 - , mask = maxBound - }) - , postProcData = - defSimCfg - { ccTopologyType = Complete $ natToInteger @FpgaCount - , clockOffsets = Nothing - , startupDelays = toList $ repeat @FpgaCount 0 - } - } - - -- tests the given topology - tt :: - forall n. - (KnownNat n, n <= FpgaCount) => - Maybe (Vec n PartsPer) -> - Vec n StartupDelay -> - Topology n -> - HitlTestCase HwTargetRef TestConfig CcConf - tt clockShifts startDelays t = - HitlTestCase - { name = topologyName t - , parameters = - Map.fromList - $ toList - ( zipWith4 - testData - indicesI - (maybeVecToVecMaybe (map partsPerToSteps <$> clockShifts)) - startDelays - (linkMasks @n t) - ) - <> [ (HwTargetByIndex (fromInteger i), disabled) - | let n = natToNum @n - , i <- [n, n + 1 .. natToNum @LinkCount] - ] - , postProcData = - defSimCfg - { ccTopologyType = topologyType t - , clockOffsets = toList <$> clockShifts - , startupDelays = fromIntegral <$> toList startDelays - } - } - - maybeVecToVecMaybe :: forall n a. (KnownNat n) => Maybe (Vec n a) -> Vec n (Maybe a) - maybeVecToVecMaybe = \case - Just v -> Just <$> v - Nothing -> repeat Nothing - - testData :: - forall n. - (KnownNat n, n <= FpgaCount) => - Index n -> - Maybe FincFdecCount -> - StartupDelay -> - BitVector LinkCount -> - (HwTargetRef, TestConfig) - testData i initialClockShift startupDelay mask = - ( HwTargetByIndex (fromIntegral i) - , TestConfig - { fpgaEnabled = True - , calibrate = NoCCCalibration - , .. - } - ) - -{- FOURMOLU_DISABLE -} -- fourmolu doesn't do well with tabular structures - testGroup = - HitlTestGroup - { topEntity = 'hwCcTopologyTest - , extraXdcFiles = [] - , externalHdl = [] - , testCases = - [ -- detect the natual clock offsets to be elided from the later tests - calibrateClockOffsets - - -- initial clock shifts startup delays topology - , tt (Just icsDiamond) ((m *) <$> sdDiamond) diamond - , tt (Just icsComplete) ((m *) <$> sdComplete) (complete d3) - , tt (Just icsCyclic) ((m *) <$> sdCyclic) (cyclic d5) - , tt (Just icsTorus) ((m *) <$> sdTorus) (torus2d d2 d3) - , tt (Just icsStar) ((m *) <$> sdStar) (star d7) - , tt (Just icsLine) ((m *) <$> sdLine) (line d4) - , tt (Just icsHourglass) ((m *) <$> sdHourglass) (hourglass d3) - - -- make sure the clock offsets detected during calibration is still the same - , validateClockOffsetCalibration - ] - , mPostProc = Nothing - } -{- FOURMOLU_ENABLE -} diff --git a/bittide-instances/src/Bittide/Instances/Hitl/IlaPlot.hs b/bittide-instances/src/Bittide/Instances/Hitl/IlaPlot.hs deleted file mode 100644 index 2efc7f1e0..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/IlaPlot.hs +++ /dev/null @@ -1,731 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -{- | ILA extension for the generation of plot data monitoring the -clock modifications and the data counts of the elastic buffers. The -extension is intended to be used by the hardware-in-the-loop tests. --} -module Bittide.Instances.Hitl.IlaPlot ( - -- * Parameters - SyncPulsePeriod, - ScheduledCapturePeriod, - AccWindowHeight, - CompressedBufferSize, - MaxPulseCount, - - -- * Timestamp Types - GlobalTimestamp, - LocalTimestamp, - - -- * Interface Types - CaptureCondition (..), - isScheduledCaptureCondition, - IlaPlotSetup (..), - IlaControl (..), - PlotData (..), - RfStageChange (..), - - -- * ILA Plot - ilaProbeNames, - ilaPlotSetup, - callistoClockControlWithIla, - - -- * Helpers - SyncPulseCycles, - ScheduledCaptureCycles, - DDiv, - DDivCheck, - accWindow, - overflowResistantDiff, - DiffResult (..), - syncOutGenerator, - syncInRecover, -) where - -import GHC.Stack (HasCallStack) - -import Clash.Explicit.Prelude hiding (PeriodToCycles) -import Clash.Explicit.Signal.Extra -import Clash.Sized.Extra (concatUnsigneds) - -import Bittide.Arithmetic.Time (PeriodToCycles, trueFor) -import Bittide.ClockControl (ClockControlConfig, RelDataCount, SpeedChange (..)) -import Bittide.ClockControl.Callisto ( - CallistoResult (..), - ReframingState (..), - callistoClockControl, - ) -import Bittide.ClockControl.StabilityChecker -import Bittide.Extra.Maybe (orNothing) - -import Clash.Cores.Xilinx.Ila (Depth (..), IlaConfig (..), ila, ilaConfig) -import Clash.Cores.Xilinx.Xpm.Cdc.Gray (xpmCdcGray) -import Clash.Cores.Xilinx.Xpm.Cdc.Single (xpmCdcSingle) -import Clash.Explicit.Reset.Extra - -import Control.Arrow (second, (***)) -import Data.Bool (bool) -import Data.Constraint.Nat.Extra ( - Dict (..), - SatSubZero, - maxGeqPlus, - minLeq, - satSubZeroMin, - ) -import Data.Maybe (fromMaybe, isJust) - -{- | Divisible division operation, which ensures that the dividend is -always a multiple of the divisor. Type family resolution will get -/stuck/ if the dividend is not a multiple of the divisor. --} -type family DDiv (a :: Nat) (b :: Nat) :: Nat where - DDiv a b = DDivCheck (Mod a b) a b - -{- | Helper type family for checking the reminder of -'DDiv'. Unfortunately type families cannot be scoped. --} -type family DDivCheck (a :: Nat) (b :: Nat) (c :: Nat) :: Nat where - DDivCheck 0 a b = Div a b - -{- | The window high of 'accWindow' for reducing the number of -reported clock modifications. --} -type AccWindowHeight = 5 :: Nat - -{- | The period of the sync pulse used to share a synchronized time -stamp between the nodes. --} -type SyncPulsePeriod = Milliseconds 5 - --- | The period of the scheduled capture (must be a multiple of 'SyncPulsePeriod'). -type ScheduledCapturePeriod = Milliseconds 20 - -{- | An upper bound on the number of synchronized pulses during a test -run. The bound allows to count pulses up to 5 minutes without -producing an overflow. We assume that the test has finished or was -canceled within that time. --} -type MaxPulseCount = DDiv (5 * 60 * Seconds 1) SyncPulsePeriod - -{- | The number of cycles within the given domain that fit into one -sync pulse period. --} -type SyncPulseCycles dom = PeriodToCycles dom SyncPulsePeriod - -{- | A global timestamp consisting of the number of synchronized -pulses received and the number of cycles of the local stable clock -(identified by the given domain and starting with 'syncStart'). The -local clock cycle counter can count up to 10% more cycles than -mathematically required to compensate potential drifts of the local -stable clock. --} -type GlobalTimestamp dom = - ( Index MaxPulseCount - , Index (Div (SyncPulseCycles dom) 10 + SyncPulseCycles dom) - ) - -{- | The number of cycles within the given domain fitting into one -scheduled capture period. --} -type ScheduledCaptureCycles dom = PeriodToCycles dom ScheduledCapturePeriod - -{- | The local timestamp counting the cycles of the dynamic clock -since the last scheduled capture (starting with clock control). -The counter can count up to 10% more cycles than mathematically -required to compensate for clock changes resulting from -'Bittide.ClockControl.SpeedUp' and 'Bittide.ClockControl.SlowDown' -applications. --} -type LocalTimestamp dom = - Index - ( ScheduledCaptureCycles dom - + Div - (ScheduledCaptureCycles dom) - 10 - ) - -{- | The number of pulses it takes until a scheduled capture gets -triggered. --} -type ScheduledPulseCount = DDiv ScheduledCapturePeriod SyncPulsePeriod - -{- | The reduced elastic buffer size to be used for reporting only the -difference since the last capture. --} -type CompressedBufferSize = 16 :: Nat - --- | The type of change with respect to the stage of 'ReframingState'. -data RfStageChange - = -- | Indicates that the reframing stage is stable and does not change. - Stable - | -- | Indicates that the reframing stage changed to the @DETECT@ state. - ToDetect - | -- | Indicates that the reframing stage changed to the @WAIT@ state. - ToWait - | -- | Indicates that the reframing stage changed to the @DONE@ state. - ToDone - deriving (Eq, Generic, BitPack, NFDataX) - --- | The ILA capture type. -data CaptureCondition - = -- | Identifies captures happening with or before the trigger. - -- - -- Note that we always need to capture everything before the - -- trigger fires, because the data that the ILA captures is - -- undefined otherwise. Moreover, 'syncStart' does not hold at the - -- trigger, but only after it. Hence, if the trigger position is - -- at 0, then we store exactly one capture that is marked with the - -- 'UntilTrigger' flag this way. - UntilTrigger - | -- | Identifies scheduled captures during the initial calibration - -- period. - Calibrate - | -- | Identifies scheduled captures after the initial calibration - -- period. - Scheduled - | -- | Identifies intermediate captures that are triggered by data - -- changes. - DataChange - deriving (Eq, Generic, NFDataX, BitPack) - -{- | Whether the given 'CaptureCondition' is a scheduled capture. I.e., one -triggered by a sync pulse. --} -isScheduledCaptureCondition :: CaptureCondition -> Bool -isScheduledCaptureCondition = \case - Scheduled -> True - Calibrate -> True - UntilTrigger -> False - DataChange -> False - -{- | All signals, as they are required for using clock control with -ILA plotting capabilities. --} -data IlaPlotSetup dom = IlaPlotSetup - { sysClk :: Clock dom - -- ^ The stable system clock. - , sysRst :: Reset dom - -- ^ The system's reset line. - , allReady :: Signal dom Bool - -- ^ A boolean signal indicating that all transceivers are ready. See - -- 'Bittide.Transceiver.Output.linkReady'. - , startTest :: Signal dom Bool - -- ^ The test start signal coming from the HITLT VIO interface. - , syncIn :: Signal dom Bool - -- ^ The signal connected to @SYNC_IN@. - } - -{- | All signals, as they are required by the ILA trigger and capture -conditions. You must use 'ilaPlotSetup' for generating them. --} -data IlaControl dom = IlaControl - { syncRst :: Reset dom - -- ^ Synchronized reset line, which is only deasserted during - -- the actual test. - , syncOut :: Signal dom Bool - -- ^ The signal to be passed to @SYNC_OUT@, which - -- is only connected for the last node in the network and wired back - -- to @SYNC_IN@ of all nodes from there. - -- - -- Note that all nodes are in reset before their local - -- 'startTest' VIO signal gets asserted, as 'startTest' is - -- directly driving 'sysRst'. Thus, for the other nodes to - -- capture the @SYNC_OUT@ signal correctly, the node receiving - -- the `startTest` rising edge last must be the one with it's - -- @SYNC_OUT@ physically connected to the @SYNC_IN@ of all nodes - -- in the network. This assumption is tested by - -- 'Bittide.Instances.Hitl.SyncInSyncOut'. - , syncStart :: Signal dom Bool - -- ^ Synchronized test start trigger - , scheduledCapture :: Signal dom Bool - -- ^ Synchronized pre-scheduled capture trigger - , globalTimestamp :: Signal dom (GlobalTimestamp dom) - -- ^ Synchronized pulse counter - , skipTest :: Signal dom Bool - -- ^ Skip this test - } - --- | Names of the additional ILA plot probes. -ilaProbeNames :: Vec 6 String -ilaProbeNames = - "trigger_1" - :> "capture_1" - :> "condition" - :> "global" - :> "local" - :> "data" - :> Nil - --- | The ILA plot setup controller. -ilaPlotSetup :: - forall dom. - (HasCallStack) => - (HasDefinedInitialValues dom, HasSynchronousReset dom) => - -- | required input signals - IlaPlotSetup dom -> - IlaControl dom -ilaPlotSetup IlaPlotSetup{..} = IlaControl{..} - where - -- 'syncOutGenerator' is used to drive 'SYNC_OUT'. - syncOut = - dflipflop sysClk - $ syncOutGenerator sysClk startTest - $ trueFor (SNat @(Seconds 5)) sysClk syncRst allReady - - -- first synchronize SYNC_IN to the local clock and filter from - -- potential glitches - syncInFiltered = - unsafeToActiveLow - $ resetGlitchFilter (SNat @128) sysClk - $ unsafeFromActiveLow - $ xpmCdcSingle sysClk sysClk syncIn - - -- generate a pulse on every change of SYNC_IN - syncInChangepoints = - changepoints sysClk sysRst enableGen syncInFiltered - - -- recover the activity and readiness states from SYNC_IN - (syncActive, syncStart) = - unbundle $ syncInRecover sysClk sysRst startTest syncInFiltered - - -- tests are reset on `sysRst` or if not synchronously active - syncRst = sysRst `orReset` unsafeFromActiveLow syncActive - - -- generate the global timestamp from the synchronous rising and - -- falling edges of SYNC_IN - globalTimestamp = - register sysClk syncRst enableGen (0, 0) - $ mux - syncInChangepoints - (((+ 1) *** const 0) <$> globalTimestamp) - (second (+ 1) <$> globalTimestamp) - - scheduledCapture = - syncStart - .&&. mealy - sysClk - syncRst - enableGen - (\c i -> (if i then satSucc SatWrap c else c, i && c == minBound)) - (minBound :: Index ScheduledPulseCount) - syncInChangepoints - - skipTest = pure False - -{- | A single data type for covering all of the non-clock related data -to be included into a capture. --} -data PlotData (n :: Nat) (m :: Nat) = PlotData - { dEBData :: Vec n (RelDataCount m, Maybe Bool, Maybe Bool) - , dSpeedChange :: SpeedChange - , dRfStageChange :: RfStageChange - } - deriving (Generic, NFDataX, BitPack) - -{- | Accumulates over multiple @FINC@/@FDEC@s to reduce the number of -captures recorded by the ILA (which are mostly jitter otherwise). - -The compression technique works as follows: if both @FINC@ and -@FDEC@ are requested after each other, then they cancel each other -out and are not reported. Hence, only @FINC@/@FDEC@s are reported -that haven't canceled out before they exceed the @n@ -boundary. Thus, for example @FINC@, @FINC@, @FDEC@, @FDEC@, ... is -not reported for @n > 2@ as the first two @FINC@s don't exceed the -boundary @n@. --} -accWindow :: - forall height dom. - (HasCallStack) => - (KnownNat height, KnownDomain dom) => - -- | The height of the accumulation window. - SNat height -> - Clock dom -> - Reset dom -> - Enable dom -> - Signal dom SpeedChange -> - Signal dom SpeedChange -accWindow _ clk rst ena = - mealy clk rst ena transF (True, minBound :: Index height) - where - transF (d, s) = \case - NoChange -> ((d, s), NoChange) - x -> - let d' = if x == SpeedUp then d else not d - in if - | d' && s == maxBound -> ((not d, minBound), x) - | not d' && s == minBound -> ((not d, minBound), NoChange) - | d' -> ((d, s + 1), NoChange) - | otherwise -> ((d, s - 1), NoChange) - -{- | Calculates the difference of a wrapping counter between two -points in time taking potential overflows into account. The -captured difference is wrapped into an option type, which defaults -to 'TooLarge' as soon as the difference exceeds the capacity of the -returned index type. - -The difference is measured against a stored reference, which is -taken from the counter whenever the additional input signal gets -asserted. 'NoReference' is output before the first assertion of this -line. - -The counter is assumed to only increase over time and may overflow -several times until the next value gets stored. This means that the -returned difference is measured against the point in time where the -reference was stored (accumulating every overflow since then) and -not against the actual value of the counter at that time. The -counter can increase by any value in the range of the counter's -type per cycle. --} -overflowResistantDiff :: - forall dom n m. - (HasCallStack) => - (KnownDomain dom, KnownNat n, KnownNat m) => - (1 <= n, 1 <= m) => - Clock dom -> - Reset dom -> - -- | Take the current counter value as the new reference if asserted - Signal dom Bool -> - -- | The input counter - Signal dom (Unsigned n) -> - -- | The measured difference as long as it fits into the output type - Signal dom (DiffResult (Index m)) -overflowResistantDiff clk rst trg cnt = - mealy clk rst enableGen transF NoReference $ bundle (cnt, trg) - where - transF state (curValue, newRef) = - if newRef - then (Difference (curValue, 0, 0), Difference 0) - else case state of - TooLarge -> (TooLarge, TooLarge) - NoReference -> (NoReference, NoReference) - Difference (refValue, prevOverflows, prevDiff) -> - let curDiff = curValue - refValue - curOverflows = - if curDiff < prevDiff - then satSucc SatError prevOverflows - else prevOverflows - in if prevOverflows - == maxOverflows - && curDiff - < prevDiff - || curOverflows - == maxOverflows - && curDiff - > maxDiff - then (TooLarge, TooLarge) - else (Difference (refValue, curOverflows, curDiff),) - $ case satSubZeroMin @(BitSize (Index m)) @n of - Dict -> case minLeq @(BitSize (Index m)) @n of - Dict -> - Difference - $ bitCoerce - $ concatUnsigneds curOverflows - $ checkedTruncateB - @(Min (BitSize (Index m)) n) - @(n - Min (BitSize (Index m)) n) - curDiff - - maxDiff :: Unsigned n - maxDiff = natToNum @(Mod (m - 1) (2 ^ n)) - - maxOverflows :: Unsigned (SatSubZero (BitSize (Index m)) n) - maxOverflows = - let x :: Unsigned (BitSize (Index m)) - x = bitCoerce (maxBound :: Index m) `shiftR` natToNum @n - in case satSubZeroMin @(BitSize (Index m)) @n of - Dict -> checkedTruncateB x - --- | The result of 'overflowResistantDiff'. -data DiffResult a - = -- | Wait for the first pulse to store the initial reference - -- value. - NoReference - | -- | The accumulated difference since the last value has been - -- stored. - Difference a - | -- | Indicates that the difference against the last stored - -- reference got to large to fit into the output type. - TooLarge - deriving (Generic, BitPack, NFDataX, Functor, Eq, Ord, Show) - -{-# NOINLINE callistoClockControlWithIla #-} - -{- | Wrapper on 'Bittide.ClockControl.Callisto.callistoClockControl' -additionally dumping all the data that is required for producing -plots of the clock control behavior. --} -callistoClockControlWithIla :: - forall n m sys dyn margin framesize. - (HasCallStack) => - (KnownDomain dyn, KnownDomain sys, HasSynchronousReset sys) => - (KnownNat n, KnownNat m, KnownNat margin, KnownNat framesize) => - (1 <= n, 1 <= m, n + m <= 32, 1 <= framesize, 6 + n * (m + 4) <= 1024) => - (CompressedBufferSize <= m) => - Clock dyn -> - Clock sys -> - Reset sys -> - ClockControlConfig sys m margin framesize -> - -- | Ila trigger and capture conditions - IlaControl sys -> - -- | Link availability mask - Signal sys (BitVector n) -> - -- | Statistics provided by elastic buffers. - Vec n (Signal sys (RelDataCount m)) -> - Signal sys (CallistoResult n) -callistoClockControlWithIla dynClk clk rst ccc IlaControl{..} mask ebs = - hwSeqX ilaInstance (muteDuringCalibration <$> calibrating <*> result) - where - result = callistoClockControl clk rst enableGen ccc mask ebs - - filterCounts vMask vCounts = flip map (zip vMask vCounts) - $ \(isActive, count) -> if isActive == high then count else 0 - - filterIndicators vMask vCounts = flip map (zip vMask vCounts) - $ \(isActive, ind) -> if isActive == high then ind else StabilityIndication False False - - maxGeqPlusApp = - maxGeqPlus @1 - @(DivRU ScheduledCapturePeriod (Max 1 (DomainPeriod dyn))) - @(Div (ScheduledCaptureCycles dyn) 10) - - -- local timestamp on the stable clock - localTs :: Signal sys (DiffResult (LocalTimestamp dyn)) - localTs = case maxGeqPlusApp of - Dict -> - overflowResistantDiff - clk - syncRst - (delay clk enableGen False (maybe False isScheduledCaptureCondition <$> captureCond)) - $ let ccRst = xpmResetSynchronizer Asserted clk dynClk syncRst - lts :: Signal dyn (Unsigned 8) - lts = - register dynClk ccRst enableGen minBound - $ satSucc SatWrap - <$> lts - in xpmCdcGray dynClk clk lts - - -- collect all plot data - localData = - let rfStageChange CallistoResult{..} = case reframingState of - Detect{} -> ToDetect - Wait{} -> ToWait - Done{} -> ToDone - - height = SNat @AccWindowHeight - idcs = - unbundle - (filterIndicators <$> fmap bv2v mask <*> (stability <$> result)) - - -- get the points in time where the monitored values change - stableUpdates = changepoints clk rst enableGen <$> (fmap stable <$> idcs) - settledUpdates = changepoints clk rst enableGen <$> (fmap settled <$> idcs) - modeUpdate = changepoints clk rst enableGen (rfStageChange <$> result) - - combine eb stU seU ind = - (,,) - <$> eb - <*> (orNothing <$> stU <*> (stable <$> ind)) - <*> (orNothing <$> seU <*> (settled <$> ind)) - - noChange = fromMaybe NoChange . maybeSpeedChange - in PlotData - <$> bundle (zipWith4 combine ebsC stableUpdates settledUpdates idcs) - <*> accWindow height clk rst enableGen (noChange <$> result) - <*> mux modeUpdate (rfStageChange <$> result) (pure Stable) - - -- compress the elastic buffer data via only reporting the - -- differences since the last capture - (ebDataChange, ebsC) = - second unbundle - $ let transF storedDataCounts (trigger, curDataCounts) = - let diffs = zipWith (-) curDataCounts storedDataCounts - half = - extend @_ - @(CompressedBufferSize - 1) - @(m - CompressedBufferSize + 1) - maxBound - truncDiffs = - truncateB @_ - @CompressedBufferSize - @(m - CompressedBufferSize) - <$> diffs - in if trigger || any ((> half) . abs) diffs - then (curDataCounts, (True, truncDiffs)) - else (storedDataCounts, (False, repeat 0)) - in mealyB - clk - rst - enableGen - transF - (repeat 0) - ( scheduledCapture - , filterCounts <$> fmap bv2v mask <*> bundle ebs - ) - - -- produce at least two calibration captures - calibrating = - unsafeToActiveLow syncRst - .&&. moore - clk - syncRst - enableGen - (\s -> bool s $ satSucc SatBound s) - (/= maxBound) - (minBound :: Index 3) - scheduledCapture - - -- do not forward clock modifications during calibration - muteDuringCalibration active ccResult = - ccResult - { maybeSpeedChange = bool (maybeSpeedChange ccResult) Nothing active - } - - -- Note that we always need to capture everything before the trigger - -- fires, because the data that the ILA captures is undefined - -- otherwise. Moreover, @syncStart@ does not hold at the trigger, - -- but only after it. Hence, if the trigger position is at 0, then - -- we store exactly one capture that is marked with the - -- @UntilTrigger@ flag this way. - captureCond :: Signal sys (Maybe CaptureCondition) - captureCond = - mux - (not <$> syncStart) - (pure $ Just UntilTrigger) - (fmap fst <$> plotData) - - plotData = - let captureType calibrate scheduled dc dat - | scheduled && calibrate = Just (Calibrate, dat) - | scheduled = Just (Scheduled, dat) - | dc || dataChange dat && not calibrate = Just (DataChange, dat) - | otherwise = Nothing - - dataChange PlotData{..} = - any (\(_, x, y) -> isJust x || isJust y) dEBData - || dSpeedChange - /= NoChange - || dRfStageChange - /= Stable - in captureType - <$> calibrating - <*> scheduledCapture - <*> ebDataChange - <*> localData - - ilaInstance :: Signal sys () - ilaInstance = - setName @"ilaPlot" - $ ila - (ilaConfig ilaProbeNames){depth = D16384, stages = 2} - -- the ILA must run on a stable clock - clk - -- trigger as soon as we start - (syncStart .&&. (not <$> skipTest)) - -- capture on relevant data changes - (syncStart .&&. (isJust <$> captureCond) .&&. (not <$> skipTest)) - -- capture the capture condition - (fromMaybe UntilTrigger <$> captureCond) - -- capture the globally synchronized timestamp - globalTimestamp - -- capture the local timestamp - localTs - -- capture all relevant plot data - (maybe dummy snd <$> plotData) - - dummy = - PlotData - { dEBData = repeat (0, Nothing, Nothing) - , dSpeedChange = NoChange - , dRfStageChange = Stable - } - --- | The state space of the Mealy machine for producing @SYNC_OUT@. -data SyncOutGen dom - = GettingReady - | WaitAtLeast (Index (SyncPulseCycles dom)) - | WaitForTransceivers - | SyncPulse Bool (Index (SyncPulseCycles dom)) - | Failure - deriving (Generic, NFDataX) - --- | The signal transformer for producing @SYNC_OUT@. -syncOutGenerator :: - forall dom. - (HasCallStack) => - (KnownDomain dom) => - Clock dom -> - -- | The generator starts after this input has turned high. - Signal dom Bool -> - -- | The transceivers being ready indicator. - Signal dom Bool -> - -- | The generated @SYNC_OUT@ signal. - Signal dom Bool -syncOutGenerator clk start inp = - start - .&&. mealyB - clk - (unsafeFromActiveLow start) - enableGen - transF - (GettingReady :: SyncOutGen dom) - inp - where - transF GettingReady _ = (WaitAtLeast maxBound, False) - transF (WaitAtLeast 0) True = (SyncPulse False maxBound, False) - transF (WaitAtLeast 0) _ = (WaitForTransceivers, True) - transF (WaitAtLeast n) _ = (WaitAtLeast (n - 1), True) - transF WaitForTransceivers True = (SyncPulse False maxBound, False) - transF WaitForTransceivers _ = (WaitForTransceivers, True) - transF (SyncPulse o 0) True = (SyncPulse (not o) maxBound, not o) - transF (SyncPulse o n) True = (SyncPulse o (n - 1), o) - transF _ _ = (Failure, True) - --- | The state space of the Moore machine for recovering @SYNC_OUT@. -data SyncInRec dom - = WaitForStart - | WaitForReady - | WaitForChange - Bool - (Index (Div (SyncPulseCycles dom) 10 + SyncPulseCycles dom)) - deriving (Generic, NFDataX) - --- | Recovers the activity cycle of a test as shared via @SYNC_OUT@. -syncInRecover :: - forall dom. - (HasCallStack) => - (KnownDomain dom) => - Clock dom -> - Reset dom -> - -- | The indicator for the test being started via the VIO interface. - Signal dom Bool -> - -- | The @SYNC_IN@ signal. - Signal dom Bool -> - -- | Returns two signals: The first one indicates that the - -- @SYNC_OUT@ signal generation has been initiated, while the second - -- one indicates the synchronized start of the test. - Signal dom (Bool, Bool) -syncInRecover clk rst = - curry - $ moore clk rst enableGen transF out (WaitForStart :: SyncInRec dom) - . bundle - where - transF _ (False, _) = WaitForStart - transF WaitForStart (_, True) = WaitForReady - transF WaitForStart (_, _) = WaitForStart - transF WaitForReady (_, True) = WaitForReady - transF WaitForReady (_, _) = WaitForChange False maxBound - transF (WaitForChange _ 0) (_, True) = WaitForStart - transF (WaitForChange o n) (_, i) - | o == i = WaitForChange o (n - 1) - | otherwise = WaitForChange i maxBound - - out WaitForStart = (False, False) - out WaitForReady = (True, False) - out WaitForChange{} = (True, True) diff --git a/bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs b/bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs deleted file mode 100644 index 36079fe61..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs +++ /dev/null @@ -1,291 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -{- | Tests whether the transceiver link setup matches with the -configuration defined in 'Bittide.Instances.Hitl.Setup.fpgaSetup'. -To this end, each node sends its own index to all of it's -neighbours, which then verify that the result matches with the -definition. --} -module Bittide.Instances.Hitl.LinkConfiguration ( - linkConfigurationTest, - tests, -) where - -import Clash.Explicit.Prelude -import qualified Clash.Explicit.Prelude as E -import Clash.Prelude (withClockResetEnable) - -import qualified Data.Map.Strict as Map (fromList) - -import Bittide.Arithmetic.Time -import Bittide.ClockControl.Si5395J -import Bittide.ClockControl.Si539xSpi (ConfigState (Error, Finished), si539xSpi) -import Bittide.ElasticBuffer (sticky) -import Bittide.Instances.Domains -import Bittide.Transceiver - -import Bittide.Hitl - -import Bittide.Instances.Hitl.Setup - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Cores.Xilinx.GTH -import Clash.Cores.Xilinx.Xpm.Cdc.Handshake.Extra -import Clash.Cores.Xilinx.Xpm.Cdc.Single -import Clash.Xilinx.ClockGen -import Data.Maybe (fromMaybe, isJust) - -import qualified Bittide.Transceiver as Transceiver -import qualified Bittide.Transceiver.ResetManager as ResetManager - -{- | Checks whether the received index matches with the corresponding -entry in 'Bittide.Instances.Hitl.Setup.fpgaSetup' and sychronizes -to the right clock domain accordingly. --} -checkData :: - forall n src dst. - (KnownDomain src, KnownDomain dst, KnownNat n, 1 <= n, BitSize (Index n) <= 64) => - Clock dst -> - Clock src -> - Signal dst Bool -> - Signal src (Maybe (BitVector 64)) -> - Signal src (Maybe (Index n)) -> - Signal dst Bool -checkData dstClk srcClk ready rx setup = - ready .&&. xpmCdcSingle srcClk dstClk (match <$> rx <*> setup) - where - match ma mb = fromMaybe False (ma .==. (zExtend . pack <$> mb)) - zExtend = zeroExtend @_ @(BitSize (Index n)) @(64 - BitSize (Index n)) - -{- | Extracts the corresponding target FPGA index from -'Bittide.Instances.Hitl.Setup.fpgaSetup' according to the given -link index and synchronizes it to the provided clock domain. --} -expectedTargetIndex :: - (KnownDomain src, KnownDomain dst) => - Clock src -> - Signal src (Index FpgaCount) -> - Clock dst -> - Reset dst -> - Index LinkCount -> - Signal dst (Maybe (Index FpgaCount)) -expectedTargetIndex srcClk myIndex dstClk dstRst link = - fmap ((!! link) . snd . (fpgaSetup !!)) - <$> xpmCdcStable srcClk myIndex dstClk dstRst - -{- | Synchronizes the fixed FPGA index from some given source domain -to the given target domain. Lossy synchronization is sufficient -here, as the index is considered to be stable stable once the test -has been started. --} -xpmCdcStable :: - ( KnownDomain src - , KnownDomain dst - , BitPack a - , NFDataX a - , 1 <= BitSize a - , BitSize a <= 1024 - ) => - Clock src -> - Signal src a -> - Clock dst -> - Reset dst -> - Signal dst (Maybe a) -xpmCdcStable srcClk idx dstClk dstRst = mIdx - where - mIdx = - register dstClk dstRst enableGen Nothing - $ (<|>) - <$> xpmCdcMaybeLossy srcClk dstClk (pure <$> idx) - <*> mIdx - -{- | Configures the clock boards, fires up all of the transceivers and -observes the incoming links. --} -transceiversStartAndObserve :: - "SMA_MGT_REFCLK_C" ::: Clock Ext200 -> - "SYSCLK" ::: Clock Basic125 -> - "RST_LOCAL" ::: Reset Basic125 -> - "MY_INDEX" ::: Signal Basic125 (Index FpgaCount) -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "allReady" ::: Signal Basic125 Bool - , "success" ::: Signal Basic125 Bool - , "stats" ::: Vec LinkCount (Signal Basic125 ResetManager.Statistics) - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -transceiversStartAndObserve refClk sysClk rst myIndex rxNs rxPs miso = - ( transceivers.txNs - , transceivers.txPs - , allReady - , success - , transceivers.stats - , spiDone - , spiOut - ) - where - allReady = and <$> bundle transceivers.linkReadys - sysRst = rst `orReset` unsafeFromActiveLow (fmap not spiErr) - - -- Clock programming - spiDone = E.dflipflop sysClk $ (== Finished) <$> spiState - spiErr = E.dflipflop sysClk $ isErr <$> spiState - - isErr = \case - Error{} -> True - _ -> False - - (_, _, spiState, spiOut) = - withClockResetEnable sysClk sysRst enableGen - $ si539xSpi - testConfig6_200_on_0a_1ppb - (SNat @(Microseconds 10)) - (pure Nothing) - miso - - -- Transceiver setup - transceivers = - transceiverPrbsN - @GthTx - @GthRx - @Ext200 - @Basic125 - @GthTxS - @GthRxS - Transceiver.defConfig - Transceiver.Inputs - { clock = sysClk - , reset = rst `orReset` unsafeFromActiveLow spiDone - , refClock = refClk - , channelNames - , clockPaths - , rxNs - , rxPs - , txDatas = myIndexTxN - , rxReadys = repeat $ pure True - , txReadys = repeat $ pure True - } - - -- synchronizes the FPGA's stable index to the individual TX clock - -- domains of the transceivers - myIndexTxN = - fmap (zeroExtend . pack . fromMaybe 0) - <$> zipWith - (xpmCdcStable sysClk myIndex) - transceivers.txClocks - transceivers.txResets - - -- check that all the received data matches with our expectations - success = - fmap and - $ bundle - $ zipWith4 - (checkData @FpgaCount sysClk) - transceivers.rxClocks - transceivers.linkReadys - transceivers.rxDatas - $ zipWith3 - (expectedTargetIndex sysClk myIndex) - transceivers.rxClocks - transceivers.rxResets - indicesI - --- | Top entity for this test. See module documentation for more information. -linkConfigurationTest :: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "SYNC_OUT" ::: Signal Basic125 Bool - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -linkConfigurationTest refClkDiff sysClkDiff syncIn rxns rxps miso = - (txns, txps, syncOut, spiDone, spiOut) - where - refClk = ibufds_gte3 refClkDiff :: Clock Ext200 - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - - -- the test starts with a valid configuration coming in - startTest = isJust <$> testConfig - -- derive the test settings from the passed configuration - myIndex = fromMaybe 0 <$> testConfig - - -- use some simple SYNC_IN / SYNC_OUT synchronization to - -- synchronously start the test - syncInRst = - resetGlitchFilter (SNat @1024) sysClk - $ unsafeFromActiveLow - $ xpmCdcSingle sysClk sysClk syncIn - syncOut = startTest - - testRst = - sysRst - `orReset` syncInRst - `orReset` unsafeFromActiveLow startTest - - (txns, txps, allReady, success, _stats, spiDone, spiOut) = - transceiversStartAndObserve refClk sysClk testRst myIndex rxns rxps miso - - failAfterUp = isFalling sysClk testRst enableGen False allReady - failAfterUpSticky = sticky sysClk testRst failAfterUp - - -- Consider the test done if links have been up consistently for 40 - -- seconds. This is just below the test timeout of 60 seconds, so - -- transceivers have ~20 seconds to come online reliably. This - -- should be plenty. The test will stop immediately on success, - -- i.e., if all neighbours have transmitted the expected ids - -- alltogether at least once. - done = - success - .||. failAfterUpSticky - .||. trueFor (SNat @(Seconds 40)) sysClk testRst allReady - - testConfig :: Signal Basic125 (Maybe (Index FpgaCount)) - testConfig = hitlVio 0 sysClk done (success .&&. (not <$> failAfterUpSticky)) - -makeTopEntity 'linkConfigurationTest - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'linkConfigurationTest - , extraXdcFiles = [] - , externalHdl = [] - , testCases = - [ HitlTestCase - { name = "LinkConfiguration" - , parameters = - Map.fromList - [ (HwTargetByIndex (fromIntegral i), i) - | i <- [0 ..] :: [Index FpgaCount] - ] - , postProcData = () - } - ] - , mPostProc = Nothing - } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Post/BoardTestExtended.hs b/bittide-instances/src/Bittide/Instances/Hitl/Post/BoardTestExtended.hs deleted file mode 100644 index 28bdc19a9..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/Post/BoardTestExtended.hs +++ /dev/null @@ -1,74 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} - -{- | Post processing of ILA data for 'boardTestExtended', serves as an example -of post processing. --} -module Bittide.Instances.Hitl.Post.BoardTestExtended (postBoardTestExtended) where - -import Prelude - -import Data.Csv -import Data.List (isSuffixOf) -import System.Exit (ExitCode (..)) -import System.FilePath -import Test.Tasty.HUnit - -import Bittide.Instances.Hitl.Post.PostProcess - -import qualified Data.ByteString.Lazy as BL -import qualified Data.Vector as V - -data Row = Row - { sampleInBuffer :: Int - , sampleInWindow :: Int - , trigger :: Bool - , capture :: Bool - , testStartA :: Bool - , testStartB :: Bool - , testDone :: Bool - , testSuccess :: Bool - } - deriving (Show) - -instance FromNamedRecord Row where - parseNamedRecord m = - Row - <$> m .: "Sample in Buffer" - <*> m .: "Sample in Window" - <*> (toEnum <$> m .: "trigger_AorB") - <*> (toEnum <$> m .: "capture") - <*> (toEnum <$> m .: "ilaTestStartA") - <*> (toEnum <$> m .: "ilaTestStartB") - <*> (toEnum <$> m .: "ilaTestDone") - <*> (toEnum <$> m .: "ilaTestSuccess") - -assertTriggerAtStart :: [Row] -> Assertion -assertTriggerAtStart rows = - assertBool "Trigger should be True in the first sample" $ (trigger . head) rows - --- | Decode the CSV file and run all post processing checks -processCsv :: FilePath -> Assertion -processCsv csvPath_ = do - csvData <- BL.readFile csvPath_ - case decodeByName csvData of - Left err -> - assertFailure $ "Failed to decode " <> csvPath_ <> ": " <> err - Right (_header, rowVector) -> do - let rows = V.toList rowVector - assertTriggerAtStart rows - -postBoardTestExtended :: ExitCode -> [FlattenedIlaCsvPath] -> Assertion -postBoardTestExtended _exitCode ilaCsvPaths = do - let - csvToProcess = filter ((baseNameEndsWith "boardTestIla") . ilaName) ilaCsvPaths - assertBool "Expected at least 1 CSV file, but got 0" $ not (null csvToProcess) - mapM_ (processCsv . csvPath) csvToProcess - putStrLn $ - "Successfully performed post processing of " - <> show (length csvToProcess) - <> " ILA CSV dumps" - where - baseNameEndsWith x = isSuffixOf x . takeBaseName diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Post/PostProcess.hs b/bittide-instances/src/Bittide/Instances/Hitl/Post/PostProcess.hs deleted file mode 100644 index 4cf0aebc0..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/Post/PostProcess.hs +++ /dev/null @@ -1,95 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} - --- | Infrastructure for post processing of ILA data -module Bittide.Instances.Hitl.Post.PostProcess where - -import Prelude - -import Data.Maybe (fromMaybe) -import GHC.Stack (HasCallStack) -import System.FilePath (makeRelative, splitDirectories, takeBaseName) -import Text.Read (readMaybe) - -import Data.Map (Map) -import qualified Data.Map as Map - -type TestName = String -type IlaName = String - -data FpgaNum - = DemoRack Int - | Any - deriving (Eq, Ord, Show) - -type NestedIlaCsvPaths = Map TestName (Map FpgaNum (Map IlaName FilePath)) - -data FlattenedIlaCsvPath = FlattenedIlaCsvPath - { testName :: TestName - , fpgaNum :: FpgaNum - , ilaName :: IlaName - , csvPath :: FilePath - } - -addIlaCsvPath :: NestedIlaCsvPaths -> FlattenedIlaCsvPath -> NestedIlaCsvPaths -addIlaCsvPath m FlattenedIlaCsvPath{..} = Map.alter goTestName testName m - where - goTestName = Just . Map.alter goFpgaNum fpgaNum . fromMaybe Map.empty - goFpgaNum = Just . Map.insert ilaName csvPath . fromMaybe Map.empty - -{- | Convert a String of the format "{idx}_{fpga_id}" to an FpgaNum. idx is -either the index of the FPGA board in the demo rack, or 'X' if it was -programmed with `Any`. fpga_id is the unique identifier of the FPGA. - -toFpgaNum "X_210308B09917" == Any -toFpgaNum "7_210308B0B0C2" == DemoRack 7 --} -toFpgaNum :: (HasCallStack) => String -> FpgaNum -toFpgaNum fpgaName = - case prefix of - "X" -> Any - _ -> - case readMaybe prefix of - Nothing -> error $ "Expected an FPGA name with prefix 'X' or a number, but got: " <> fpgaName - Just n -> DemoRack n - where - prefix = takeWhile (/= '_') fpgaName - -{- | Create NestedIlaCsvPaths using a list of filepaths of CSV dumps and the -base directory of ILA data. --} -toNestedIlaCsvPaths :: (HasCallStack) => FilePath -> [FilePath] -> NestedIlaCsvPaths -toNestedIlaCsvPaths ilaDataDir = foldl addIlaCsvPath Map.empty . toFlattenedIlaCsvPathList ilaDataDir - -{- | Create a list of FlattenedIlaCsvPath using a list of filepaths of CSV dumps -and the base directory of ILA data. --} -toFlattenedIlaCsvPathList :: - (HasCallStack) => FilePath -> [FilePath] -> [FlattenedIlaCsvPath] -toFlattenedIlaCsvPathList ilaDataDir = map go - where - go :: FilePath -> FlattenedIlaCsvPath - go csvPath = FlattenedIlaCsvPath{..} - where - relativeCsvPath = makeRelative ilaDataDir csvPath - (testName, toFpgaNum -> fpgaNum, takeBaseName -> ilaName) = - case splitDirectories relativeCsvPath of - [a, b, c] -> (a, b, c) - zs -> - error $ - "Execpted to split " - <> show relativeCsvPath - <> " in tree parts," - <> " but was able to split it into: " - <> show zs - --- | Like 'Map.!', but mentions key in error message if it can't be found -get :: (HasCallStack, Ord k, Show k) => Map k v -> k -> v -get m k = - case Map.lookup k m of - Just v -> v - Nothing -> error $ "Could not find key: " <> show k - -infixl 9 `get` diff --git a/bittide-instances/src/Bittide/Instances/Hitl/README.md b/bittide-instances/src/Bittide/Instances/Hitl/README.md deleted file mode 100644 index fb5119b91..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/README.md +++ /dev/null @@ -1,104 +0,0 @@ - - -# Hardware in the loop tests -Besides simulation, we also want to test our designs on physical hardware. We -have constructed a demo rig, which consists of 8 FPGA boards (KCU105), which are -all connected to a PC through their JTAG ports. This PC runs a GitHub runner. - -To add a HTIL test: - -- Instantiate `Bittide.Hitl.hitlVio` in your design -- Add your test to `hitlTests` in ([Tests.hs](/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs)) -- Add your test to CI: - - [staging](/.github/synthesis/staging.json) runs on every PR, - [all](/.github/synthesis/all.json) runs every night - - Set stage to `test` - -A design marked as a Hardware-in-the-loop test (hitlt) should adhere to -framework described in the next chapters. - -## ILA -All ILAs present in the design are armed before the VIO test is started. Each -ILA produces a separate CSV file for each VIO test. - -For the Hardware-in-the-Loop test (hitlt) at least 3 probes need to be present -in the design: -- `trigger*` is an active-high boolean value (`probeType` is `Trigger` or -`DataAndTrigger`). This signal _may_ be sticky, but a single cycle is enough. -- `capture*` indicates when to sample data values (`probeType` is `Trigger` or -`DataAndTrigger`) -- At least one data probe. All other probes must have `probeType` `Data` or -`DataAndTrigger`. - -The CSV files are written to the following directory: - -``` -_build/vivado/{instance}/ila-data/{test_case_name}/{index_in_rig}_{FPGA_id} -``` - -or, when the index in the rig could not be determined: - -``` -_build/vivado/{instance}/ila-data/{test_case_name}/{FPGA_id} -``` - -In this directory, a CSV file and a VCD file with the name of the ILA are -written. The name of the ILA can be set with `setName`, identical to setting a -name for a VIO. Note that a lot of files can be generated, e.g. a -hardware-in-the-loop test with 2 test cases and 2 ILAs programmed on all 8 -FPGAs in the demo rig results in 32 CSV files. - -The default ILA configuration (`ilaConfig`, see [Clash.Cores.Xilinx.Ila](https://github.com/clash-lang/clash-compiler/blob/15dc344dfa091de14c63759c0b6ea107ca0fa892/clash-cores/src/Clash/Cores/Xilinx/Ila.hs#L63) is valid -for hardware-in-the-loop tests. If a custom configuration is used, make sure to -set `captureControl` to `True`, and use the `probeType`s described above. - -All ILA data is uploaded from the FPGA to the PC after the VIO test is finished -(or has timed out). If an ILA did not trigger, the saved CSV file will only -contain the header. - - -## Pseudo-code of a hardware-in-the-loop test -A complete hardware-in-the-loop test as pseudo-code: -``` -for each FPGA - upload bitstream to FPGA -for each test - for each FPGA - assert `probe_test_done` is `0` - set `probe_test_data` to the parameter for this FPGA if there is one - arm all ILAs - start test by setting `probe_test_start` to `1` - for each FPGA - wait for `probe_test_done` to assert - print test results - for each FPGA - upload ILA data - stop test by setting `probe_test_start` to `0` - print test summary -print summary all tests -``` -Test execution is implemented in `Clash.Shake.Vivado` of bittide-shake. - - -## Post processing of ILA data -If a Shake target has a post processing function, it is executed after the -hardware test as part of the `:test` call. The post processing function can also -be called without performing the hardware test again using `:post-process`. - -To add post processing to a bittide instance: - -1. Create a Haskell file in `bittide-instances/exe/post-{test_name}/` with a `main` -function. This file can import any file from `Bittide.Instances`. The function -is called from Shake with 2 arguments: filepath of the ILA data directory and -the exit code of the hardware test which generated the ILA data. -2. Add an executable for the new Haskell file named `post-{test_name}` in -`bittide-instances.cabal`. -3. In the Haskell file containing the test group definition of type -`Bittide.Hitl.HitlTestGroup`, define the `mPostProc` field to be a `Just` with the -name of the executable created in the step above as a `String`. - -See the example for the instance `boardTestExtended`. diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Setup.hs b/bittide-instances/src/Bittide/Instances/Hitl/Setup.hs deleted file mode 100644 index c4f896cd2..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/Setup.hs +++ /dev/null @@ -1,120 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Bittide.Instances.Hitl.Setup ( - FpgaCount, - LinkCount, - FpgaId, - TransceiverWires, - allHwTargets, - channelNames, - clockPaths, - fpgaSetup, - knownFpgaIds, - knownFpgaIdsVec, - linkMask, - linkMasks, -) where - -import Clash.Prelude - -import Bittide.Hitl (FpgaId, HwTargetRef (..)) -import Bittide.Topology -import Data.Constraint (Dict (..), (:-) (..)) -import Data.Constraint.Nat (leTrans) - --- | The number of FPGAs in the current setup -type FpgaCount = 8 :: Nat - -type LinkCount = FpgaCount - 1 - -{- | Data wires from/to transceivers. No logic should be inserted on these -wires. Should be considered asynchronous to one another - even though their -domain encodes them as related. --} -type TransceiverWires dom n = Signal dom (BitVector n) - -channelNames :: Vec LinkCount String -channelNames = - "X0Y10" :> "X0Y9" :> "X0Y16" :> "X0Y17" :> "X0Y18" :> "X0Y19" :> "X0Y11" :> Nil - -clockPaths :: Vec LinkCount String -clockPaths = - "clk0" :> "clk0" :> "clk0-2" :> "clk0-2" :> "clk0-2" :> "clk0-2" :> "clk0" :> Nil - -{- | Some order of the FPGA ids and a mapping to their connected -neighbors (via the index position in the vector) according to the -different hardware interfaces on the boards. --} -fpgaSetup :: Vec FpgaCount (FpgaId, Vec LinkCount (Index FpgaCount)) -fpgaSetup = - -- FPGA Id SFP0 SFP1 J4 J5 J6 J7 SMA - ("210308B3B272", 3 :> 2 :> 4 :> 5 :> 6 :> 7 :> 1 :> Nil) - :> ("210308B0992E", 2 :> 3 :> 5 :> 6 :> 7 :> 4 :> 0 :> Nil) - :> ("210308B0AE73", 1 :> 0 :> 6 :> 7 :> 4 :> 5 :> 3 :> Nil) - :> ("210308B0AE6D", 0 :> 1 :> 7 :> 4 :> 5 :> 6 :> 2 :> Nil) - :> ("210308B0AFD4", 7 :> 6 :> 0 :> 3 :> 2 :> 1 :> 5 :> Nil) - :> ("210308B0AE65", 6 :> 7 :> 1 :> 0 :> 3 :> 2 :> 4 :> Nil) - :> ("210308B3A22D", 5 :> 4 :> 2 :> 1 :> 0 :> 3 :> 7 :> Nil) - :> ("210308B0B0C2", 4 :> 5 :> 3 :> 2 :> 1 :> 0 :> 6 :> Nil) - :> Nil - -{- | The IDs of the Digilent chips on each of the FPGA boards of the test -setup. The indices match the position of each FPGA in the mining rig. --} -knownFpgaIdsVec :: Vec FpgaCount FpgaId -knownFpgaIdsVec = fst <$> fpgaSetup - -{- | The IDs of the Digilent chips on each of the FPGA boards of the test -setup. The indices match the position of each FPGA in the mining rig. --} -knownFpgaIds :: [FpgaId] -knownFpgaIds = toList knownFpgaIdsVec - -allHwTargets :: [HwTargetRef] -allHwTargets = HwTargetById <$> knownFpgaIds - -{- | Determines the link mask of a particular node. - ->>> import Data.Graph ->>> import Clash.Prelude ->>> import Bittide.Topology ->>> let edges = [(0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (2, 1)] ->>> let g = fromGraph @3 "test" $ buildG (0, 2) edges ->>> linkMask g d0 -0b010_0001 ->>> linkMask g d1 -0b100_0001 ->>> linkMask g d2 -0b110_0000 --} -linkMask :: - forall n i. - (KnownNat n, KnownNat i, n <= FpgaCount, i + 1 <= n) => - Topology n -> - SNat i -> - BitVector (FpgaCount - 1) -linkMask g i = case leTrans @(i + 1) @n @FpgaCount of - Sub Dict -> pack $ map edge $ snd $ at @i @(FpgaCount - i - 1) i fpgaSetup - where - edge j = - j - <= (natToNum @(n - 1)) - && hasEdge g (natToNum @i) (truncateB @_ @n @(FpgaCount - n) j) - -linkMasks :: - forall n. - (KnownNat n, n <= FpgaCount) => - Topology n -> - Vec n (BitVector (FpgaCount - 1)) -linkMasks g = smap (const . linkMask') indicesI - where - -- workaround, which is required to compensate for the missing upper - -- bound witness of smap, which can be improved as soon as - -- https://github.com/clash-lang/clash-compiler/pull/2686 - -- is available. - linkMask' :: forall i. SNat i -> BitVector (FpgaCount - 1) - linkMask' i@SNat = case compareSNat (SNat @(i + 1)) (SNat @n) of - SNatLE -> linkMask g i - _ -> error "impossible" diff --git a/bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs b/bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs deleted file mode 100644 index 993373945..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs +++ /dev/null @@ -1,174 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} - -{- | Test to confirm physical connection of SYNC_IN / SYNC_OUT. - -For some tests in Bittide is important that our demo rig (consisting of eight -FPGA development boards) start their tests synchronously. To this end, we've -wired up these boards as such (shown with just three): - - FPGA 0 - +------------+ - | SYNC_IN |<--------+ - | | | - | SYNC_OUT |--X | - +------------+ | - | - FPGA 1 | - +------------+ | - | SYNC_IN |<--------| - | | | - | SYNC_OUT |--X | - +------------+ | - | - FPGA 2 | - +------------+ | - | SYNC_IN |<--------+ - | | | - | SYNC_OUT |>--------+ - +------------+ - -In this setup, all FPGAs can drive their SYNC_OUT to low as a default value -while driving it to high as soon as the their tests starts. Because only the -last FPGA has its SYNC_OUT physically connected, only its assertion will have -effect. In the mean time, all FPGAs should monitor their SYNC_IN and start -running when it gets asserted. As long as the last FPGA is the last to be -started (through test VIOs), this will make FPGAs start their tests -synchronously. - -This test therefore consists of two parts: - - * A component that drives SYNC_OUT low for 1 second, and high indefinitely. - * A component that checks whether SYNC_IN is low for at least a second, and - that it is asserted within 10 seconds thereafter. - -This gives the TCL ~9 seconds to program all the boards. - -Failure modes: - - 1. One or more SYNC_INs are *disconnected*. Boards will read either low or - high indefinitely, failing the tests. Alternatively; if no pull up or - pull down is available, results will be random - also failing the test. - - 2. None of the SYNC_OUTs are connected. Same result as (1). - - 3. Multiple SYNC_OUTs are connected. The last board in the chain will see - the rising edge "too soon". - - 4. Wrong SYNC_OUT is connected. Same result as (3). --} -module Bittide.Instances.Hitl.SyncInSyncOut where - -import Clash.Explicit.Prelude hiding (PeriodToCycles) - -import Bittide.Arithmetic.Time -import Bittide.Hitl -import Bittide.Instances.Domains -import Bittide.Instances.Hitl.Setup (allHwTargets) - -import Clash.Annotations.TH -import Clash.Cores.Xilinx.Xpm.Cdc.Single -import Clash.Xilinx.ClockGen - --- | An 'Index' counting to /n/ seconds on 'Basic125' -type IndexSeconds n = Index (PeriodToCycles Basic125 (Seconds n)) - --- | Status of test. Used to communicate test success/failure to host computer. -data TestStatus - = Busy - | Fail - | Success - deriving (Generic, Show, ShowX, NFDataX) - --- | State for 'testFsm' -data TestFsmState - = -- | Lie low for a bit - InReset - | -- | Expect _low_ for a least 1 second - ExpectLow (IndexSeconds 1) - | -- | Expect a rising edge within 10 seconds. The edge is expected after _time - -- it takes to start the remaining tests_. - WaitForRising (IndexSeconds 10) - | Done TestStatus - deriving (Generic, Show, ShowX, NFDataX) - --- | State for 'genFsm' -data GenFsmState - = -- | Lie low for a bit - GInReset - | -- | Drive SYNC_OUT _low_ for exactly one second - GLow (IndexSeconds 1) - | -- | Drive SYNC_OUT _high_ indefinitely - GHigh - deriving (Generic, Show, ShowX, NFDataX) - --- | Check SYNC_IN. See Module documenation for more information. -testFsm :: TestFsmState -> Bool -> (TestFsmState, TestStatus) -testFsm InReset _ = (ExpectLow maxBound, Busy) -testFsm (ExpectLow _) True = (Done Fail, Busy) -testFsm (ExpectLow 0) False = (WaitForRising maxBound, Busy) -testFsm (ExpectLow n) False = (ExpectLow (n - 1), Busy) -testFsm (WaitForRising _) True = (Done Success, Busy) -testFsm (WaitForRising 0) False = (Done Fail, Busy) -testFsm (WaitForRising n) False = (WaitForRising (n - 1), Busy) -testFsm s@(Done result) _ = (s, result) - --- | Generate SYNC_OUT. See Module documenation for more information. -genFsm :: GenFsmState -> () -> (GenFsmState, Bool) -genFsm GInReset _ = (GLow maxBound, False) -genFsm (GLow 0) _ = (GHigh, False) -genFsm (GLow n) _ = (GLow (n - 1), False) -genFsm GHigh _ = (GHigh, True) - -{- | Convert a 'TestStatus' in to a pair of booleans @done@ and @success@. Used -to communicate test status to host computer. --} -testStatusToDoneSuccess :: TestStatus -> (Bool, Bool) -testStatusToDoneSuccess = \case - Busy -> (False, False) - Fail -> (True, False) - Success -> (True, True) - --- | Entry point for test. See module documentation for more information. -syncInSyncOut :: - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "SYNC_OUT" ::: Signal Basic125 Bool -syncInSyncOut sysClkDiff syncIn0 = syncOut - where - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - testRst = sysRst `orReset` unsafeFromActiveLow startTest - syncIn1 = - unsafeToActiveHigh - $ resetGlitchFilter (SNat @1024) sysClk - $ unsafeFromActiveHigh - $ xpmCdcSingle sysClk sysClk syncIn0 - - testStatus = mealy sysClk testRst enableGen testFsm InReset syncIn1 - (testDone, testSuccess) = unbundle (testStatusToDoneSuccess <$> testStatus) - - syncOut = - delay sysClk enableGen False - $ mealy sysClk testRst enableGen genFsm GInReset (pure ()) -- << filter glitches in output - startTest :: Signal Basic125 Bool - startTest = hitlVioBool sysClk testDone testSuccess - -makeTopEntity 'syncInSyncOut - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'syncInSyncOut - , extraXdcFiles = [] - , externalHdl = [] - , testCases = - [ HitlTestCase - { name = "SyncInSyncOut" - , parameters = paramForHwTargets allHwTargets () - , postProcData = () - } - ] - , mPostProc = Nothing - } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs b/bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs deleted file mode 100644 index e2b0d9b70..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs +++ /dev/null @@ -1,117 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} - -{- | Check whether the temperature of the system monitor in the FPGA is within a -given range. --} -module Bittide.Instances.Hitl.TemperatureMonitor where - -import Clash.Prelude - -import Data.Maybe (isJust) - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Xilinx.ClockGen (clockWizardDifferential) - -import Bittide.Arithmetic.Time (trueFor) -import Bittide.Hitl ( - HitlTestCase (..), - HitlTestGroup (..), - hitlVioBool, - paramForHwTargets, - ) -import Bittide.Instances.Hitl.Setup (allHwTargets) - -import Bittide.Instances.Domains - -import Clash.Cores.Xilinx.Ila (ila, ilaConfig) - -import qualified Clash.Cores.Xilinx.SystemMonitor as SysMon -import qualified Clash.Explicit.Prelude as E - -temperatureMonitor :: - "CLK_125MHZ" ::: DiffClock Ext125 -> - "" - ::: Signal - Basic200 - ( "done" ::: Bool - , "success" ::: Bool - ) -temperatureMonitor diffClk = temperatureIla `hwSeqX` bundle (testDone, testSuccess) - where - (clk, rst) = clockWizardDifferential diffClk E.noReset - - (status, temperatureMaybe) = - unbundle - $ withClockResetEnable clk rst enableGen SysMon.temperatureMonitorCelcius - temperature = E.regMaybe clk rst enableGen 0 temperatureMaybe - - testDone = trueFor (SNat @(Milliseconds 1200)) clk testRst (pure True) - testSuccess = - trueFor (SNat @(Seconds 1)) clk testRst - $ 20 - .<=. temperature - .&&. temperature .<=. 45 - - testRst = unsafeFromActiveLow $ hitlVioBool clk testDone testSuccess - - capture :: Signal Basic200 Bool - capture = - withClockResetEnable clk rst enableGen - $ onChange - $ bundle - ( temperature - , status - ) - where - onChange :: - (HiddenClockResetEnable dom, Eq a, NFDataX a) => Signal dom a -> Signal dom Bool - onChange x = (Just <$> x) ./=. register Nothing (Just <$> x) - - temperatureIla :: Signal Basic200 () - temperatureIla = - setName @"temperatureIla" - $ ila - ( ilaConfig - $ "trigger" - :> "capture" - :> "probe_temperatureReady" - :> "probe_temperature" - :> "probe_busy" - :> "probe_channel" - :> "probe_endOfConversion" - :> "probe_endOfSequence" - :> Nil - ) - clk - -- Trigger as soon as we come out of reset - (unsafeToActiveLow rst) - capture - -- Debug probes - (isJust <$> temperatureMaybe) - temperature - ((.busy) <$> status) - ((.channel) <$> status) - ((.endOfConversion) <$> status) - ((.endOfSequence) <$> status) - -makeTopEntity 'temperatureMonitor - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'temperatureMonitor - , extraXdcFiles = [] - , externalHdl = [] - , testCases = - [ HitlTestCase - { name = "TemperatureMonitor" - , parameters = paramForHwTargets allHwTargets () - , postProcData = () - } - ] - , mPostProc = Nothing - } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs b/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs deleted file mode 100644 index 9d17f8f1f..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs +++ /dev/null @@ -1,51 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} - -{- | Full definitions of HITL tests. For every test, this includes: - - 1. The fully qualified name of the function that is the top-level Clash - circuit. The test controller will compile, synthesize and implement this - and program the relevant hardware targets (FPGAs). - - 2. The HITL test configuration. See `Bittide.Hitl.HitlTestGroup`. --} -module Bittide.Instances.Hitl.Tests ( - ClashTargetName, - HitlTestGroup (..), - HitlTestCase (..), - hitlTests, -) where - -import Bittide.Hitl (ClashTargetName, HitlTestCase (..), HitlTestGroup (..)) - -import qualified Bittide.Instances.Hitl.BoardTest as BoardTest -import qualified Bittide.Instances.Hitl.Ethernet as Ethernet -import qualified Bittide.Instances.Hitl.FincFdec as FincFdec -import qualified Bittide.Instances.Hitl.FullMeshHwCc as FullMeshHwCc -import qualified Bittide.Instances.Hitl.FullMeshSwCc as FullMeshSwCc -import qualified Bittide.Instances.Hitl.HwCcTopologies as HwCcTopologies -import qualified Bittide.Instances.Hitl.LinkConfiguration as LinkConfiguration -import qualified Bittide.Instances.Hitl.SyncInSyncOut as SyncInSyncOut -import qualified Bittide.Instances.Hitl.TemperatureMonitor as TemperatureMonitor -import qualified Bittide.Instances.Hitl.Transceivers as Transceivers -import qualified Bittide.Instances.Hitl.VexRiscv as VexRiscv - -hitlTests :: [HitlTestGroup] -hitlTests = - [ BoardTest.testSimple - , BoardTest.testExtended - , FincFdec.tests - , FullMeshHwCc.fullMeshHwCcTest' - , FullMeshHwCc.fullMeshHwCcWithRiscvTest' - , FullMeshSwCc.tests - , HwCcTopologies.tests - , LinkConfiguration.tests - , TemperatureMonitor.tests - , SyncInSyncOut.tests - , Transceivers.tests - , VexRiscv.tests - , Ethernet.tests - ] diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs b/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs deleted file mode 100644 index b449587b6..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs +++ /dev/null @@ -1,250 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} - -{- | Test whether clock boards are configurable and transceiver links come -online. This assumes to run on a fully connected mesh of 8 FPGAs. Also see -'c_CHANNEL_NAMES' and 'c_CLOCK_PATHS'. It has two tricks up its sleeve: - - 1. It uses @SYNC_IN@/@SYNC_OUT@ to make sure each board starts programming - its clock boards at the same time. - - 2. It keeps track of how many times the GTH's reset manager had to reset - the connection and how often it lost connections after establishing - them. - -This test will succeed if all links have been up for ten seconds. --} -module Bittide.Instances.Hitl.Transceivers where - -import Clash.Explicit.Prelude -import Clash.Prelude (withClockResetEnable) - -import Bittide.Arithmetic.Time -import Bittide.ClockControl.Si5395J -import Bittide.ClockControl.Si539xSpi -import Bittide.ElasticBuffer (sticky) -import Bittide.Hitl -import Bittide.Instances.Domains -import Bittide.Instances.Hitl.Setup -import Bittide.Transceiver - -import Clash.Annotations.TH (makeTopEntity) -import Clash.Cores.Xilinx.GTH -import Clash.Cores.Xilinx.Xpm.Cdc.Single (xpmCdcSingle) -import Clash.Xilinx.ClockGen -import Data.Maybe (fromMaybe, isJust) - -import qualified Bittide.Transceiver.ResetManager as ResetManager -import qualified Clash.Explicit.Prelude as E -import qualified Data.List as L -import qualified Data.Map as Map - -{- | Start value of the counters used in 'counter' and 'expectCounter'. This is -a non-zero start value, as a regression test for a bug where the transceivers -would not come up if the counters started at zero. --} -counterStart :: BitVector 64 -counterStart = 0xDEAD_BEEF_CA55_E77E - --- | A counter starting at 'counterStart' -counter :: - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Signal dom Bool -> - Signal dom (BitVector 64) -counter clk rst ena = let c = register clk rst (toEnable ena) counterStart (c + 1) in c - -{- | Expect a counter starting at 'counterStart' and incrementing by one on each -cycle. --} -expectCounter :: - (KnownDomain dom) => - Clock dom -> - Reset dom -> - -- | Received data - Signal dom (Maybe (BitVector 64)) -> - -- | Error - Signal dom Bool -expectCounter clk rst = sticky clk rst . mealy clk rst enableGen go counterStart - where - go c (Just e) = (c + 1, c /= e) - go c Nothing = (c, False) - -{- | Worker function for 'transceiversUpTest'. See module documentation for more -information. --} -goTransceiversUpTest :: - Signal Basic125 (Index FpgaCount) -> - "SMA_MGT_REFCLK_C" ::: Clock Ext200 -> - "SYSCLK" ::: Clock Basic125 -> - "RST_LOCAL" ::: Reset Basic125 -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "allUp" ::: Signal Basic125 Bool - , "anyErrors" ::: Signal Basic125 Bool - , "stats" ::: Vec LinkCount (Signal Basic125 ResetManager.Statistics) - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -goTransceiversUpTest fpgaIndex refClk sysClk rst rxNs rxPs miso = - ( transceivers.txNs - , transceivers.txPs - , allUp - , expectCounterErrorSys - , transceivers.stats - , spiDone - , spiOut - ) - where - allUp = and <$> bundle transceivers.linkUps - - sysRst = orReset rst (unsafeFromActiveLow (fmap not spiErr)) - - -- Clock programming - spiDone = E.dflipflop sysClk $ (== Finished) <$> spiState - spiErr = E.dflipflop sysClk $ isErr <$> spiState - - isErr (Error _) = True - isErr _ = False - - (_, _, spiState, spiOut) = - withClockResetEnable sysClk sysRst enableGen - $ si539xSpi testConfig6_200_on_0a_1ppb (SNat @(Microseconds 10)) (pure Nothing) miso - - -- Transceiver setup - gthAllReset = unsafeFromActiveLow spiDone - - -- Send counters - counters = - zipWith3 - counter - transceivers.txClocks - transceivers.txResets - transceivers.txSamplings - - expectCounterError = - zipWith3 - expectCounter - transceivers.rxClocks - transceivers.rxResets - transceivers.rxDatas - - expectCounterErrorSys = - fmap and - $ bundle - $ zipWith (.&&.) transceivers.linkUps - $ zipWith (`xpmCdcSingle` sysClk) transceivers.rxClocks expectCounterError - - transceivers = - transceiverPrbsN - @GthTx - @GthRx - @Ext200 - @Basic125 - @GthTxS - @GthRxS - defConfig{debugIla = True, debugFpgaIndex = bitCoerce <$> fpgaIndex} - Inputs - { clock = sysClk - , reset = gthAllReset - , refClock = refClk - , channelNames - , clockPaths - , rxNs - , rxPs - , txDatas = counters - , txReadys = repeat (pure True) - , rxReadys = repeat (pure True) - } - --- | Top entity for this test. See module documentation for more information. -transceiversUpTest :: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "SYNC_IN" ::: Signal Basic125 Bool -> - "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> - "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> - "MISO" ::: Signal Basic125 Bit -> - ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount - , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount - , "SYNC_OUT" ::: Signal Basic125 Bool - , "spiDone" ::: Signal Basic125 Bool - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -transceiversUpTest refClkDiff sysClkDiff syncIn rxns rxps miso = - (txns, txps, syncOut, spiDone, spiOut) - where - refClk = ibufds_gte3 refClkDiff :: Clock Ext200 - - (sysClk, sysRst) = clockWizardDifferential sysClkDiff noReset - - testRst = sysRst `orReset` unsafeFromActiveLow startTest `orReset` syncInRst - syncOut = startTest - syncInRst = - resetGlitchFilter (SNat @1024) sysClk - $ unsafeFromActiveLow - $ xpmCdcSingle sysClk sysClk syncIn - - (txns, txps, allUp, anyErrors, _stats, spiDone, spiOut) = - goTransceiversUpTest fpgaIndex refClk sysClk testRst rxns rxps miso - - failAfterUp = isFalling sysClk testRst enableGen False allUp - failAfterUpSticky = sticky sysClk testRst failAfterUp - - startTest = isJust <$> maybeFpgaIndex - fpgaIndex = fromMaybe 0 <$> maybeFpgaIndex - - maybeFpgaIndex :: Signal Basic125 (Maybe (Index FpgaCount)) - maybeFpgaIndex = - hitlVio - 0 - sysClk - -- Consider test done if links have been up consistently for 40 seconds. This - -- is just below the test timeout of 60 seconds, so transceivers have ~20 - -- seconds to come online reliably. This should be plenty. - (trueFor (SNat @(Seconds 40)) sysClk testRst allUp .||. failAfterUpSticky .||. anyErrors) - -- Success? - (fmap not failAfterUpSticky .&&. fmap not anyErrors) - -makeTopEntity 'transceiversUpTest - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'transceiversUpTest - , externalHdl = [] - , extraXdcFiles = [] - , testCases = iters - , mPostProc = Nothing - } - where - fpgaIndices = [0 ..] :: [Index FpgaCount] - nIters = 1 - iterNames = ["I" <> show n | n <- [(0 :: Int) .. nIters - 1]] - iters = - [ HitlTestCase - { name = nm - , parameters = - Map.fromList (L.zip (HwTargetByIndex . fromIntegral <$> fpgaIndices) fpgaIndices) - , postProcData = () - } - | nm <- iterNames - ] diff --git a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs b/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs deleted file mode 100644 index ac457fc48..000000000 --- a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs +++ /dev/null @@ -1,166 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - --- {-# OPTIONS -fplugin-opt=Protocols.Plugin:debug #-} - -module Bittide.Instances.Hitl.VexRiscv where - -import Clash.Annotations.TH (makeTopEntity) - -import Clash.Explicit.Prelude (noReset, orReset) -import Clash.Prelude - -import Clash.Cores.UART (ValidBaud) -import Clash.Xilinx.ClockGen (clockWizardDifferential) -import Protocols -import Protocols.Wishbone -import VexRiscv - -import Bittide.DoubleBufferedRam -import Bittide.Hitl -import Bittide.Instances.Domains (Basic125, Ext125) -import Bittide.ProcessingElement -import Bittide.SharedTypes -import Bittide.Wishbone - -data TestStatus = Running | Success | Fail - deriving (Enum, Eq, Generic, NFDataX, BitPack) - -type TestDone = Bool -type TestSuccess = Bool -type UartRx = Bit -type UartTx = Bit - -vexRiscvInner :: - forall dom. - ( HiddenClockResetEnable dom - , 1 <= DomainPeriod dom - , ValidBaud dom 921600 - ) => - Signal dom JtagIn -> - Signal dom UartRx -> - ( Signal dom (TestDone, TestSuccess) - , Signal dom JtagOut - , Signal dom UartTx - ) -vexRiscvInner jtagIn0 uartRx = - ( stateToDoneSuccess <$> status - , jtagOut - , uartTx - ) - where - stateToDoneSuccess Running = (False, False) - stateToDoneSuccess Success = (True, True) - stateToDoneSuccess Fail = (True, False) - - ((_, jtagOut), (status, uartTx)) = - circuitFn ((uartRx, jtagIn0), (pure (), pure ())) - - Circuit circuitFn = circuit $ \(uartRx, jtag) -> do - [timeBus, uartBus, statusRegisterBus] <- processingElement peConfig -< jtag - (uartTx, _uartStatus) <- uartWb @dom d16 d16 (SNat @921600) -< (uartBus, uartRx) - timeWb -< timeBus - testResult <- statusRegister -< statusRegisterBus - idC -< (testResult, uartTx) - - statusRegister :: Circuit (Wishbone dom 'Standard 27 (Bytes 4)) (CSignal dom TestStatus) - statusRegister = Circuit $ \(fwd, _) -> - let (unbundle -> (m2s, st)) = mealy go Running fwd - in (m2s, st) - where - go st WishboneM2S{..} - -- out of cycle, no response, same state - | not (busCycle && strobe) = (st, (emptyWishboneS2M, st)) - -- already done, ACK and same state - | st /= Running = (st, (emptyWishboneS2M{acknowledge = True}, st)) - -- read, this is write-only, so error, same state - | not writeEnable = - ( st - , - ( (emptyWishboneS2M @(Bytes 4)) - { err = True - , readData = errorX "status register is write-only" - } - , st - ) - ) - -- write! change state, ACK - | otherwise = - let state = case writeData of - 1 -> Success - _ -> Fail - in (state, (emptyWishboneS2M{acknowledge = True}, state)) - - -- ╭────────┬───────┬───────┬────────────────────────────────────╮ - -- │ bin │ hex │ bus │ description │ - -- ├────────┼───────┼───────┼────────────────────────────────────┤ - -- │ 0b000. │ 0x0 │ │ │ - -- │ 0b001. │ 0x2 │ │ │ - -- │ 0b010. │ 0x4 │ 1 │ Data memory │ - -- │ 0b011. │ 0x6 │ │ │ - -- │ 0b100. │ 0x8 │ 0 │ Instruction memory │ - -- │ 0b101. │ 0xA │ 2 │ Time │ - -- │ 0b110. │ 0xC │ 3 │ UART │ - -- │ 0b111. │ 0xE │ 4 │ Test status register │ - -- ╰────────┴───────┴───────┴────────────────────────────────────╯ - -- - -- peConfig :: PeConfig 5 - peConfig = - PeConfig - (0b100 :> 0b010 :> 0b101 :> 0b110 :> 0b111 :> Nil) - (Undefined @(Div (64 * 1024) 4)) -- 64 KiB - (Undefined @(Div (64 * 1024) 4)) -- 64 KiB - -vexRiscvTest :: - "CLK_125MHZ" ::: DiffClock Ext125 -> - "JTAG" ::: Signal Basic125 JtagIn -> - "USB_UART_TXD" ::: Signal Basic125 UartRx -> - "" - ::: ( "done" ::: Signal Basic125 TestDone - , "success" ::: Signal Basic125 TestSuccess - , "JTAG" ::: Signal Basic125 JtagOut - , "USB_UART_RXD" ::: Signal Basic125 UartTx - ) -vexRiscvTest diffClk jtagIn uartRx = (testDone, testSuccess, jtagOut, uartTx) - where - (clk, clkStableRst) = clockWizardDifferential diffClk noReset - - (_, jtagOut, uartTx) = - withClockResetEnable clk reset enableGen (vexRiscvInner @Basic125 jtagIn uartRx) - - reset = orReset clkStableRst (unsafeFromActiveLow testStarted) - - testStarted :: Signal Basic125 Bool - testStarted = hitlVioBool clk testDone testSuccess - - -- TODO: We used to perform a HITL test where the CPU would write to a success - -- register (or a failure register when it would get trapped). We - -- currently load programs over JTAG instead of preloading them in the - -- bitstream, making this impossible to do. We should add a _pre_ - -- processing step to the HITL infrastructure, restoring the ability to - -- do this once more. - testDone = testStarted - testSuccess = testStarted -{-# NOINLINE vexRiscvTest #-} -makeTopEntity 'vexRiscvTest - -tests :: HitlTestGroup -tests = - HitlTestGroup - { topEntity = 'vexRiscvTest - , extraXdcFiles = ["jtag_config.xdc", "jtag_pmod1.xdc"] - , externalHdl = [] - , testCases = - [ HitlTestCase - { name = "VexRiscV" - , parameters = paramForSingleHwTarget (HwTargetByIndex 7) () - , postProcData = () - } - ] - , mPostProc = Just "post-vex-riscv-test" - } diff --git a/bittide-instances/src/Bittide/Instances/Pnr/Calendar.hs b/bittide-instances/src/Bittide/Instances/Pnr/Calendar.hs deleted file mode 100644 index d099d80c6..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/Calendar.hs +++ /dev/null @@ -1,45 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} - -module Bittide.Instances.Pnr.Calendar where - -import Clash.Prelude - -import Bittide.Calendar -import Bittide.Instances.Domains (Basic200) -import Bittide.SharedTypes -import Bittide.Switch as SW -import Protocols.Wishbone - -import Bittide.Instances.Hacks (reducePins) - -type WishboneWidth = 4 -type WishboneAddrWidth = 32 - -switchCalendar1k :: - Clock Basic200 -> - Reset Basic200 -> - Signal Basic200 (WishboneM2S WishboneAddrWidth WishboneWidth (Bytes WishboneWidth)) -> - ( Signal Basic200 (Vec 15 (CrossbarIndex 15)) - , Signal Basic200 Bool - , Signal Basic200 (WishboneS2M (Bytes WishboneWidth)) - ) -switchCalendar1k clk rst = - withClockResetEnable clk syncRst enableGen - $ mkCalendar (CalendarConfig (SNat @1024) cal cal) - where - syncRst = resetSynchronizer clk rst - cal = ValidEntry{veEntry = repeat 0, veRepeat = 0 :: Unsigned 8} :> Nil -{-# NOINLINE switchCalendar1k #-} - -switchCalendar1kReducedPins :: - Clock Basic200 -> - Reset Basic200 -> - Signal Basic200 Bit -> - Signal Basic200 Bit -switchCalendar1kReducedPins clk rst = - withClock clk - $ reducePins (bundle . switchCalendar1k clk rst) -{-# NOINLINE switchCalendar1kReducedPins #-} diff --git a/bittide-instances/src/Bittide/Instances/Pnr/ClockControl.hs b/bittide-instances/src/Bittide/Instances/Pnr/ClockControl.hs deleted file mode 100644 index b6dc87ef9..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/ClockControl.hs +++ /dev/null @@ -1,28 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Bittide.Instances.Pnr.ClockControl where - -import Clash.Prelude - -import Bittide.ClockControl -import Bittide.ClockControl.Callisto -import Bittide.Instances.Domains - -config :: ClockControlConfig Basic200 12 8 1500000 -config = $(lift (defClockConfig @Basic200)) - -callisto3 :: - Clock Basic200 -> - Reset Basic200 -> - Enable Basic200 -> - -- | Data counts from elastic buffers - Vec 3 (Signal Basic200 (RelDataCount 12)) -> - -- | Speed change requested from clock multiplier - Signal Basic200 (CallistoResult 3) -callisto3 clk rst ena dataCounts = - callistoClockControl clk rst ena config availableLinkMask dataCounts - where - -- all links available - availableLinkMask = pure $ complement 0 diff --git a/bittide-instances/src/Bittide/Instances/Pnr/Counter.hs b/bittide-instances/src/Bittide/Instances/Pnr/Counter.hs deleted file mode 100644 index 5050bed28..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/Counter.hs +++ /dev/null @@ -1,27 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Bittide.Instances.Pnr.Counter where - -import Clash.Explicit.Prelude -import Clash.Prelude (withClock) - -import Bittide.Counter -import Bittide.Instances.Domains -import Bittide.Instances.Hacks - -counter :: - Clock Basic200 -> - Reset Basic200 -> - Clock Basic200 -> - Reset Basic200 -> - Signal Basic200 () -> - Signal Basic200 (Signed 32, Bool) -counter clk0 rst0 clk1 rst1 _ = - domainDiffCounter clk0 rst0 clk1 rst1 - -counterReducedPins :: Clock Basic200 -> Signal Basic200 Bit -counterReducedPins clk = - withClock clk - $ reducePins (counter clk noReset clk noReset) (pure 0) diff --git a/bittide-instances/src/Bittide/Instances/Pnr/ElasticBuffer.hs b/bittide-instances/src/Bittide/Instances/Pnr/ElasticBuffer.hs deleted file mode 100644 index ef97257de..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/ElasticBuffer.hs +++ /dev/null @@ -1,30 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# OPTIONS_GHC -Wno-orphans #-} - -module Bittide.Instances.Pnr.ElasticBuffer where - -import Clash.Annotations.TH -import Clash.Prelude - -import Bittide.ClockControl (RelDataCount) -import Bittide.ElasticBuffer - -createDomain vXilinxSystem{vPeriod = hzToPeriod 201e6, vName = "Fast"} -createDomain vXilinxSystem{vPeriod = hzToPeriod 199e6, vName = "Slow"} - -elasticBuffer5 :: - "clkReadFast" ::: Clock Fast -> - "clkWriteSlow" ::: Clock Slow -> - "resetRead" ::: Reset Fast -> - "writeData" ::: Signal Slow (Unsigned 8) -> - ( "dataCount" ::: Signal Fast (RelDataCount 5) - , "underflow" ::: Signal Fast Underflow - , "overrflow" ::: Signal Fast Overflow - , "ebMode" ::: Signal Fast EbMode - , "readData" ::: Signal Fast (Unsigned 8) - ) -elasticBuffer5 = resettableXilinxElasticBuffer - -makeTopEntity 'elasticBuffer5 diff --git a/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs b/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs deleted file mode 100644 index 251e0dfad..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs +++ /dev/null @@ -1,188 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# OPTIONS -fplugin=Protocols.Plugin #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE RecordWildCards #-} - -module Bittide.Instances.Pnr.Ethernet where - -import Clash.Explicit.Prelude -import Clash.Explicit.Reset.Extra -import Clash.Prelude (HiddenClockResetEnable, withClockResetEnable) - -import Clash.Cores.UART (ValidBaud) -import Clash.Cores.Xilinx.Ethernet.Gmii -import Clash.Cores.Xilinx.Unisim.DnaPortE2 (simDna2) -import Protocols -import VexRiscv - -import Bittide.Axi4 -import Bittide.DoubleBufferedRam -import Bittide.Ethernet.Mac -import Bittide.Instances.Domains -import Bittide.ProcessingElement -import Bittide.Wishbone -import Protocols.Idle - -type Baud = 921_600 - -baud :: SNat Baud -baud = SNat - -{- | Instance containing: -* VexRiscv CPU -* UART -* Free running timer -* GPIO -* Ethernet MAC --} -vexRiscGmii :: - forall logic rx tx gpioWidth. - ( KnownDomain logic - , KnownDomain rx - , KnownDomain tx - , KnownNat (DomainPeriod logic) - , 1 <= DomainPeriod logic - , ValidBaud logic 921600 - ) => - SNat gpioWidth -> - Clock logic -> - Reset logic -> - Clock rx -> - Reset rx -> - Clock tx -> - Reset tx -> - ( Signal logic Bit - , Signal rx Gmii - , Signal logic JtagIn - ) -> - ( Signal logic Bit - , Signal tx Gmii - , Signal logic JtagOut - , Signal logic (BitVector gpioWidth) - ) -vexRiscGmii SNat sysClk sysRst rxClk rxRst txClk txRst fwd = - (\((_, _, jtagBwd), (uartFwd, gmiiFwd, gpioFwd)) -> (uartFwd, gmiiFwd, jtagBwd, gpioFwd)) - $ toSignals - ( circuit $ \(uartTx, gmiiRx, jtag) -> do - [uartBus, timeBus, wbAxiRx, wbAxiTx, dnaWb, gpioWb, macWb] <- pe -< jtag - (uartRx, _uartStatus) <- uart -< (uartBus, uartTx) - time -< timeBus - dna -< dnaWb - macStatIf -< (macWb, macStatus) - gpioDf <- idleSource -< () - gpioOut <- gpio -< (gpioWb, gpioDf) - (axiRx0, gmiiTx, macStatus) <- mac -< (axiTx1, gmiiRx) - axiRx1 <- axiRxPipe -< axiRx0 - axiTx0 <- wbToAxiTx' -< wbAxiTx - axiTx1 <- axiTxPipe -< axiTx0 - _rxBufStatus <- wbAxiRxBuffer -< (wbAxiRx, axiRx1) - - idC -< (uartRx, gmiiTx, gpioOut) - ) - (fwd, (pure (), pure (), pure ())) - where - time = wcre timeWb - dna = wcre readDnaPortE2Wb simDna2 - mac = - ethMac1GFifoC - (SNat @1500) - (SNat @1500) - sysClk - sysRst - txClk - txRst - rxClk - rxRst - miiSel - txClkEna - rxClkEna - macStatIf = wcre $ macStatusInterfaceWb d16 - uart = wcre uartWb d32 d2 baud - pe = wcre processingElement peConfig - wbToAxiTx' = wcre wbToAxiTx - wbAxiRxBuffer = wcre wbAxisRxBufferCircuit (SNat @2048) - axiTxPipe = wcre (axiUserMapC (const False) <| axiStreamToByteStream) - axiRxPipe = wcre (axiUserMapC or <| axiStreamFromByteStream) - gpio = wcre $ registerWbC WishbonePriority (0 :: BitVector gpioWidth) - miiSel = pure False - rxClkEna = pure True - txClkEna = pure True - wcre :: (((HiddenClockResetEnable logic) => a) -> a) - wcre = withClockResetEnable sysClk sysRst enableGen - - peConfig = - PeConfig - ( 0b1000 - :> 0b0001 - :> 0b0010 - :> 0b0011 - :> 0b0101 - :> 0b0110 - :> 0b0111 - :> 0b0100 - :> 0b1001 - :> Nil - ) - (Undefined @(256 * 1024)) - (Undefined @(64 * 1024)) - -vexRiscEthernet :: - Clock Basic125B -> - Reset Basic125B -> - DiffClock Basic625 -> - ( Signal Basic125B JtagIn - , Signal Basic125B Bit - , Signal Basic625 Lvds - ) -> - ( Signal Basic125B JtagOut - , Signal Basic125B Bit - , Signal Basic625 Lvds - , Signal Basic125B (BitVector 32) - ) -vexRiscEthernet sysClk sysRst sgmiiPhyClk (jtagin, uartIn, sgmiiIn) = - (jtagOut, uartOut, bridgeLvdsOut, gpioOut) - where - BridgeOutput{..} = bridge sgmiiIn gmiiOut - signalDetect = pure True - anRestart = pure False - conf = pure def{cAutoNegEnable = True} - anConf = - pure - def - { cAcknowledge = True - , cDuplexMode = FullDuplex - , cLinkSpeed = Speed1000 - , cPhyLinkStatus = True - } - bridge = gmiiSgmiiBridge sgmiiPhyClk bridgeRst signalDetect conf anConf anRestart - rxClk = bridgeClk125 :: Clock Basic125A - rxRst = bridgeRst125 - bridgeRst = unsafeResetDesynchronizer sysClk sysRst - (uartOut, gmiiOut, jtagOut, gpioOut) = vexRiscGmii SNat sysClk sysRst rxClk rxRst rxClk rxRst (uartIn, bridgeGmiiRx, jtagin) - -{- | Take a synchronous reset from one domain and convert it to an asynchronous reset. -This inserts a register in the source domain to prevent glitching and then converts the domain. -Note that the target domain is merely an implementation detail imposed by the digital -abstraction. The resulting reset is not synchronous to the target domain. --} -unsafeResetDesynchronizer :: - forall domA domS. - (KnownDomain domA, KnownDomain domS, HasSynchronousReset domS, HasAsynchronousReset domA) => - -- | Clock in the source domain - Clock domS -> - -- | Synchronous reset in the source domain - Reset domS -> - -- | Asynchronous reset in the "target" domain - Reset domA -unsafeResetDesynchronizer clkIn = - unsafeFromActiveHigh - . unsafeSynchronizer clkIn clockGen - . unsafeToActiveHigh - . delayReset Asserted clkIn - --- unsafeSynchronizer needs a clock in the target domain for simulation purposes, we --- can only use clockGen here because the black box of unsafeSynchronizer does --- not use the clock (it becomes a wire in the generated HDL). diff --git a/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs b/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs deleted file mode 100644 index ceb7c6d28..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs +++ /dev/null @@ -1,87 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -module Bittide.Instances.Pnr.ProcessingElement where - -import Clash.Prelude - -import Clash.Annotations.TH -import Clash.Explicit.Prelude (noReset, orReset) -import Clash.Xilinx.ClockGen -import Language.Haskell.TH -import Protocols -import System.FilePath -import VexRiscv - -import Bittide.DoubleBufferedRam -import Bittide.Instances.Domains -import Bittide.ProcessingElement -import Bittide.ProcessingElement.Util -import Bittide.SharedTypes -import Bittide.Wishbone -import Project.FilePath - -{- | A simple instance containing just VexRisc and UART as peripheral. -Runs the `hello` binary from `firmware-binaries`. --} -vexRiscUartHello :: - "SYSCLK_125" ::: DiffClock Ext125 -> - "CPU_RESET" ::: Reset Basic200 -> - ( "" - ::: ( "USB_UART_TX" ::: Signal Basic200 Bit - , "JTAG" ::: Signal Basic200 JtagIn - ) - , Signal Basic200 () - ) -> - ( "" - ::: ( "" ::: Signal Basic200 () - , "JTAG" ::: Signal Basic200 JtagOut - ) - , "USB_UART_RX" ::: Signal Basic200 Bit - ) -vexRiscUartHello diffClk rst_in = - toSignals - $ withClockResetEnable clk200 rst200 enableGen - $ circuit - $ \(uartRx, jtag) -> do - [uartBus, timeBus] <- processingElement @Basic200 peConfig -< jtag - (uartTx, _uartStatus) <- uartWb d16 d16 (SNat @921600) -< (uartBus, uartRx) - timeWb -< timeBus - idC -< uartTx - where - (clk200, rst200_) = clockWizardDifferential diffClk noReset - rst200 = rst200_ `orReset` rst_in - (iMem, dMem) = - $( do - root <- runIO $ findParentContaining "cabal.project" - let - elfDir = root firmwareBinariesDir "riscv32imc-unknown-none-elf" Debug - elfPath = elfDir "hello" - iSize = 64 * 1024 -- 64 KB - dSize = 64 * 1024 -- 64 KB - memBlobsFromElf BigEndian (Just iSize, Just dSize) elfPath Nothing - ) - - -- ╭────────┬───────┬───────┬────────────────────╮ - -- │ bin │ hex │ bus │ description │ - -- ├────────┼───────┼───────┼────────────────────┤ - -- │ 0b000. │ 0x0 │ │ INSTR │ - -- │ 0b001. │ 0x2 │ │ │ - -- │ 0b010. │ 0x4 │ │ DATA │ - -- │ 0b011. │ 0x6 │ │ │ - -- │ 0b100. │ 0x8 │ │ UART │ - -- │ 0b101. │ 0xA │ │ │ - -- │ 0b110. │ 0xC │ │ TIME │ - -- │ 0b111. │ 0xE │ │ │ - -- ╰────────┴───────┴───────┴────────────────────╯ - - peConfig = - PeConfig - (0b00 :> 0b01 :> 0b10 :> 0b11 :> Nil) - (Reloadable $ Blob iMem) - (Reloadable $ Blob dMem) - -makeTopEntity 'vexRiscUartHello diff --git a/bittide-instances/src/Bittide/Instances/Pnr/ScatterGather.hs b/bittide-instances/src/Bittide/Instances/Pnr/ScatterGather.hs deleted file mode 100644 index f9dffbdad..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/ScatterGather.hs +++ /dev/null @@ -1,118 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# OPTIONS_GHC -fconstraint-solver-iterations=7 #-} - -module Bittide.Instances.Pnr.ScatterGather where - -import Clash.Prelude - -import Bittide.Calendar -import Bittide.Instances.Domains -import Bittide.Instances.Hacks -import Bittide.ScatterGather -import Bittide.SharedTypes -import Protocols.Wishbone - -type FrameWidth = 64 -type NLinks = 16 -type WishboneAddrWidth = 32 -type WishboneWidth = 4 - -scatterCal1K :: ScatterConfig WishboneWidth WishboneAddrWidth -scatterCal1K = ScatterConfig cal - where - cal :: CalendarConfig WishboneWidth WishboneAddrWidth (Index 1024) - cal = - CalendarConfig - (SNat @1024) - (ValidEntry{veEntry = 0, veRepeat = 0 :: Unsigned 8} :> Nil) - (ValidEntry{veEntry = 0, veRepeat = 0 :: Unsigned 8} :> Nil) - -gatherCal1K :: GatherConfig WishboneWidth WishboneAddrWidth -gatherCal1K = GatherConfig cal - where - cal :: CalendarConfig WishboneWidth WishboneAddrWidth (Index 1024) - cal = - CalendarConfig - (SNat @1024) - (ValidEntry{veEntry = 0, veRepeat = 0 :: Unsigned 8} :> Nil) - (ValidEntry{veEntry = 0, veRepeat = 0 :: Unsigned 8} :> Nil) - -{-# ANN - scatterUnit1K - ( Synthesize - { t_name = "scatterUnit1K" - , t_inputs = - [ PortName "clk" - , PortName "rst" - , PortName "wbInCal" - , PortName "linkIn" - , PortName "wbInSu" - ] - , t_output = - PortProduct - "" - [ PortName "wbOutSu" - , PortName "wbOutCal" - ] - } - ) - #-} -scatterUnit1K :: - Clock Basic200 -> - Reset Basic200 -> - Signal Basic200 (WishboneM2S WishboneAddrWidth WishboneWidth (Bytes WishboneWidth)) -> - Signal Basic200 (DataLink 64) -> - Signal Basic200 (WishboneM2S WishboneAddrWidth WishboneWidth (Bytes WishboneWidth)) -> - ( Signal Basic200 (WishboneS2M (Bytes WishboneWidth)) - , Signal Basic200 (WishboneS2M (Bytes WishboneWidth)) - ) -scatterUnit1K clk rst = withClockResetEnable clk rst enableGen $ scatterUnitWb scatterCal1K -{-# NOINLINE scatterUnit1K #-} - -scatterUnit1KReducedPins :: - Clock Basic200 -> Reset Basic200 -> Signal Basic200 Bit -> Signal Basic200 Bit -scatterUnit1KReducedPins clk rst = - withClockResetEnable clk rst enableGen $ reducePins scatterUnit1K' - where - scatterUnit1K' (unbundle -> (a, b, c)) = bundle $ scatterUnit1K clk rst a b c - -{-# ANN - gatherUnit1K - ( Synthesize - { t_name = "gatherUnit1K" - , t_inputs = - [ PortName "clk" - , PortName "rst" - , PortName "wbInCal" - , PortName "wbInGu" - ] - , t_output = - PortProduct - "" - [ PortName "linkOut" - , PortName "wbOutGu" - , PortName "wbOutCal" - ] - } - ) - #-} -gatherUnit1K :: - Clock Basic200 -> - Reset Basic200 -> - Signal Basic200 (WishboneM2S WishboneAddrWidth WishboneWidth (Bytes WishboneWidth)) -> - Signal Basic200 (WishboneM2S WishboneAddrWidth WishboneWidth (Bytes WishboneWidth)) -> - ( Signal Basic200 (DataLink 64) - , Signal Basic200 (WishboneS2M (Bytes WishboneWidth)) - , Signal Basic200 (WishboneS2M (Bytes WishboneWidth)) - ) -gatherUnit1K clk rst = withClockResetEnable clk rst enableGen $ gatherUnitWb gatherCal1K -{-# NOINLINE gatherUnit1K #-} - -gatherUnit1KReducedPins :: - Clock Basic200 -> Reset Basic200 -> Signal Basic200 Bit -> Signal Basic200 Bit -gatherUnit1KReducedPins clk rst = - withClockResetEnable clk rst enableGen $ reducePins gatherUnit1K' - where - gatherUnit1K' (unbundle -> (a, b)) = bundle $ gatherUnit1K clk rst a b diff --git a/bittide-instances/src/Bittide/Instances/Pnr/Si539xSpi.hs b/bittide-instances/src/Bittide/Instances/Pnr/Si539xSpi.hs deleted file mode 100644 index 8329796dc..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/Si539xSpi.hs +++ /dev/null @@ -1,34 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -module Bittide.Instances.Pnr.Si539xSpi where - -import Clash.Prelude - -import Clash.Annotations.TH (makeTopEntity) - -import Bittide.ClockControl.Si5395J -import Bittide.ClockControl.Si539xSpi -import Bittide.Instances.Domains -import Bittide.SharedTypes - -si5391Spi :: - "CLK_125MHZ_P" ::: Clock Basic125 -> - "reset" ::: Reset Basic125 -> - "extOp" ::: Signal Basic125 (Maybe RegisterOperation) -> - "MISO" ::: Signal Basic125 Bit -> - "" - ::: ( "readByte" ::: Signal Basic125 (Maybe Byte) - , "BUSY" ::: Signal Basic125 Busy - , "STATE" ::: Signal Basic125 (ConfigState Basic125 TestConfig6_200_on_0a_TotalRegs) - , "" - ::: ( "SCLK" ::: Signal Basic125 Bool - , "MOSI" ::: Signal Basic125 Bit - , "CSB" ::: Signal Basic125 Bool - ) - ) -si5391Spi clk rst extOp miso = - withClockResetEnable clk rst enableGen - $ si539xSpi testConfig6_200_on_0a_1ppb (SNat @50000) extOp miso - -makeTopEntity 'si5391Spi diff --git a/bittide-instances/src/Bittide/Instances/Pnr/StabilityChecker.hs b/bittide-instances/src/Bittide/Instances/Pnr/StabilityChecker.hs deleted file mode 100644 index abe58a761..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/StabilityChecker.hs +++ /dev/null @@ -1,22 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE TypeApplications #-} - -module Bittide.Instances.Pnr.StabilityChecker where - -import Clash.Prelude - -import Bittide.ClockControl (RelDataCount) -import Bittide.ClockControl.StabilityChecker -import Bittide.Instances.Domains (Basic200) - -stabilityChecker_3_1M :: - Clock Basic200 -> - Reset Basic200 -> - Signal Basic200 (RelDataCount 16) -> - Signal Basic200 StabilityIndication -stabilityChecker_3_1M clk rst = - withClockResetEnable clk rst enableGen - $ stabilityChecker d3 (SNat @1_000_000) diff --git a/bittide-instances/src/Bittide/Instances/Pnr/Synchronizer.hs b/bittide-instances/src/Bittide/Instances/Pnr/Synchronizer.hs deleted file mode 100644 index b82159ef8..000000000 --- a/bittide-instances/src/Bittide/Instances/Pnr/Synchronizer.hs +++ /dev/null @@ -1,21 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Bittide.Instances.Pnr.Synchronizer where - -import Clash.Explicit.Prelude - -import Bittide.Instances.Domains - -import Clash.Annotations.TH (makeTopEntity) -import qualified Clash.Cores.Extra as Cores - -safeDffSynchronizer :: - "clk1" ::: Clock Basic200 -> - "clk2" ::: Clock Basic199 -> - "source" ::: Signal Basic200 Bit -> - "target" ::: Signal Basic199 Bit -safeDffSynchronizer clk1 clk2 = - Cores.safeDffSynchronizer @Basic200 @Basic199 @Bit clk1 clk2 0 -makeTopEntity 'safeDffSynchronizer diff --git a/bittide-instances/src/Paths/Bittide/Instances.hs b/bittide-instances/src/Paths/Bittide/Instances.hs deleted file mode 100644 index 8e0fa65b4..000000000 --- a/bittide-instances/src/Paths/Bittide/Instances.hs +++ /dev/null @@ -1,9 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Paths.Bittide.Instances ( - module Paths_bittide_instances, -) where - -import Paths_bittide_instances diff --git a/bittide-instances/src/Project/FilePath.hs b/bittide-instances/src/Project/FilePath.hs deleted file mode 100644 index 00d5736bc..000000000 --- a/bittide-instances/src/Project/FilePath.hs +++ /dev/null @@ -1,77 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -module Project.FilePath where - -import Prelude - -import Control.Exception -import System.Directory -import System.FilePath - -{- $setup ->>> import Clash.Prelude --} - -{- | Relative path to the build directory. - -Example: - ->>> buildDir -"_build" --} -buildDir :: FilePath -buildDir = "_build" - -{- | Relative path to the build directory. - -Example: - ->>> cargoDir -"_build/cargo" --} -cargoDir :: FilePath -cargoDir = buildDir "cargo" - -data CargoBuildType = Release | Debug - deriving (Eq) - -{- | Relative path to the firmware binaries directory. - -Example: - ->>> firmwareBinariesDir "riscv32imc-unknown-none-elf" Release -"_build/cargo/firmware-binaries/riscv32imc-unknown-none-elf/release" --} -firmwareBinariesDir :: String -> CargoBuildType -> FilePath -firmwareBinariesDir rustTargetArchitecture buildType = - cargoDir - "firmware-binaries" - rustBinSubDir rustTargetArchitecture buildType - -{- | Firmware binaries directory relative to cargo's target directory. - -Example: - ->>> rustBinSubDir "riscv32imc-unknown-none-elf" Release -"riscv32imc-unknown-none-elf/release" --} -rustBinSubDir :: String -> CargoBuildType -> FilePath -rustBinSubDir rustTargetArchitecture buildType = - rustTargetArchitecture - case buildType of - Release -> "release" - Debug -> "debug" - --- | Recursive function that returns a parent directory containing a certain filename. -findParentContaining :: String -> IO FilePath -findParentContaining filename = goUp =<< getCurrentDirectory - where - goUp :: FilePath -> IO FilePath - goUp path - | isDrive path = throwIO $ userError $ "Could not find " <> filename - | otherwise = do - exists <- doesFileExist (path filename) - if exists - then return path - else goUp (takeDirectory path) diff --git a/bittide-instances/src/Project/Handle.hs b/bittide-instances/src/Project/Handle.hs deleted file mode 100644 index ff0b07d89..000000000 --- a/bittide-instances/src/Project/Handle.hs +++ /dev/null @@ -1,54 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Project.Handle where - -import Prelude - -import Data.List.Extra (trim) -import System.IO - -import Test.Tasty.HUnit - -data Error = Ok | Error String -data Filter = Continue | Stop Error - -{- | Utility function that reads lines from a handle, and applies a filter to -each line. If the filter returns 'Continue', the function will continue -reading lines. If the filter returns @Stop Ok@, the function will return -successfully. If the filter returns @Stop (Error msg)@, the function will -fail with the given message. --} -expectLine :: (HasCallStack) => Handle -> (String -> Filter) -> Assertion -expectLine h f = do - line <- trim <$> hGetLine h - let cont = expectLine h f - if null line - then cont - else case f line of - Continue -> cont - Stop Ok -> pure () - Stop (Error msg) -> assertFailure msg - -{- | Utility function that reads lines from a handle, and waits for a specific -line to appear. Though this function does not fail in the traditional sense, -it will get stuck if the expected line does not appear. Only use in combination -with sensible time outs (also see 'main'). --} -waitForLine :: Handle -> String -> IO () -waitForLine h expected = - expectLine h $ \s -> - if s == expected - then Stop Ok - else Continue - --- Utility function that returns the remaining characters in a handle. -readRemainingChars :: Handle -> IO String -readRemainingChars h = do - rdy <- hReady h - if rdy - then do - c <- hGetChar h - (c :) <$> readRemainingChars h - else (pure "") diff --git a/bittide-instances/src/Project/Programs.hs b/bittide-instances/src/Project/Programs.hs deleted file mode 100644 index 3787728fe..000000000 --- a/bittide-instances/src/Project/Programs.hs +++ /dev/null @@ -1,55 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Project.Programs where - -import Prelude - -import Control.Monad (unless) -import Control.Monad.Extra (forM_) -import Data.List.Extra (isPrefixOf, trim) -import Paths.Bittide.Instances -import System.IO -import System.IO.Temp - -getOpenOcdStartPath :: IO FilePath -getOpenOcdStartPath = getDataFileName "data/openocd/start.sh" - -getPicocomStartPath :: IO FilePath -getPicocomStartPath = getDataFileName "data/picocom/start.sh" - -getTcpSprayPath :: IO FilePath -getTcpSprayPath = getDataFileName "data/tcpspray/start.sh" - -{- | XXX: Currently hardcoded to a very specific position. Maybe we could probe - using JTAG to see what device we're connected to? --} -getUartDev :: IO String -getUartDev = pure "/dev/serial/by-path/pci-0000:00:14.0-usb-0:5.1:1.1-port0" - -{- | Take a GDB script, create copy that echoes everything it's doing, and give its path to action - -This works by creating a temporary copy with @echo > {line}\n@ prepended to each non-comment, non-empty line. -This effectively emulates Bash's @set -x@ for the GDB script. -And can be used to wait for specific commands to be executed, or simply for debugging. - -After the action returns the generated file gets deleted automatically. --} -withAnnotatedGdbScriptPath :: FilePath -> (FilePath -> IO ()) -> IO () -withAnnotatedGdbScriptPath srcPath action = do - withSystemTempFile "gdb-script" $ \dstPath dstHandle -> do - withFile srcPath ReadMode $ \srcHandle -> do - srcLines <- lines <$> hGetContents srcHandle - forM_ srcLines $ \line -> do - let trimmedLine = trim line - unless - (null trimmedLine || "#" `isPrefixOf` trimmedLine) - ( hPutStr dstHandle "echo > " - >> hPutStr dstHandle line - >> hPutStrLn dstHandle "\\n" - ) - hPutStrLn dstHandle line - - hClose dstHandle - action dstPath diff --git a/bittide-instances/tests/Tests/OverflowResistantDiff.hs b/bittide-instances/tests/Tests/OverflowResistantDiff.hs deleted file mode 100644 index cf79abf41..000000000 --- a/bittide-instances/tests/Tests/OverflowResistantDiff.hs +++ /dev/null @@ -1,134 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} - -module Tests.OverflowResistantDiff where - -import Clash.Explicit.Prelude -import qualified Prelude as P - -import Hedgehog -import Hedgehog.Gen as Gen -import Hedgehog.Range as Range -import Test.Tasty -import Test.Tasty.Hedgehog - -import Clash.Hedgehog.Sized.Unsigned - -import qualified Data.List as List -import qualified GHC.TypeNats as TN - -import Bittide.Instances.Hitl.IlaPlot - -tests :: TestTree -tests = - testGroup - "OverflowResistantDiff" - [ testPropertyNamed - "test with step-wise incrementing counter" - "testStepwise" - $ ordTest True - , testPropertyNamed - "test with randomly incrementing counter" - "testStepwise" - $ ordTest False - ] - -ordTest :: Bool -> Property -ordTest stepwise = property $ do - dN <- forAll $ Gen.enum 0 (maxBitSize - 1) - dM <- forAll $ Gen.enum 0 (2 P.^ maxBitSize - 1) - case (TN.someNatVal dN, TN.someNatVal dM) of - ( SomeNat (snatProxy -> (SNat :: SNat n)) - , SomeNat (snatProxy -> (SNat :: SNat m)) - ) -> - do - inputCounterValues <- - fmap (0 :) - $ if stepwise - then return - $ flip List.unfoldr (0 :: Int, 0 :: Unsigned (n + 1)) - $ \(n, o) -> - if n == depth - then Nothing - else Just (o, (n + 1, satSucc SatWrap o)) - else - forAll - $ Gen.list (Range.singleton depth) - $ genUnsigned Range.linearBounded - - newRefPositions <- - forAll - $ Gen.list (Range.linear 10 100) - $ Gen.integral - $ Range.linear 0 (depth - 1) - - let - inputTriggerValues :: [Bool] - inputTriggerValues = (False :) - $ flip List.unfoldr (0, List.nub $ List.sort $ newRefPositions) - $ \(n, xs) -> - if n == depth - then Nothing - else Just $ case xs of - [] -> (False, (n + 1, [])) - x : xr -> (x == n, ((n + 1), if x == n then xr else xs)) - - -- the generated input lists should be of equal length - List.length inputCounterValues === List.length inputTriggerValues - - let - outputValues :: [DiffResult Integer] - outputValues = - fmap toInteger - <$> sampleWithResetN @System @(DiffResult (Index (m + 1))) - d1 - depth - ( \clk rst _ -> - overflowResistantDiff - clk - rst - (fromList inputTriggerValues) - (fromList inputCounterValues) - ) - - expectedOutputs = - List.tail - $ List.reverse - $ snd - $ List.foldl golden (Nothing, []) - $ List.zip inputCounterValues inputTriggerValues - - -- don't use fixed size numbers for the golden reference, so - -- there are basically no overflows and we just can compute - -- distance directly, where we discard the if it exceeds the - -- capacity the output type - golden :: - ( Maybe (Integer, Unsigned (n + 1)) - , [DiffResult Integer] - ) -> - (Unsigned (n + 1), Bool) -> - ( Maybe (Integer, Unsigned (n + 1)) - , [DiffResult Integer] - ) - - golden (_, xs) (c, True) = - (Just (0, c), Difference 0 : xs) - golden (Nothing, xs) _ = - (Nothing, NoReference : xs) - golden (Just (x, o), xs) (c, _) = - let y = x + toInteger (c - o) - out = - if y > toInteger (maxBound :: Index (m + 1)) - then TooLarge - else Difference y - in (Just (y, c), out : xs) - - -- the generated output lists should be of equal length - List.length outputValues === List.length expectedOutputs - - outputValues === expectedOutputs - where - maxBitSize = 8 - depth = 1000 :: Int diff --git a/bittide-instances/tests/Wishbone/Axi.hs b/bittide-instances/tests/Wishbone/Axi.hs deleted file mode 100644 index b8307ca83..000000000 --- a/bittide-instances/tests/Wishbone/Axi.hs +++ /dev/null @@ -1,160 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -module Wishbone.Axi where - -import Clash.Explicit.Prelude - -import Bittide.Axi4 -import Bittide.DoubleBufferedRam -import Bittide.Instances.Domains -import Bittide.ProcessingElement -import Bittide.ProcessingElement.Util -import Bittide.SharedTypes -import Bittide.Wishbone -import Clash.Cores.UART (ValidBaud, uart) -import Clash.Cores.UART.Extra (MaxBaudRate) -import Clash.Explicit.Testbench -import Clash.Prelude (withClockResetEnable) -import Clash.Xilinx.ClockGen -import Control.Monad (forM_) -import Data.Char -import Data.Maybe -import Data.Proxy -import Language.Haskell.TH -import Project.FilePath -import Protocols -import Protocols.Axi4.Stream -import Protocols.Wishbone -import System.FilePath -import Test.Tasty -import Test.Tasty.HUnit -import Test.Tasty.TH -import Text.Parsec -import Text.Parsec.String -import VexRiscv - -import qualified Protocols.DfConv as DfConv - -{- | Run the axi module self test with processingElement and inspect it's uart output. -The test returns names of tests and a boolean indicating if the test passed. --} -case_axi_stream_rust_self_test :: Assertion -case_axi_stream_rust_self_test = - -- Run the test with HUnit - case parseTestResults simResult of - Left errMsg -> assertFailure $ show errMsg <> "\n" <> simResult - Right results -> do - forM_ results $ \result -> assertResult result - where - assertResult (TestResult name (Just errMsg)) = assertFailure ("Test " <> name <> " failed with error \"" <> errMsg <> "\"") - assertResult (TestResult _ Nothing) = return () - baud = SNat @(MaxBaudRate Basic50) - clk = clockGen - rst = resetGen - ena = enableGen - simResult = fmap (chr . fromIntegral) $ catMaybes $ sampleN 500_000 uartStream - (uartStream, _, _) = withClockResetEnable (clockGen @Basic50) rst ena $ uart baud uartTx (pure Nothing) - (_, uartTx) = dut baud (clockToDiffClock clk) rst (pure 0, pure ()) - -{- | A simple instance containing just VexRisc and UART as peripheral. -Runs the `hello` binary from `firmware-binaries`. --} -dut :: - forall dom baud. - (KnownDomain dom, ValidBaud dom baud) => - SNat baud -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "CPU_RESET" ::: Reset dom -> - ("USB_UART_TX" ::: Signal dom Bit, Signal dom ()) -> - (Signal dom (), "USB_UART_RX" ::: Signal dom Bit) -dut baud diffClk rst_in = - toSignals - $ withClockResetEnable clk200 rst200 enableGen - $ circuit - $ \uartTx -> do - [uartBus, axiTxBus, wbNull, axiRxBus] <- processingElement @dom peConfig <| jtagIdle -< () - wbAlwaysAck -< wbNull - (uartRx, _uartStatus) <- uartWb d128 d2 baud -< (uartBus, uartTx) - _interrupts <- wbAxisRxBufferCircuit (SNat @128) -< (axiRxBus, axiStream) - axiStream <- - axiUserMapC (const False) - <| DfConv.fifo axiProxy axiProxy (SNat @1024) - <| axiPacking - <| wbToAxiTx - -< axiTxBus - idC -< uartRx - where - axiProxy = Proxy @(Axi4Stream dom ('Axi4StreamConfig 4 0 0) ()) - (clk200 :: Clock dom, pllLock :: Reset dom) = clockWizardDifferential diffClk noReset - rst200 = resetSynchronizer clk200 (unsafeOrReset rst_in pllLock) - - (iMem, dMem) = - $( do - root <- runIO $ findParentContaining "cabal.project" - let - elfDir = root firmwareBinariesDir "riscv32imc-unknown-none-elf" Release - elfPath = elfDir "axi_stream_self_test" - memBlobsFromElf BigEndian (Nothing, Nothing) elfPath Nothing - ) - - jtagIdle = Circuit $ const ((), pure $ JtagIn low low low) - peConfig = - PeConfig - (0b000 :> 0b001 :> 0b010 :> 0b011 :> 0b100 :> 0b101 :> Nil) - (Reloadable $ Blob iMem) - (Reloadable $ Blob dMem) - -data TestResult = TestResult String (Maybe String) deriving (Show, Eq) - -wbAlwaysAck :: - (NFDataX a) => - Circuit - (Wishbone dom 'Standard addrW a) - () -wbAlwaysAck = Circuit (const (pure $ emptyWishboneS2M{acknowledge = True}, ())) - -testResultParser :: Parser TestResult -testResultParser = do - testName <- manyTill anyChar (try (string ": ")) - result <- - choice - [ string "None" >> return Nothing - , Just <$> (string "Some(" *> manyTill anyChar (char ')')) - ] - _ <- endOfLine - return $ TestResult testName result - -testResultsParser :: Parser [TestResult] -testResultsParser = do - _ <- string "Start axi self test" >> endOfLine - manyTill testResultParser done - where - done = try (string "Done") >> endOfLine >> return () - -{- | Parse test results from the simulation output. See 'case_parseTestResults' -for example inputs. --} -parseTestResults :: String -> Either ParseError [TestResult] -parseTestResults = parse testResultsParser "" - -case_parseTestResults :: Assertion -case_parseTestResults = do - Right [] @=? parseTestResults "Start axi self test\nDone\n" - - Right [TestResult "a" Nothing] - @=? parseTestResults "Start axi self test\na: None\nDone\n" - - Right [TestResult "a" Nothing, TestResult "b" Nothing] - @=? parseTestResults "Start axi self test\na: None\nb: None\nDone\n" - - Right [TestResult "a" (Just "1"), TestResult "b" Nothing] - @=? parseTestResults "Start axi self test\na: Some(1)\nb: None\nDone\n" - -tests :: TestTree -tests = $(testGroupGenerator) diff --git a/bittide-instances/tests/Wishbone/DnaPortE2.hs b/bittide-instances/tests/Wishbone/DnaPortE2.hs deleted file mode 100644 index d2d0a7987..000000000 --- a/bittide-instances/tests/Wishbone/DnaPortE2.hs +++ /dev/null @@ -1,105 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -module Wishbone.DnaPortE2 where - -import Clash.Explicit.Prelude -import Clash.Prelude (withClockResetEnable) - -import Clash.Cores.UART (ValidBaud, uart) -import Clash.Cores.Xilinx.Unisim.DnaPortE2 -import Clash.Explicit.Testbench -import Clash.Xilinx.ClockGen -import Data.Char -import Data.Maybe -import Language.Haskell.TH -import Numeric -import Project.FilePath -import Protocols -import System.FilePath -import Test.Tasty -import Test.Tasty.HUnit -import Test.Tasty.TH -import VexRiscv - -import Bittide.DoubleBufferedRam -import Bittide.Instances.Domains -import Bittide.ProcessingElement -import Bittide.ProcessingElement.Util -import Bittide.SharedTypes -import Bittide.Wishbone -import Clash.Cores.UART.Extra (MaxBaudRate) - -import qualified Prelude as P - --- | Test whether we can read the DNA from the DNA port peripheral. -case_dna_port_self_test :: Assertion -case_dna_port_self_test = assertBool msg (receivedDna == simDna2) - where - msg = - "Received dna " - <> showHex receivedDna "" - <> " not equal to expected dna " - <> showHex simDna2 "" - receivedDna = parseResult simResult - baud = SNat @(MaxBaudRate Basic50) - clk = clockGen - rst = resetGen - ena = enableGen - simResult = fmap (chr . fromIntegral) $ catMaybes $ sampleN 100_000 uartStream - (uartStream, _, _) = withClockResetEnable (clockGen @Basic50) rst ena $ uart baud uartTx (pure Nothing) - uartTx = dut baud (clockToDiffClock clk) rst (pure 0) - -{- | A simple instance containing just VexRisc with UART and the DNA peripheral which -runs the `dna_port_e2_test` binary from `firmware-binaries`. --} -dut :: - forall dom baud. - (KnownDomain dom, ValidBaud dom baud) => - SNat baud -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "CPU_RESET" ::: Reset dom -> - "USB_UART_TX" ::: Signal dom Bit -> - "USB_UART_RX" ::: Signal dom Bit -dut baud diffClk rst_in usbUartTx = usbUartRx - where - (_, usbUartRx) = go ((usbUartTx, pure $ JtagIn low low low), pure ()) - - go = - toSignals - $ withClockResetEnable clk200 rst200 enableGen - $ circuit - $ \(uartRx, jtag) -> do - [uartBus, dnaWb] <- processingElement @dom peConfig -< jtag - (uartTx, _uartStatus) <- uartWb d256 d16 baud -< (uartBus, uartRx) - readDnaPortE2Wb simDna2 -< dnaWb - idC -< uartTx - - (clk200 :: Clock dom, pllLock :: Reset dom) = clockWizardDifferential diffClk noReset - rst200 = resetSynchronizer clk200 (unsafeOrReset rst_in pllLock) - - (iMem, dMem) = - $( do - root <- runIO $ findParentContaining "cabal.project" - let - elfDir = root firmwareBinariesDir "riscv32imc-unknown-none-elf" Release - elfPath = elfDir "dna_port_e2_test" - - memBlobsFromElf BigEndian (Nothing, Nothing) elfPath Nothing - ) - - peConfig = - PeConfig - (0b00 :> 0b01 :> 0b10 :> 0b11 :> Nil) - (Reloadable $ Blob iMem) - (Reloadable $ Blob dMem) - -parseResult :: String -> BitVector 96 -parseResult = pack . (read :: String -> Unsigned 96) . P.head . lines - -tests :: TestTree -tests = $(testGroupGenerator) diff --git a/bittide-instances/tests/Wishbone/Time.hs b/bittide-instances/tests/Wishbone/Time.hs deleted file mode 100644 index 30de8487d..000000000 --- a/bittide-instances/tests/Wishbone/Time.hs +++ /dev/null @@ -1,131 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -module Wishbone.Time where - -import Bittide.DoubleBufferedRam -import Bittide.Instances.Domains -import Bittide.ProcessingElement -import Bittide.ProcessingElement.Util -import Bittide.SharedTypes -import Bittide.Wishbone -import Clash.Cores.UART (ValidBaud, uart) -import Clash.Cores.UART.Extra (MaxBaudRate) -import Clash.Explicit.Prelude -import Clash.Explicit.Testbench -import Clash.Prelude (withClockResetEnable) -import Clash.Xilinx.ClockGen -import Control.Monad (forM_) -import Data.Char -import Data.Maybe -import Language.Haskell.TH -import Project.FilePath -import Protocols -import System.FilePath -import Test.Tasty -import Test.Tasty.HUnit -import Test.Tasty.TH -import Text.Parsec -import Text.Parsec.String -import VexRiscv - -{- | Run the timing module self test with processingElement and inspect it's uart output. -The test returns names of tests and a boolean indicating if the test passed. --} -case_time_rust_self_test :: Assertion -case_time_rust_self_test = - -- Run the test with HUnit - case parseTestResults simResult of - Left err -> assertFailure $ show err <> "\n" <> simResult - Right results -> do - forM_ results $ \result -> assertResult result - where - assertResult (TestResult name (Just err)) = assertFailure ("Test " <> name <> " failed with error" <> err) - assertResult (TestResult _ Nothing) = return () - baud = SNat @(MaxBaudRate Basic50) - clk = clockGen - rst = resetGen - ena = enableGen - simResult = fmap (asciiToChar . fromIntegral) $ catMaybes $ sampleN 1_000_000 uartStream - (uartStream, _, _) = withClockResetEnable (clockGen @Basic50) rst ena $ uart baud uartTx (pure Nothing) - uartTx = dut baud (clockToDiffClock clk) rst (pure 0) - -{- | A simple instance containing just VexRisc and UART as peripheral. -Runs the `hello` binary from `firmware-binaries`. --} -dut :: - forall dom baud. - (KnownDomain dom, ValidBaud dom baud) => - SNat baud -> - "SYSCLK_125" ::: DiffClock Ext125 -> - "CPU_RESET" ::: Reset dom -> - "USB_UART_TX" ::: Signal dom Bit -> - "USB_UART_RX" ::: Signal dom Bit -dut baud diffClk rst_in usbUartTx = usbUartRx - where - (_, usbUartRx) = go ((usbUartTx, pure $ JtagIn low low low), pure ()) - - go = - toSignals - $ withClockResetEnable clk200 rst200 enableGen - $ circuit - $ \(uartRx, jtag) -> do - [uartBus, timeBus] <- processingElement @dom peConfig -< jtag - (uartTx, _uartStatus) <- uartWb d256 d16 baud -< (uartBus, uartRx) - timeWb -< timeBus - idC -< uartTx - - (clk200 :: Clock dom, pllLock :: Reset dom) = clockWizardDifferential diffClk noReset - rst200 = resetSynchronizer clk200 (unsafeOrReset rst_in pllLock) - - (iMem, dMem) = - $( do - root <- runIO $ findParentContaining "cabal.project" - let - elfDir = root firmwareBinariesDir "riscv32imc-unknown-none-elf" Release - elfPath = elfDir "time_self_test" - - iSize = 64 * 1024 -- 64 KB - dSize = 64 * 1024 -- 64 KB - memBlobsFromElf BigEndian (Just iSize, Just dSize) elfPath Nothing - ) - - peConfig = - PeConfig - (0b00 :> 0b01 :> 0b10 :> 0b11 :> Nil) - (Reloadable $ Blob iMem) - (Reloadable $ Blob dMem) - -data TestResult = TestResult String (Maybe String) deriving (Show) - -type Ascii = BitVector 8 -asciiToChar :: Ascii -> Char -asciiToChar = chr . fromIntegral - -testResultParser :: Parser TestResult -testResultParser = do - testName <- manyTill anyChar (try (string ": ")) - result <- - choice - [ string "None" >> return Nothing - , Just <$> (string "Some(" *> manyTill anyChar (char ')')) - ] - _ <- endOfLine - return $ TestResult testName result - -testResultsParser :: Parser [TestResult] -testResultsParser = do - _ <- string "Start time self test" >> endOfLine - manyTill testResultParser done - where - done = try (string "Done") >> endOfLine >> return () - -parseTestResults :: String -> Either ParseError [TestResult] -parseTestResults = parse testResultsParser "" - -tests :: TestTree -tests = $(testGroupGenerator) diff --git a/bittide-instances/tests/doctests.hs b/bittide-instances/tests/doctests.hs deleted file mode 100644 index 2cb8b6ccf..000000000 --- a/bittide-instances/tests/doctests.hs +++ /dev/null @@ -1,14 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import System.Environment (getArgs) -import Test.DocTest (mainFromCabal) - -main :: IO () -main = do - -- We use Nix to setup tooling, not to provide GHC packages so we need to set --no-nix - args <- getArgs - mainFromCabal "bittide-instances" ("--no-nix" : args) diff --git a/bittide-instances/tests/unittests.hs b/bittide-instances/tests/unittests.hs deleted file mode 100644 index 3ea3eedc3..000000000 --- a/bittide-instances/tests/unittests.hs +++ /dev/null @@ -1,27 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import Prelude - -import Test.Tasty - -import qualified Tests.OverflowResistantDiff as Ord -import qualified Wishbone.Axi as Axi -import qualified Wishbone.DnaPortE2 as DnaPortE2 -import qualified Wishbone.Time as Time - -tests :: TestTree -tests = - testGroup - "Unittests" - [ DnaPortE2.tests - , Ord.tests - , Time.tests - , Axi.tests - ] - -main :: IO () -main = defaultMain tests diff --git a/bittide-shake/LICENSE b/bittide-shake/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/bittide-shake/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/bittide-shake/README.md b/bittide-shake/README.md deleted file mode 100644 index f1495cd75..000000000 --- a/bittide-shake/README.md +++ /dev/null @@ -1,99 +0,0 @@ - - -# bittide-shake -Build system based on Haskell's [shake](https://hackage.haskell.org/package/shake). -This system can be build to build targets in `bittide-instances` to various levels. Shake will make sure that all required steps are performed for the relevant build level, e.g. `:pnr` will first make sure `:hdl` and `:synth` are up-to-date. - -Different build levels: -* \:hdl => Generate Verilog code for the Clash target. -* \:synth => Perform synthesis for the target instance. -* \:pnr => Perform place, route, and netlist generation for the target instance. -* \:bitstream => Perform bitstream generation for the target instance. -* \:program => Program the FPGA board connected to your PC. -* \:test => Run hardware-in-the-loop test. -* \:post-process => Run ILA data post processing. - -## Prerequisites -* We have tested the build system with Vivado 2022.1 -* To change the part for which the instances are synthesized, set either environment variable `SYNTHESIS_BOARD` or `SYNTHESIS_PART`. - * For the part we've bought use either `SYNTHESIS_BOARD=xilinx.com:kcu105:part0:1.7` or `SYNTHESIS_PART=xcku040-ffva1156-2-e`. Note that for this board/part you need to use Vivado Enterprise. - * If neither is set, instances are synthesized for `SYNTHESIS_PART=xcku035-ffva1156-2-e`, which is the smaller cousin of the FPGA we've bought, but which comes with a free license. -* Only targets which have the flag `targetHasXdc` can be used to generate a bitstream. This XDC file must have the same name as the instance, and be located in the `data/constraints/` directory. -* For targets which have the flag `targetHasVio`, a probes file is generated alongside the bitstream. -* Only targets which have a `targetTest` value can be used to perform hardware tests. - - -## Shake -The build rules are defined in `exe/Main.hs`. Shake can be called using: - -``` -shake -``` - -Which is a command alias for: - -``` -cabal run shake -- $@ -``` - - -You can list all build targets like this: - -``` -shake --help -``` - -All build results end up in `_build`. - -## HDL generation -Example: - -``` -shake scatterUnitWb:hdl -``` - -## Synthesis -Example: - -``` -shake scatterUnitWb:synth -``` - -## Place, route, and netlist generation -Example: - -``` -shake scatterUnitWb:pnr -``` - -## Bitstream generation -Example: - -``` -shake boardTestExtended:bitstream -``` - -## Board programming -Example: - -``` -shake boardTestExtended:program -``` - -## Hardware testing and (if available) ILA data post processing -Example: - -``` -shake boardTestExtended:test -``` - -## ILA data post processing -Example: - -``` -shake boardTestExtended:post-process -``` diff --git a/bittide-shake/bittide-shake.cabal b/bittide-shake/bittide-shake.cabal deleted file mode 100644 index 3896f6514..000000000 --- a/bittide-shake/bittide-shake.cabal +++ /dev/null @@ -1,90 +0,0 @@ -cabal-version: 2.4 -name: bittide-shake -version: 0.1 -license: Apache-2.0 -license-file: LICENSE -author: QBayLogic B.V. -maintainer: devops@qbaylogic.com -copyright: Copyright © 2022-2024 Google LLC -data-files: - data/scripts/*.py - data/tcl/*.tcl - -common common-options - ghc-options: - -Wall - -Wcompat - - default-language: Haskell2010 - build-depends: - aeson, - base, - base16-bytestring, - bittide-experiments, - bittide-instances, - bytestring, - clash-lib, - clash-prelude, - clock, - containers, - cryptohash-sha256, - directory, - extra, - filepath, - shake, - string-interpolate, - template-haskell, - text, - vector, - vivado-hs, - -library - import: common-options - hs-source-dirs: src - exposed-modules: - Clash.Shake.Extra - Clash.Shake.Flags - Clash.Shake.Vivado - Development.Shake.Extra - Paths.Bittide.Shake - - autogen-modules: - Paths_bittide_shake - - other-modules: - Paths_bittide_shake - -test-suite doctests - type: exitcode-stdio-1.0 - hs-source-dirs: tests - main-is: doctests.hs - ghc-options: - -Wall - -Wcompat - -threaded - - build-depends: - base, - bittide-shake, - doctest-parallel >=0.3.0.1, - - default-language: Haskell2010 - -executable shake - import: common-options - ghc-options: - -Wall - -Wcompat - -threaded - -rtsopts - - main-is: exe/Main.hs - build-depends: - Glob, - ansi-terminal, - bittide-experiments, - bittide-shake, - directory, - process, - tasty-hunit, - vivado-hs, diff --git a/bittide-shake/bittide-shake.cabal.license b/bittide-shake/bittide-shake.cabal.license deleted file mode 100644 index 848612f0e..000000000 --- a/bittide-shake/bittide-shake.cabal.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/bittide-shake/data/scripts/get_watch_files.py b/bittide-shake/data/scripts/get_watch_files.py deleted file mode 100755 index 7a0d2caf6..000000000 --- a/bittide-shake/data/scripts/get_watch_files.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python3 -""" -Print all files tracked and untracked, but not ignored by git. Arguments passed -to this script are interpreted as git-style ignore patterns. E.g.: - - - get_watch_files.py '*.md' - -would return all tracked, untracked, and not ignored files minus all Markdown -files. Be careful when using this from the command line: your shell might do -globbing for you. Examples of supported patterns: - - README.md (matches any README.md file) - *.md (matches all files ending in '.md') - /.github (matches .github folder in root of repo) - -This is implemented in Python, because Haskell doesn't provide a satisfactory -implementation of Python's 'fnmatch'. -""" - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -import os -import sys -import subprocess - -from fnmatch import fnmatch - -def check_output_lines(args): - """ - Like 'subprocess.check_output', but assumes command output is UTF-8 and - returns whitespace cleaned, non-empty lines. - """ - lines = subprocess.check_output(args, encoding="utf-8").split("\n") - lines = [l.strip() for l in lines] - return [l for l in lines if l] - -def get_git_root(): - """Get root of current git repository""" - return check_output_lines(["git", "rev-parse", "--show-toplevel"])[0] - -def get_tracked_files(): - """Return files tracked by git""" - return check_output_lines(["git", "ls-files"]) - -def get_untracked_files(): - """Return files untracked by git, but also not ignored""" - lines = check_output_lines(["git", "status", "--short", "--untracked-files=all"]) - for line in lines: - if line.startswith("?"): - yield line.split(" ", 1)[1] - -def get_deleted_tracked_files(): - """Return files that are tracked by git, but removed from the filesystem""" - lines = check_output_lines(["git", "status", "--short", "--untracked-files=all"]) - for line in lines: - line = line.strip() - if line.startswith("D"): - yield line.split(" ", 1)[1] - -def get_path_parts(path): - """Yield all parts that make up a path, in reverse order""" - root, tail = os.path.split(path) - if tail: - yield tail - yield from get_path_parts(root) - elif root: - yield root - -def match(path, patterns): - """Match an fnmatch pattern to a path, or any of its parts.""" - def go(pattern): - if pattern.startswith("/"): - yield path.startswith(pattern[1:]) - yield fnmatch(path, pattern[1:]) - else: - yield fnmatch(path, pattern) - yield any(fnmatch(part, pattern) for part in get_path_parts(path)) - - for pattern in patterns: - if any(go(pattern)): - return True - -if __name__ == '__main__': - os.chdir(get_git_root()) - - ignores = sys.argv[1:] - tracked_files = set(get_tracked_files()) - untracked_files = set(get_untracked_files()) - deleted_files = set(get_deleted_tracked_files()) - all_files = sorted((tracked_files | untracked_files) - deleted_files) - for file in all_files: - if not match(file, ignores): - print(file) diff --git a/bittide-shake/data/scripts/shorten_names.py b/bittide-shake/data/scripts/shorten_names.py deleted file mode 100644 index eb1ca809e..000000000 --- a/bittide-shake/data/scripts/shorten_names.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 -""" -Vivado produces ILA dumps as VCDs and CSVs with incredibly long probe names, -depending on the hardware design. This script shortens the names in the data -dumps to just their probe names, without any prefixes or bus suffixes. - -This is written in Python because its currently not feasible to transfer Haskell -build products (executables) to our hardware-in-the-loop machine. -""" - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -import subprocess -import shutil -import glob -import os -import csv -import re - -BUS_RE = re.compile(r'( *\[[0-9]+:[0-9]+\] *)*$') -VIVADO_DIR = "_build/vivado" - -def check_output_lines(args): - """ - Like 'subprocess.check_output', but assumes command output is UTF-8 and - returns right-whitespace cleaned, non-empty lines. - """ - lines = subprocess.check_output(args, encoding="utf-8").split("\n") - lines = [l.rstrip() for l in lines] - return [l for l in lines if l] - -def get_git_root(): - """Get root of current git repository""" - return check_output_lines(["git", "rev-parse", "--show-toplevel"])[0] - -def remove_bus_suffix(s): - """See `run_remove_bus_suffix_tests`""" - return re.sub(BUS_RE, '', s) - -def remove_prefix(s): - """See `run_remove_prefix_tests`""" - return s.rsplit("/", 1)[-1] - -def get_ila_dumps(extension): - """Get ILA data dumps with given extension""" - return glob.glob(f"{VIVADO_DIR}/*/ila-data/*/*/*.{extension}") - -def shorten_csv_names(path): - """ - Remove probe prefixes and bus suffixes from probe names in a CSV file - """ - with open(path, "r") as fp_src: - with open(f"{path}.tmp", "w") as fp_dst: - reader = csv.reader(fp_src, delimiter=',', quotechar='"') - writer = csv.writer(fp_dst, delimiter=',', quotechar='"') - header = [remove_prefix(remove_bus_suffix(c)) for c in next(reader)] - writer.writerow(header) - writer.writerows(reader) - shutil.move(f"{path}.tmp", path) - -def shorten_vcd_line(line): - """ - If the given line is a VCD line introducing a variable name, remove probe - prefixes and bus suffixes. If it is not, return line unchanged. - """ - if not line.startswith("$var"): - return line - - var, typ, width, id_, *name, end = line.split(" ") - name = remove_prefix(remove_bus_suffix(" ".join(name))) - assert end == "$end" - assert var == "$var" - assert width.isnumeric() - - return " ".join([var, typ, width, id_, name, end]) - -def shorten_vcd_names(path): - """ - Remove probe prefixes and bus suffixes from probe names in a VCD file - """ - with open(path, "r") as fp_src: - with open(f"{path}.tmp", "w") as fp_dst: - for line in fp_src: - fp_dst.write(shorten_vcd_line(line.rstrip()) + "\n") - shutil.move(f"{path}.tmp", path) - -def main(): - os.chdir(get_git_root()) - - for path in get_ila_dumps("csv"): - shorten_csv_names(path) - - for path in get_ila_dumps("vcd"): - shorten_vcd_names(path) - -def run_remove_bus_suffix_tests(): - assert remove_bus_suffix("abc [3:0]") == "abc" - assert remove_bus_suffix("abc [3:0] [4:0]") == "abc" - assert remove_bus_suffix("abc [3:0][4:0]") == "abc" - assert remove_bus_suffix("abc") == "abc" - assert remove_bus_suffix("abc0") == "abc0" - assert remove_bus_suffix("abc[0]") == "abc[0]" - assert remove_bus_suffix("abc[a:0]") == "abc[a:0]" - -def run_remove_prefix_tests(): - assert remove_prefix("abc") == "abc" - assert remove_prefix("def/abc") == "abc" - assert remove_prefix("xxx/def/abc") == "abc" - assert remove_prefix("") == "" - assert remove_prefix("/abc") == "abc" - assert remove_prefix("//") == "" - -if __name__ == '__main__': - run_remove_bus_suffix_tests() - run_remove_prefix_tests() - main() diff --git a/bittide-shake/exe/Main.hs b/bittide-shake/exe/Main.hs deleted file mode 100644 index 614e1ad01..000000000 --- a/bittide-shake/exe/Main.hs +++ /dev/null @@ -1,584 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ViewPatterns #-} - -module Main where - -import Prelude - -import Bittide.Hitl (HitlTestGroup (..), hwTargetRefsFromHitlTestGroup) -import Bittide.Instances.Hitl.Tests (ClashTargetName, hitlTests) -import Clash.DataFiles (tclConnector) -import Clash.Shake.Extra -import Clash.Shake.Flags -import Clash.Shake.Vivado -import Control.Monad (forM_, unless, when) -import Control.Monad.Extra (ifM, unlessM, (&&^)) -import Data.Foldable (for_) -import Data.Function ((&)) -import Data.List (isPrefixOf, sort, uncons) -import Data.Maybe (fromJust, fromMaybe, isJust) -import Development.Shake -import Development.Shake.Classes -import GHC.Stack (HasCallStack) -import Language.Haskell.TH.Syntax (mkName) -import System.Console.ANSI (setSGR) -import System.Directory hiding (doesFileExist) -import System.Exit (ExitCode (..), exitWith) -import System.FilePath -import System.FilePath.Glob (glob) -import System.Process (callProcess, readProcess) -import Test.Tasty.HUnit (Assertion) - -import qualified Clash.Util.Interpolate as I -import qualified Paths.Bittide.Shake as Shake (getDataFileName) -import qualified System.Directory as Directory - -{- | Get all files whose changes will trigger an HDL rebuild. Because we lack a -reliable way to determine which files should trigger a rebuild, this function -returns a (very) pessimistic list: all files in the project's directory, -except files ignored by git and files matching patterns in 'ignorePatterns'. --} -getWatchFiles :: IO [String] -getWatchFiles = do - getWatchFilesPy <- - Shake.getDataFileName ("data" "scripts" "get_watch_files.py") - lines <$> readProcess getWatchFilesPy ignorePatterns "" - --- | Call 'need' on output of 'getWatchFiles' -needWatchFiles :: Action () -needWatchFiles = do - need [watchFilesPath] - need =<< liftIO (lines <$> readFile watchFilesPath) - -{- | File patterns of file we do _not_ want to make trigger a rebuild of HDL -files. Also see 'getWatchFiles'. --} -ignorePatterns :: [String] -ignorePatterns = - [ "*.md" - , ".github" - , ".reuse" - , ".vscode" - , -- Used for synthesis, but not for generating Clash output: - dataFilesDir "**" "*.xdc" - , dataFilesDir "**" "*.tcl" - , -- Used for HITL tests - "bittide-instances/data/openocd/*" - , "bittide-instances/data/picocom/*" - , "bittide-instances/data/gdb/*" - ] - --- | Build directory for Shake/Vivado/Cargo (not Cabal, it ignores builddir settings) -buildDir :: FilePath -buildDir = "_build" - --- | Clash HDL output directory -clashBuildDir :: FilePath -clashBuildDir = buildDir "clash" - --- | Directory for Vivado input + output files -vivadoBuildDir :: FilePath -vivadoBuildDir = buildDir "vivado" - -hitlBuildDir :: FilePath -hitlBuildDir = buildDir "hitl" - -dataFilesDir :: FilePath -dataFilesDir = buildDir "data" - --- | List of files to watch for Cabal/Cargo cache invalidation -watchFilesPath :: FilePath -watchFilesPath = buildDir "watch_files.txt" - --- | Build and run the executable for post processing of ILA data -doPostProcessing :: String -> FilePath -> ExitCode -> Assertion -doPostProcessing postProcessMain ilaDataDir testExitCode = do - callProcess "cabal" ["build", postProcessMain] - callProcess "cabal" ["run", postProcessMain, ilaDataDir, show testExitCode] - -{- | Searches for a file called @cabal.project@ It will look for it in the -current working directory. If it can't find it there, it will traverse up -until it finds the file. - -The returned path points to the directory containing @cabal.project@. Errors -if it could not find @cabal.project@ anywhere. --} -findProjectRoot :: (HasCallStack) => IO FilePath -findProjectRoot = goUp =<< getCurrentDirectory - where - goUp :: FilePath -> IO FilePath - goUp path - | isDrive path = error "Could not find 'cabal.project'" - | otherwise = - ifM - (Directory.doesFileExist (path projectFilename)) - (return path) - (goUp (takeDirectory path)) - - projectFilename = "cabal.project" - --- | Shake target -data Target = Target - { targetName :: ClashTargetName - -- ^ TemplateHaskell reference to top entity to synthesize - , targetHasXdc :: Bool - -- ^ Whether target has an associated XDC file in 'data/constraints'. An XDC - -- file implies that a bitstream can be generated. - , targetHasVio :: Bool - -- ^ Whether target has one or more VIOs - , targetTest :: Maybe HitlTestGroup - -- ^ Whether target has a VIO probe that can be used to run hardware-in-the- - -- loop tests. Note that this flag, 'targetTest', implies 'targetHasVio'. - , targetPostProcess :: Maybe String - -- ^ Name of the executable for post processing of ILA CSV data, or Nothing - -- if it has none. - , targetExtraXdc :: [FilePath] - -- ^ Extra constraints to be sourced. Will be sourced _after_ main XDC. - , targetExternalHdl :: [TclGlobPattern] - -- ^ A list of patterns that match the external HDL files that are used by the - -- instance. Generates tck that utilizes https://www.tcl.tk/man/tcl8.6/TclCmd/glob.htm - } - -defTarget :: ClashTargetName -> Target -defTarget name = - Target - { targetName = name - , targetHasXdc = False - , targetHasVio = False - , targetTest = Nothing - , targetPostProcess = Nothing - , targetExtraXdc = [] - , targetExternalHdl = [] - } - -testTarget :: HitlTestGroup -> Target -testTarget test@(HitlTestGroup{..}) = - Target - { targetName = topEntity - , targetHasXdc = True - , targetHasVio = True - , targetTest = Just test - , targetPostProcess = mPostProc - , targetExtraXdc = extraXdcFiles - , targetExternalHdl = externalHdl - } - -enforceValidTarget :: Target -> Target -enforceValidTarget target@Target{..} - | isJust targetTest && not targetHasVio = - error $ - show targetName - <> " should have set 'targetHasVio', because" - <> " the target has a test ('targetTest')." - | otherwise = target - --- | All synthesizable targets -targets :: [Target] -targets = - map enforceValidTarget $ - [ defTarget $ mkName "Bittide.Instances.Pnr.Calendar.switchCalendar1k" - , defTarget $ mkName "Bittide.Instances.Pnr.Calendar.switchCalendar1kReducedPins" - , defTarget $ mkName "Bittide.Instances.Pnr.ClockControl.callisto3" - , defTarget $ mkName "Bittide.Instances.Pnr.Counter.counterReducedPins" - , defTarget $ mkName "Bittide.Instances.Pnr.ElasticBuffer.elasticBuffer5" - , defTarget $ mkName "Bittide.Instances.Pnr.ScatterGather.gatherUnit1K" - , defTarget $ mkName "Bittide.Instances.Pnr.ScatterGather.gatherUnit1KReducedPins" - , defTarget $ mkName "Bittide.Instances.Pnr.ScatterGather.scatterUnit1K" - , defTarget $ mkName "Bittide.Instances.Pnr.ScatterGather.scatterUnit1KReducedPins" - , defTarget $ mkName "Bittide.Instances.Pnr.Si539xSpi.si5391Spi" - , defTarget $ mkName "Bittide.Instances.Pnr.StabilityChecker.stabilityChecker_3_1M" - , defTarget $ mkName "Bittide.Instances.Pnr.Synchronizer.safeDffSynchronizer" - ] - <> (testTarget <$> Bittide.Instances.Hitl.Tests.hitlTests) - -shakeOpts :: ShakeOptions -shakeOpts = - shakeOptions - { shakeFiles = buildDir - , shakeChange = ChangeDigest - , shakeVersion = "11" - } - -{- | Constructs a 'BoardPart' based on environment variables @SYNTHESIS_BOARD@ -or @SYNTHESIS_PART@. Errors if both are set, returns a default (free) part -if neither is set. --} -getBoardPart :: Action BoardPart -getBoardPart = do - boardName <- getEnv "SYNTHESIS_BOARD" - partName <- getEnv "SYNTHESIS_PART" - case (boardName, partName) of - (Just b, Nothing) -> pure $ Board b - (Nothing, Just p) -> pure $ Part p - (Nothing, Nothing) -> pure $ Part "xcku035-ffva1156-2-e" - (Just _b, Just _p) -> - error "Both 'SYNTHESIS_BOARD' and 'SYNTHESIS_PART' are set, unset either and retry" - -{- | Inspect DRC and timing report. Throw an error if suspicious strings were -found. --} -meetsDrcOrError :: FilePath -> FilePath -> FilePath -> IO () -meetsDrcOrError methodologyPath summaryPath checkpointPath = - unlessM - (meetsTiming methodologyPath &&^ meetsTiming summaryPath) - ( error - [I.i| - Design did not meet design rule checks (DRC). Check out the timing summary at: - - #{summaryPath} - - Check out the methodology report at: - - #{methodologyPath} - - You can investigate interactively by opening the latest checkpoint with Vivado: - - vivado #{checkpointPath} - - |] - ) - --- | Newtype used for adding oracle rules for flags to Shake -newtype ForceTestRerun = ForceTestRerun () - deriving (Show) - deriving newtype (Eq, Typeable, Hashable, Binary, NFData) - -type instance RuleResult ForceTestRerun = Bool - -{- | Defines a Shake build executable for calling Vivado. Like Make, in Shake -you define rules that explain how to build a certain file. For example: - - manifestPath %> ... - -means: to build @manifestPath@ I need to do dot-dot-dot. See the README for -an overview of which commands are user-passable (or simply scroll down). - -For a fundamental introduction into Shake, read the (lightweight!) paper -introducing it: - - https://ndmitchell.com/downloads/paper-shake_before_building-10_sep_2012.pdf. - -Or, see https://shakebuild.com/. --} -main :: IO () -main = do - setCurrentDirectory =<< findProjectRoot - - shakeArgsWith shakeOpts customFlags $ \flags shakeTargets -> pure $ Just $ do - let - Options{..} = foldl (&) defaultOptions flags - - rules = do - _ <- addOracle $ \(ForceTestRerun _) -> return forceTestRerun - - -- 'all' builds all targets defined below - phony "all" $ do - for_ targets $ \Target{..} -> do - need [entityName targetName <> ":synth"] - - (dataFilesDir "**") %> \_ -> do - Stdout out <- - command - [] - "cabal" - [ "sdist" - , "bittide" - , "bittide-extra" - , "bittide-experiments" - , "bittide-instances" - , "bittide-tools" - ] - command_ [] "mkdir" ["-p", dataFilesDir] - for_ (filter (((==) (Just '/')) . fmap fst . uncons) $ lines out) $ - \sdist -> do - (Exit (_ :: ExitCode), Stderr ()) <- - command - [] - "tar" - [ "--strip-components=2" - , "--overwrite" - , "-C" - , dataFilesDir - , "-xf" - , sdist - , takeBaseName (takeBaseName sdist) "data" - ] - return () - - -- Files used for cache invalidation - watchFilesPath %> \_ -> do - alwaysRerun - watchFiles <- sort <$> liftIO getWatchFiles - writeFileChanged watchFilesPath (unlines watchFiles) - - -- For each target, generate a user callable command (PHONY). Run with - -- '--help' to list them. - for_ targets $ \Target{..} -> do - let - -- TODO: Dehardcode these paths. They're currently hardcoded in both the - -- TCL and here, which smells. - manifestPath = getManifestLocation clashBuildDir targetName - synthesisDir = vivadoBuildDir show targetName - checkpointsDir = synthesisDir "checkpoints" - netlistDir = synthesisDir "netlist" - reportDir = synthesisDir "reports" - ilaDataDir = synthesisDir "ila-data" - - postSynthCheckpointPath = checkpointsDir "post_synth.dcp" - postPlaceCheckpointPath = checkpointsDir "post_place.dcp" - postRouteCheckpointPath = checkpointsDir "post_route.dcp" - - netlistPaths = - [ netlistDir "netlist.v" - , netlistDir "netlist.xdc" - ] - bitstreamPath = synthesisDir "bitstream.bit" - probesFilePath = synthesisDir "probes.ltx" - testExitCodePath = synthesisDir "test_exit_code" - - postRouteMethodologyPath = reportDir "post_route_methodology.rpt" - postRouteTimingSummaryPath = reportDir "post_route_timing_summary.rpt" - postRouteTimingPath = reportDir "post_route_timing.rpt" - - synthReportsPaths = [reportDir "post_synth_timing_summary.rpt"] - routeReportsPaths = - [ reportDir "post_route_clock_util.rpt" - , reportDir "post_route_drc.rpt" - , reportDir "post_route_methodology.rpt" - , reportDir "post_route_power.rpt" - , reportDir "post_route_timing_summary.rpt" - , reportDir "post_route_timing.rpt" - , reportDir "post_route_util.rpt" - ] - - withoutTargets $ do - manifestPath %> \path -> do - needWatchFiles - - -- We build all rust binaries in "firmware-binaries". They are required to - -- build bittide-instance because we have instances that includes a binaries. - command_ [Cwd "firmware-binaries"] "cargo" ["build", "--release"] - command_ [Cwd "firmware-binaries"] "cargo" ["build"] - - -- XXX: Cabal/GHC doesn't know about files produced by cargo, and - -- will therefore fail to invalidate caches. While there are - -- ways to tell Cabal/GHC to depend on these files, they are - -- known to be broken in our tool versions. This workaround - -- removes all build artifacts _except_ for "bittide-shake" - -- and "vivado-hs". - -- - -- See: https://github.com/haskell/cabal/issues/4746 - -- - -- We need to manually remove build artifacts, because Cabal - -- does not support per package/component cleans: - -- - -- See: https://github.com/haskell/cabal/issues/7506 - -- - ci <- getEnvWithDefault "false" "CI" - unless (ci == "true" || ci == "false") $ do - error $ "Environment variable 'CI' must be either 'true' or 'false', but it is: " <> ci - when (ci == "false") $ do - buildDirs <- liftIO (glob "dist-newstyle/build/*/ghc-*/*") - forM_ buildDirs $ \dir -> do - let dirName = takeFileName dir - unless (any (`isPrefixOf` dirName) ["bittide-shake", "vivado-hs"]) $ do - command_ [] "rm" ["-rf", dir] - - -- Generate RTL - let - (buildTool, buildToolArgs) = - defaultClashCmd clashBuildDir targetName - command_ [] buildTool buildToolArgs - - -- Clash messes up ANSI escape codes, leaving the rest of the terminal - -- printed in bold text. Reset manually: - liftIO (setSGR []) - - produces [path] - - -- Synthesis - (postSynthCheckpointPath : synthReportsPaths) |%> \_ -> do - -- XXX: Will not re-run if _dependencies_ mentioned in 'manifestPath' - -- change. This is only relevant in designs with multiple - -- binders with 'Synthesize' pragmas, which we currently do - -- not have. Ideally we would parse the manifest file and - -- also depend on the dependencies' manifest files, etc. - connector <- liftIO tclConnector - need [manifestPath, connector] - let - xdcNames = entityName targetName <> ".xdc" : targetExtraXdc - xdcPaths = map ((dataFilesDir "constraints") ) xdcNames - constraints <- - if targetHasXdc - then do - need xdcPaths - pure xdcPaths - else pure [] - - synthesisPart <- getBoardPart - locatedManifest <- decodeLocatedManifest manifestPath - - liftIO $ - runSynthesis - synthesisDir -- Output directory for Vivado - False -- Out of context run - synthesisPart -- Part we're synthesizing for - constraints -- List of filenames with constraints - targetExternalHdl -- List of external HDL files to be included in synthesis - locatedManifest - connector -- Path to tclConnector script - - -- Routing + netlist generation - ( postPlaceCheckpointPath - : postRouteCheckpointPath - : routeReportsPaths - <> netlistPaths - ) - |%> \_ -> do - need [postSynthCheckpointPath] - liftIO $ runPlaceAndRoute synthesisDir - - -- Design should meet design rule checks (DRC). - liftIO $ - unlessM - (meetsTiming postRouteMethodologyPath &&^ meetsTiming postRouteTimingSummaryPath) - ( error - [I.i| - Design did not meet design rule checks (DRC). Check out the timing summary at: - - #{postRouteTimingSummaryPath} - - Check out the methodology report at: - - #{postRouteMethodologyPath} - - You can investigate interactively by opening the latest checkpoint with Vivado: - - vivado #{postRouteCheckpointPath} - - You can recreate the files (locally) using: - - shake #{targetName}:pnr - - |] - ) - - -- Design should meet timing post routing. Note that this is not a - -- requirement after synthesis as many of the optimizations only follow - -- after. - liftIO $ - unlessM - (meetsTiming postRouteTimingSummaryPath) - ( error - [I.i| - Design did not meet timing. Check out the timing summary at: - - #{postRouteTimingSummaryPath} - - Alternatively, check out the full report: - - #{postRouteTimingPath} - - You can investigate interactively by opening the latest checkpoint with Vivado: - - vivado #{postRouteCheckpointPath} - - You can recreate the files (locally) using: - - shake #{targetName}:pnr - - |] - ) - - -- Bitstream generation - bitstreamPath %> \_ -> do - need [postRouteCheckpointPath] - liftIO $ runBitstreamGen synthesisDir - - -- Probes file generation - probesFilePath %> \_ -> do - need [bitstreamPath] - liftIO $ runProbesFileGen synthesisDir - - -- Run hardware test - testExitCodePath %> \path -> do - forceRerun <- askOracle $ ForceTestRerun () - when forceRerun alwaysRerun - need - [ entityName targetName <> ":program" - , bitstreamPath - , probesFilePath - ] - url <- getEnvWithDefault "localhost:3121" "HW_SERVER_URL" - exitCode <- - liftIO $ - runHitlTest (fromJust targetTest) url probesFilePath ilaDataDir - writeFileChanged path (show exitCode) - - shortenNamesPy <- - liftIO $ - Shake.getDataFileName ("data" "scripts" "shorten_names.py") - command_ [] "python3" [shortenNamesPy] - - -- User friendly target names - phony (entityName targetName <> ":hdl") $ do - need [manifestPath] - - phony (entityName targetName <> ":synth") $ do - need [postSynthCheckpointPath] - - phony (entityName targetName <> ":pnr") $ do - need [postRouteCheckpointPath] - - when targetHasXdc $ do - phony (entityName targetName <> ":bitstream") $ do - when targetHasVio $ need [probesFilePath] - need [bitstreamPath] - - -- Write bitstream to hardware target(s) - phony (entityName targetName <> ":program") $ do - when targetHasVio $ need [probesFilePath] - need [bitstreamPath] - let hwTRefs = - hwTargetRefsFromHitlTestGroup $ - fromMaybe - ( error $ - "Asked to program target " - ++ show targetName - ++ " while the " - <> "hardware targets to program could not be found as this target does not " - <> "have a HITL test associated with it." - ) - targetTest - url <- getEnvWithDefault "localhost:3121" "HW_SERVER_URL" - liftIO $ programBitstream synthesisDir hwTRefs url targetHasVio - - when (isJust targetTest) $ do - phony (entityName targetName <> ":test") $ do - need [testExitCodePath] - exitCode <- read <$> readFile' testExitCodePath - when (isJust targetPostProcess) $ do - liftIO $ doPostProcessing (fromJust targetPostProcess) ilaDataDir exitCode - unless (exitCode == ExitSuccess) $ do - liftIO $ exitWith exitCode - - when (isJust targetPostProcess) $ do - phony (entityName targetName <> ":post-process") $ do - need [testExitCodePath] - exitCode <- read <$> readFile' testExitCodePath - liftIO $ doPostProcessing (fromJust targetPostProcess) ilaDataDir exitCode - - if null shakeTargets - then rules - else want shakeTargets >> withoutActions rules diff --git a/bittide-shake/src/Clash/Shake/Extra.hs b/bittide-shake/src/Clash/Shake/Extra.hs deleted file mode 100644 index 56d326f31..000000000 --- a/bittide-shake/src/Clash/Shake/Extra.hs +++ /dev/null @@ -1,94 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -{- | Shake utilities related to Clash. Although 'clash-shake' already exists, it -assumes Clash runs in non-project mode. We should discuss with the author if -he sees a way of upstreaming the code in this module. --} -module Clash.Shake.Extra where - -import Prelude - -import Bittide.Hitl (ClashTargetName) -import Clash.Annotations.Primitive (HDL (Verilog)) -import Data.Char (toLower) -import Development.Shake -import Development.Shake.FilePath (()) - -import qualified Crypto.Hash.SHA256 as Sha256 -import qualified Data.ByteString.Base16 as Base16 -import qualified Data.ByteString.Lazy as ByteStringLazy -import qualified Data.Text as Text -import qualified Data.Text.Encoding as Encoding - -hdlToFlag :: HDL -> String -hdlToFlag = ("--" <>) . map toLower . show - --- | Calculate a SHA256 hex digest of a given file path -hexDigestFile :: FilePath -> IO String -hexDigestFile path = do - contents <- liftIO (ByteStringLazy.readFile path) - pure $ - Text.unpack $ - Encoding.decodeUtf8 $ - Base16.encode $ - Sha256.hashlazy $ - contents - -{- | Generate command to run and arguments to supply for a top entity passed as -a @TemplateHaskell@ name. Generates the expected location of a Clash manifest -file. --} -clashCmd :: - -- | Build directory - FilePath -> - -- | HDL to compile to - HDL -> - -- | Entity to compile - ClashTargetName -> - -- | Extra arguments to pass to Clash - [String] -> - -- | (command, arguments) - (String, [String]) -clashCmd buildDir hdl topName extraArgs = - ( "cabal" - , [ "run" - , pkgName <> ":clash" - , "--" - , modName - , "-fclash-hdldir" - , buildDir - , "-main-is" - , funcName - , hdlToFlag hdl - , "-fclash-clear" - , "-fclash-spec-limit=100" - , "-fclash-debug" - , "DebugSilent" - ] - <> extraArgs - ) - where - (modName, funcName) = splitName topName - pkgName = "bittide-instances" - --- | Split a 'ClashTargetName' into the fully qualified module name and the function name. -splitName :: ClashTargetName -> (String, String) -splitName qualifiedName = - let (f, m) = break (== '.') $ reverse $ show qualifiedName - in (reverse $ tail m, reverse f) - -entityName :: ClashTargetName -> String -entityName = snd . splitName - -moduleName :: ClashTargetName -> String -moduleName = fst . splitName - -defaultClashCmd :: FilePath -> ClashTargetName -> (String, [String]) -defaultClashCmd buildDir topName = clashCmd buildDir Verilog topName [] - --- | Given a 'ClashTargetName', return expected location of Clash manifest file. -getManifestLocation :: FilePath -> ClashTargetName -> String -getManifestLocation buildDir topName = - buildDir show topName "clash-manifest.json" diff --git a/bittide-shake/src/Clash/Shake/Flags.hs b/bittide-shake/src/Clash/Shake/Flags.hs deleted file mode 100644 index 699ef1156..000000000 --- a/bittide-shake/src/Clash/Shake/Flags.hs +++ /dev/null @@ -1,32 +0,0 @@ --- SPDX-FileCopyrightText: 2023-2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - --- | Flags used by Shake -module Clash.Shake.Flags where - -import Prelude - -import System.Console.GetOpt (ArgDescr (NoArg), OptDescr (Option)) - -data Options = Options - { forceTestRerun :: Bool - } - -defaultOptions :: Options -defaultOptions = - Options - { forceTestRerun = False - } - -{- | List of custom flags supported by us. Note that we currently support only -one flag, 'HardwareTargets'. --} -customFlags :: [OptDescr (Either String (Options -> Options))] -customFlags = - [ Option - "" -- no short flags - ["force-test-rerun"] - (NoArg $ Right (\opts -> opts{forceTestRerun = True})) - "Force the rerun of hardware in the loop tests" - ] diff --git a/bittide-shake/src/Clash/Shake/Vivado.hs b/bittide-shake/src/Clash/Shake/Vivado.hs deleted file mode 100644 index 5fa9831d2..000000000 --- a/bittide-shake/src/Clash/Shake/Vivado.hs +++ /dev/null @@ -1,955 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} - -{- | Helper functions to do things like synthesis, place & route, bitstream -generation, programming and running hardware tests for the Bittide project. -This is realized by letting Vivado execute Tcl using the `vivado-hs` package. -Refer to @bittide-instances/src/Bittide/Instances/Hitl/README.md@ and -`Bittide.Hitl` for more information on the HITL test infrastructure. --} -module Clash.Shake.Vivado ( - LocatedManifest (..), - BoardPart (..), - TclGlobPattern, - decodeLocatedManifest, - runSynthesis, - runPlaceAndRoute, - runBitstreamGen, - runProbesFileGen, - programBitstream, - runHitlTest, - meetsTiming, - meetsDrc, -) where - -import Prelude - -import Development.Shake (Action) -import Development.Shake.Extra (decodeFile) - -import Bittide.Hitl -import Bittide.Instances.Hitl.Setup (knownFpgaIds) -import Clash.Driver.Manifest -import Clash.Prelude (BitPack (BitSize), Natural, natToNatural, pack) -import Clash.Shake.Extra (hexDigestFile) -import qualified Clash.Sized.Internal.BitVector as BitVector -import Control.Concurrent (threadDelay) -import Control.Exception (try) -import Control.Monad.Extra (andM, forM, forM_, orM, unless, when) -import Data.Containers.ListUtils (nubOrd) -import Data.Either (lefts, rights) -import Data.Functor ((<&>)) -import Data.List (elemIndex, isInfixOf, isSuffixOf, sort, sortOn, (\\)) -import Data.List.Extra (anySame, split, (!?)) -import Data.Map.Strict (fromList, keys, mapKeys, toAscList) -import qualified Data.Map.Strict as Map -import Data.Maybe (fromJust, fromMaybe) -import Data.String.Interpolate (__i) -import Data.Text (unpack) -import System.Clock (Clock (Monotonic), diffTimeSpec, getTime, toNanoSecs) -import System.Directory (createDirectoryIfMissing) -import System.Exit (ExitCode (..)) -import System.FilePath (dropFileName, ()) -import Text.Read (readMaybe) -import Vivado (TclException (..), VivadoHandle, execPrint, execPrint_, with) -import Vivado.Tcl - --- | Satisfied if all actions result in 'False' -noneM :: (Monad m) => [m Bool] -> m Bool -noneM = fmap not . orM - --- | Whether a string occurs in a file -inFile :: String -> FilePath -> IO Bool -inFile msg path = do - content <- readFile path - pure $ (msg `isInfixOf` content) - -{- | Read a timing summary or DRC report and determine whether it passed DRC -checks. --} -meetsDrc :: FilePath -> IO Bool -meetsDrc path = - noneM - [ inFile "No report available as report_methodology has not been run prior." path - , inFile "Critical Warning" path - ] - --- | Read a timing summary and determine whether it met timing. -meetsTiming :: FilePath -> IO Bool -meetsTiming path = - andM - [ meetsDrc path -- for safety; users should use meetsDrc for useful error reporting - , not <$> inFile "Timing constraints are not met" path - ] - --- | Patterns compatible with https://www.tcl.tk/man/tcl8.6/TclCmd/glob.htm -type TclGlobPattern = String - --- TODO: Upstream -data LocatedManifest = LocatedManifest - { lmPath :: FilePath - -- ^ Path pointing to the manifest file itself - , lmManifest :: Manifest - -- ^ Manifest file corresponding to the one at 'lmPath' - } - -decodeLocatedManifest :: FilePath -> Action LocatedManifest -decodeLocatedManifest path = LocatedManifest path <$> decodeFile path - -{- | Vivado board part or part. If a board part is set, Vivado will infer the -part on that board. --} -data BoardPart - = Board String - | Part String - --- | Generate Tcl that sets the board part or part for the current project -mkBoardPartTcl :: BoardPart -> String -mkBoardPartTcl boardPart = case boardPart of - (Board name) -> "set_property board_part " <> name <> " [current_project]" - (Part name) -> "set_property part " <> name <> " [current_project]" - -{- | Generates TCL that generates and reads Xilinx IP and reads constraints and -HDL files generated by Clash. The caller is responsible for starting synthesis -or simulation. --} -execBaseTcl :: - -- | Handle to a Vivado object that is to execute the Tcl. - VivadoHandle -> - -- | Where to create ip directory. - FilePath -> - -- | List of glob patterns to external HDL files. - [TclGlobPattern] -> - -- | Top entity directory - LocatedManifest -> - -- | Board part or part to synthesize for - BoardPart -> - -- | Path to tclConnector: a Tcl script that can parse Clash output and emit - -- the correct commands for loading the design into Vivado - FilePath -> - IO () -execBaseTcl v outputDir globPatterns LocatedManifest{lmPath} boardPart connector = do - connectorDigest <- hexDigestFile connector - lmPathDigest <- hexDigestFile lmPath - let - topEntityDir = dropFileName lmPath - boardPartTcl = mkBoardPartTcl boardPart - globTcl - | null globPatterns = "" :: String - | otherwise = - [__i| - set extra_hdl_files [list] - \# We use file join to be able to retrieve environment variables - \# from our paths - set glob_patterns "#{unlines $ fmap (\p -> "[file join " <> p <> "]") globPatterns}" - foreach pat $glob_patterns { - puts "Adding files matching $pat" - set extra_hdl_files [concat $extra_hdl_files [glob $pat]] - } - add_files $extra_hdl_files - |] - - execPrint_ - v - [__i| - \# #{lmPath}: #{lmPathDigest} - \# #{connector}: #{connectorDigest} - set_msg_config -severity {CRITICAL WARNING} -new_severity ERROR - source -notrace {#{connector}} - file delete -force {#{outputDir "ip"}} - file mkdir {#{outputDir "ip"}} - clash::readMetadata {#{topEntityDir}} - - set hasIp [expr [llength [clash::GetAllTclIfaces {purposes createIp}]] > 0] - - if {${hasIp}} { - create_project -in_memory - #{boardPartTcl} - set ips [clash::createIp -dir {#{outputDir "ip"}}] - set ipFiles [get_property IP_FILE [get_ips $ips]] - close_project - } - - clash::readHdl - #{boardPartTcl} - #{globTcl} - if {${hasIp}} { - read_ip $ipFiles - set_property GENERATE_SYNTH_CHECKPOINT false [get_files $ipFiles] - generate_target {synthesis simulation} [get_ips $ips] - } - - clash::readXdc {early normal late} - set_property TOP $clash::topEntity [current_fileset] - |] - -runSynthesis :: - -- | Directory to write logs and checkpoints to - FilePath -> - -- | Out of context? - Bool -> - -- | Board part or part to synthesize for. E.g., 'xcku040-ffva1156-2-e'. - BoardPart -> - -- | List of filepaths to XDC files - [FilePath] -> - -- | List of glob patterns to external HDL files. - [TclGlobPattern] -> - -- | Manifests of which the first is the top-level to synthesize - LocatedManifest -> - -- | Path to tclConnector: a Tcl script that can parse Clash output and emit - -- the correct commands for loading the design into Vivado - FilePath -> - IO () -runSynthesis - outputDir - outOfContext - boardPart - constraints - globPatterns - manifest@LocatedManifest{lmManifest} - connector = with $ \v -> do - execBaseTcl v outputDir globPatterns manifest boardPart connector - - constraintDigests <- unlines <$> mapM constraintDigest constraints - putStrLn constraintDigests - - execCmd_ v "set_msg_config" ["-severity {CRITICAL WARNING}", "-new_severity ERROR"] - forM_ constraints (\xdcPath -> execCmd v "read_xdc" ["-unmanaged {" <> xdcPath <> "}"]) - - -- Synthesis - execCmd_ - v - "synth_design" - [ "-name " <> unpack (topComponent lmManifest) - , "-mode " <> if outOfContext then "outOfContext" else "default" - ] - createDirectoryIfMissing True $ outputDir "reports" - createDirectoryIfMissing True $ outputDir "checkpoints" - execCmd_ - v - "report_methodology" - ["-file {" <> outputDir "reports" "post_synth_methodology.rpt}"] - execCmd_ - v - "report_timing_summary" - ["-file {" <> outputDir "reports" "post_synth_timing_summary.rpt}"] - execCmd_ - v - "report_utilization" - ["-file {" <> outputDir "reports" "post_synth_util.rpt}"] - execCmd_ - v - "write_checkpoint" - ["-force", "{" <> outputDir "checkpoints" "post_synth.dcp}"] - - -- Netlist - createDirectoryIfMissing True $ outputDir "netlist" - execCmd_ - v - "write_verilog" - ["-force", outputDir "netlist" "netlist.v"] - execCmd_ - v - "write_xdc" - ["-no_fixed_only", "-force", outputDir "netlist" "netlist.xdc"] - where - constraintDigest path = do - pathDigest <- hexDigestFile path - pure [__i|\# #{path}: #{pathDigest}|] - -runPlaceAndRoute :: FilePath -> IO () -runPlaceAndRoute outputDir = with $ \v -> - execPrint_ - v - [__i| - set_msg_config -severity {CRITICAL WARNING} -new_severity ERROR - - \# Pick up where synthesis left off - open_checkpoint {#{outputDir "checkpoints" "post_synth.dcp"}} - - \# Run optimization & placement - opt_design - place_design - phys_opt_design - write_checkpoint -force {#{outputDir "checkpoints" "post_place.dcp"}} - - \# Routing - route_design - write_checkpoint -force {#{outputDir "checkpoints" "post_route.dcp"}} - report_methodology -file {#{outputDir "reports" "post_route_methodology.rpt"}} - report_timing_summary -file {#{outputDir "reports" "post_route_timing_summary.rpt"}} - report_timing -sort_by group -max_paths 100 -path_type summary -file {#{outputDir "reports" "post_route_timing.rpt"}} - - report_clock_utilization -file {#{outputDir "reports" "post_route_clock_util.rpt"}} - report_utilization -file {#{outputDir "reports" "post_route_util.rpt"}} - report_power -file {#{outputDir "reports" "post_route_power.rpt"}} - report_drc -file {#{outputDir "reports" "post_route_drc.rpt"}} - - \# Generate netlist and constraints - file mkdir {#{outputDir "netlist"}} - write_verilog -force {#{outputDir "netlist" "netlist.v"}} - write_xdc -no_fixed_only -force {#{outputDir "netlist" "netlist.xdc"}} -|] - -runBitstreamGen :: FilePath -> IO () -runBitstreamGen outputDir = with $ \v -> do - execCmd_ v "set_msg_config" ["-severity {CRITICAL WARNING}", "-new_severity ERROR"] - - -- Pick up where netlist left off - execPrint_ - v - ("open_checkpoint {" <> outputDir "checkpoints" "post_route.dcp" <> "}") - - -- Generate bitstream - execPrint_ v "set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]" - execPrint_ v ("write_bitstream -force {" <> outputDir "bitstream.bit" <> "}") - -runProbesFileGen :: FilePath -> IO () -runProbesFileGen outputDir = with $ \v -> do - execPrint_ v "set_msg_config -severity {CRITICAL WARNING} -new_severity ERROR" - - -- Pick up where netlist left off - execPrint_ - v - ("open_checkpoint {" <> outputDir "checkpoints" "post_route.dcp" <> "}") - - -- Generate probes file - execPrint_ v ("write_debug_probes -force {" <> outputDir "probes.ltx" <> "}") - -{- | Attempts to find and return the ID of a hardware target referenced by a given -`HwTargetRef`. This is what Vivado seems to call the UID minus the vendor string. --} -idFromHwTRef :: HwTargetRef -> FpgaId -idFromHwTRef (HwTargetByIndex i) = - fromMaybe - ("The given index " <> show i <> " is out of range for the list of known FPGA IDs") - (knownFpgaIds !? fromIntegral i) -idFromHwTRef (HwTargetById targetId) = targetId - -{- | Takes the ID part of a Vivado hardware target. This is what Vivado seems to -call the UID minus the vendor string. - -==== __Example__ ->>> idFromHwT (HwTarget "localhost:3121/xilinx_tcf/Digilent/210308B0B0C2") -"210308B0B0C2" --} -idFromHwT :: HwTarget -> FpgaId -idFromHwT = fromMaybe err . (!? 3) . split (== '/') . fromHwTarget - where - err = error "Unexpected format for hw_target Tcl object" - -{- | Attempt to determine the hardware target index/position in the HITL -test setup to prepend it to its prettier name. --} -prettyShow :: HwTarget -> String -prettyShow hwT = - let hwTId = idFromHwT hwT - in case hwTId `elemIndex` knownFpgaIds of - Just index -> show index <> "_" <> hwTId - Nothing -> hwTId - -{- | Tries to find the hardware target with a specific FPGA ID in a given list of hardware targets. -Returns the FPGA ID wrapped in a `Left` on failure to find such a target. --} -findHwTWithId :: FpgaId -> [HwTarget] -> Either FpgaId HwTarget -findHwTWithId fpgaId hwTs = do - case filter ((== fpgaId) . idFromHwT) hwTs of - [] -> Left fpgaId - [hwT] -> Right hwT - hwTs' -> error $ "Found multiple hardware targets with the same ID: " <> show hwTs' - -{- | Attempts to resolve a given list of hardware target references and return a -`Map HwTargetRef HwTarget`. The available hardware targets on the connected -hardware servers are queried for a limited number of times and errors if the -requested targets cannot be found. --} -resolveHwTRefs :: - -- | Handle to a Vivado object that is to execute the Tcl. - VivadoHandle -> - [HwTargetRef] -> - IO (Map.Map HwTargetRef HwTarget) -resolveHwTRefs v requestedHwTRefs = do - let requestedIds = idFromHwTRef <$> requestedHwTRefs - let - go :: Int -> IO (Map.Map HwTargetRef HwTarget) - go numTries = do - foundTargets <- get_hw_targets v [] - printInfo foundTargets - let matchingTargets = (`findHwTWithId` foundTargets) <$> requestedIds - if null (lefts matchingTargets) - then do - pure $ fromList $ zip requestedHwTRefs (rights matchingTargets) - else do - putStrLn $ - "WARNING: The connected hardware servers did not host the requested " - <> "hardware targets with IDs " - <> show (lefts matchingTargets) - if numTries < 0 - then error "Giving up." - else do - putStrLn "Retrying..." - threadDelay 500000 -- In μs - refresh_hw_server v [] - go (numTries - 1) - go 10 - where - printInfo foundTargets = do - putStrLn $ - "The connected hardware servers host " - <> show (length foundTargets) - <> " hardware targets:" - mapM_ (putStrLn . ('\t' :) . show) foundTargets - let foundFpgaIds = idFromHwT <$> foundTargets - when (sort foundFpgaIds /= sort knownFpgaIds) $ - putStrLn $ - "WARNING: The IDs of the hosted hardware targets do not match the known ones." - <> "\n\tNot found but expected: " - <> show (knownFpgaIds \\ foundFpgaIds) - <> "\n\tFound but unexpected: " - <> show (foundFpgaIds \\ knownFpgaIds) - -{- | Open the given hardware target and set the current hardware device to the -Xilinx FPGA on it. --} -openHwT :: VivadoHandle -> HwTarget -> IO () -openHwT v hwT = do - currentHwT <- current_hw_target v [] - currentIsOpened <- - execPrint v "get_property IS_OPENED [current_hw_target]" <&> \case - "1" -> True - "0" -> False - o -> error $ "Property IS_OPENED was " <> show o <> " where 0 or 1 was expected." - if currentHwT == hwT - then do - unless currentIsOpened $ - open_hw_target v [] - else do - when currentIsOpened $ - close_hw_target v ["-quiet"] - _ <- current_hw_target v [show hwT] - open_hw_target v [] - -- Assumes that the open target has the Xilinx device to program at index 0. - -- This is also what Xilinx does in its examples in UG908. - hwD <- current_hw_device v ["[lindex [get_hw_devices] 0]"] - when (null (fromHwDevice hwD)) $ - error "Setting the current hardware device failed." - -programBitstream :: - -- | Directory where the bitstream files are located - FilePath -> - -- | References to the hardware targets to program - [HwTargetRef] -> - -- | Hardware server URL - String -> - -- | Flag indicating if the target has a probes file. If true, the probes file - -- is programmed alongside the bitstream. - Bool -> - IO () -programBitstream outputDir hwTRefs url hasProbesFile = with $ \v -> do - putStrLn "Starting programming of given hardware targets..." - if null hwTRefs - then putStrLn "WARNING: Not programming as no hardware target references were given." - else do - execCmd_ v "set_msg_config" ["-severity {CRITICAL WARNING}", "-new_severity ERROR"] - execCmd_ v "open_hw_manager" [] - execCmd_ v "connect_hw_server" ["-url " <> url] - refToHwTMap <- resolveHwTRefs v hwTRefs - let hwTs = nubOrd $ Map.elems refToHwTMap - forM_ hwTs $ \hwT -> do - openHwT v hwT - execCmd_ - v - "set_property" - [ "PROGRAM.FILE" - , embrace (outputDir "bitstream.bit") - , "[current_hw_device]" - ] - execCmd_ - v - "set_property" - [ "PROBES.FILE" - , if hasProbesFile then embrace (outputDir "probes.ltx") else "{}" - , "[current_hw_device]" - ] - -- Program the device and close properly - _ <- program_hw_devices v ["[current_hw_device]"] - refresh_hw_device v ["[current_hw_device]"] - -data VioProbeInfo = VioProbeInfo - { probeName :: String - , probeType :: String - , probeWidth :: String - } - -{- | Verifies whether the bitstream programmed on the current hardware target -includes a VIO IP core that is configured as required by this HITL framework. -See `Bittide.Hitl` for details. - -Make sure that the `PROBES.FILE` property is set for the `current_hw_device` -and that `refresh_hw_device` has been run afterwards. --} -verifyHitlVio :: VivadoHandle -> Natural -> IO () -verifyHitlVio v paramBitSize = do - vioProbes <- get_hw_probes v ["-of_objects [get_hw_vios]", "*vioHitlt/*"] - let unexpectedProbes = - [ show probe - | probe <- vioProbes - , not (any (`isSuffixOf` show probe) requiredProbeSimpleNames) - ] - where - requiredProbeSimpleNames = map (last . split (== '/') . probeName) requiredProbes - unless (null unexpectedProbes) $ do - putStrLn "WARNING: Encountered unexpected HITL VIO probes, they will be ignored:" - mapM_ (putStrLn . ('\t' :)) unexpectedProbes - mapM_ (`verifyHitlProbe` vioProbes) requiredProbes - where - requiredProbes = - [ VioProbeInfo "*vioHitlt/probe_test_start" "vio_output" "1" - , VioProbeInfo "*vioHitlt/probe_test_done" "vio_input" "1" - , VioProbeInfo "*vioHitlt/probe_test_success" "vio_input" "1" - ] - <> [ VioProbeInfo "*vioHitlt/probe_test_data" "vio_output" (show paramBitSize) - | paramBitSize /= 0 - ] - verifyHitlProbe :: VioProbeInfo -> [HwProbe] -> IO () - verifyHitlProbe VioProbeInfo{..} vioProbes = do - let simpleName = last (split (== '/') probeName) - let probe = case filter (('/' : simpleName) `isSuffixOf`) (show <$> vioProbes) of - [p] -> p - ps -> - error $ - "Exactly one probe named '" - <> probeName - <> "' " - <> "must be present but " - <> show (length ps) - <> " were found." - execCmd_ - v - "set" - [ simpleName - , "[get_hw_probes -of_objects [get_hw_vios] " <> probeName <> "]" - ] - typeProp <- execCmd v "get_property" ["type", "$" <> simpleName] - unless (typeProp == probeType) $ - error $ - "Probe '" - <> probe - <> "' must have type " - <> probeType - <> " but has '" - <> typeProp - <> "'." - widthProp <- execCmd v "get_property" ["width", "$" <> simpleName] - unless (widthProp == probeWidth) $ - error $ - "Probe '" <> probe <> "' must have width " <> probeWidth <> " but it is " <> widthProp - -getTestProbeTcl :: String -> String -getTestProbeTcl probeNm = - "[get_hw_probes -of_objects [get_hw_vios] " <> probeNm <> "]" - -{- | Tcl code to get the HITL VIO test start output probe. -Run `verifyHitlVio` beforehand to ensure that the probe is available. --} -getProbeTestStartTcl :: String -getProbeTestStartTcl = getTestProbeTcl "*vioHitlt/probe_test_start" - -{- | Tcl code to get the HITL VIO test data output probe. -Run `verifyHitlVio` beforehand and verify that the HITL test parameter -`BitSize` isn't zero to ensure that the probe is available. --} -getProbeTestDataTcl :: String -getProbeTestDataTcl = getTestProbeTcl "*vioHitlt/probe_test_data" - -{- | Tcl code to get the HITL VIO test done input probe. -Run `verifyHitlVio` beforehand to ensure that the probe is available. --} -getProbeTestDoneTcl :: String -getProbeTestDoneTcl = getTestProbeTcl "*vioHitlt/probe_test_done" - -{- | Tcl code to get the HITL VIO test success input probe. -Run `verifyHitlVio` beforehand to ensure that the probe is available. --} -getProbeTestSuccessTcl :: String -getProbeTestSuccessTcl = getTestProbeTcl "*vioHitlt/probe_test_success" - -{- | Observed instances of property CELL_NAME of an hw_ila object include: -- "Bittide_Instances_Hitl_FullMeshSwCc_fullMeshSwCcTest_callistoClockControlWithIla_callistoResult/ilaPlot/ilaPlot" -- "instructionBus/dataBus" - -This short name should return "ilaPlot" and "instructionBus" for -those examples respectively. Could be improved, see -https://github.com/bittide/bittide-hardware/issues/530 --} -getCurrentIlaShortName :: VivadoHandle -> IO String -getCurrentIlaShortName v = do - ilaCellName <- execCmd v "get_property" ["CELL_NAME", "[current_hw_ila]"] - pure $ - fromMaybe - (error $ "Determining short name failed for ILA with CELL_NAME " <> ilaCellName) - (reverse (split (== '/') ilaCellName) !? 1) - -{- | Verify hardware ILAs. Verification should be performed before the `HwIla` -objects are used for the first time. --} -verifyHwIlas :: VivadoHandle -> IO () -verifyHwIlas v = do - -- TODO either use or remove the Tcl dictionary - execPrint_ - v - [__i| - \# Create a list of dictionaries where each dictionary corresponds to one ILA. - \# Each dictionary has the following keys: - \# name : short name of the ILA - \# cell_name : name of the cell the ILA is in - \# trigger_probe : name of the trigger probe - \# capture_probe : name of the capture probe - \# data_probes : list of names of all other probes - proc get_ila_dicts {} { - set ila_dicts {} - - set hw_ilas [get_hw_ilas -quiet] - set ila_count [llength $hw_ilas] - if {$ila_count == 0} { - puts "\nNo ILAs in design" - return {} - } - - puts "\nFound $ila_count ILAs:" - foreach hw_ila $hw_ilas { - set ila_dict {} - - \# The short name is the name of the module the ILA is in. For example a - \# cell named `fullMeshSwCcTest/ilaPlot/ila_inst` will give the short - \# name `ilaPlot`. - set cell_name [get_property CELL_NAME $hw_ila] - set before_last [expr [string last / $cell_name] - 1] - set module_name [string range $cell_name 0 $before_last] - set after_second_to_last [expr [string last / $module_name] + 1] - set short_name [string range $cell_name $after_second_to_last $before_last] - dict set ila_dict name $short_name - dict set ila_dict cell_name $cell_name - - \# Get trigger probe and verify it conforms with ILA framework - set trigger_probe [get_hw_probes -of_objects $hw_ila */trigger*] - set trigger_probe_count [llength $trigger_probe] - if {$trigger_probe_count != 1} { - set err_msg "Exactly one probe named 'trigger*' must be present, " - append err_msg "but $trigger_probe_count were found" \n [all_probe_names_msg] - error $err_msg - } elseif {[get_property is_trigger $trigger_probe] != 1} { - set probe_name_short [get_property name.short $trigger_probe] - set err_msg "Probe '$probe_name_short' should have probeType " - append err_msg {Trigger or DataAndTrigger} \n [all_probe_names_msg] - error $err_msg - } elseif {[get_property width $trigger_probe] != 1} { - set probe_name_short [get_property name.short $trigger_probe] - set err_msg "Probe '$probe_name_short' must have a width of 1 bit\n" - append err_msg [all_probe_names_msg] - error $err_msg - } else { - dict set ila_dict trigger_probe [get_property name $trigger_probe] - } - - \# Get capture probe and verify it conforms with ILA framework - set capture_probe [get_hw_probes -of_objects $hw_ila */capture*] - set capture_probe_count [llength $capture_probe] - if {$capture_probe_count != 1} { - set err_msg {Exactly one probe named 'capture*' must be present, } - append err_msg "but $capture_probe_count were found" \n [all_probe_names_msg] - error $err_msg - } elseif {[get_property is_trigger $capture_probe] != 1} { - set probe_name_short [get_property name.short $capture_probe] - set err_msg "Probe '$probe_name_short' should have probeType " - append err_msg {Trigger or DataAndTrigger} \n [all_probe_names_msg] - error $err_msg - } elseif {[get_property width $capture_probe] != 1} { - set probe_name_short [get_property name.short $capture_probe] - set err_msg "Probe '$probe_name_short' must have a width of 1 bit\n" - append err_msg [all_probe_names_msg] - error $err_msg - } else { - dict set ila_dict capture_probe [get_property name $capture_probe] - } - - \# Get all data probes and verify each conforms with ILA framework - set all_probes [get_hw_probes -of_objects $hw_ila] - if {[llength $all_probes] < 3} { - set err_msg "ILA '$short_name' has no data probes, at least 1 " - append err_msg {data probe is required} \n [all_probe_names_msg] - error $err_msg - } - dict set ila_dict data_probes [list] - foreach probe $all_probes { - if {$probe eq $trigger_probe || $probe eq $capture_probe} { - continue - } elseif {[get_property is_data $probe] != 1} { - set probe_name_short [get_property name.short $probe] - set err_msg "Probe '$probe_name_short' should have probeType " - append err_msg {Data or DataAndTrigger} \n [all_probe_names_msg] - error $err_msg - } else { - dict update ila_dict data_probes probe_list { - lappend probe_list [get_property name $probe] - } - } - } - lappend ila_dicts $ila_dict - - \# Print all ILA probes - puts "ILA $short_name with probes:" - set probe_name_short [get_property name.short $trigger_probe] - puts "\t$probe_name_short" - set probe_name_short [get_property name.short $capture_probe] - puts "\t$probe_name_short" - foreach probe_name [dict get $ila_dict data_probes] { - set idx_start [expr {[string first / $probe_name] + 1}] - set probe_name_short [string range $probe_name $idx_start end] - puts "\t$probe_name_short" - } - } - return $ila_dicts - } - |] - execCmd_ v "set" ["ila_dicts", "[get_ila_dicts]"] - -{- | Waits (with a timeout) until a HITL test case is finished by probing -the probe_test_done probe. Returns whether the test case was successful. --} -waitTestCaseEnd :: VivadoHandle -> HitlTestCase HwTarget a b -> FilePath -> IO ExitCode -waitTestCaseEnd v HitlTestCase{..} probesFilePath = do - startTime <- getTime Monotonic - let calcTimeSpentMs = (`div` 1000000) . toNanoSecs . diffTimeSpec startTime <$> getTime Monotonic - exitCodes <- forM (keys parameters) $ \hwT -> do - openHwT v hwT - execCmd_ v "set_property" ["PROBES.FILE", embrace probesFilePath, "[current_hw_device]"] - let - pollTestDone :: IO ExitCode - pollTestDone = do - refresh_hw_device v ["-quiet"] - timeSpentMs <- calcTimeSpentMs - done <- execCmd v "get_property" ["INPUT_VALUE", getProbeTestDoneTcl] - success <- execCmd v "get_property" ["INPUT_VALUE", getProbeTestSuccessTcl] - case (done, success, timeSpentMs >= testTimeoutMs) of - ("1", "1", _) -> do - pure ExitSuccess - ("1", _, _) -> do - putStrLn $ "HITL test case failure for hardware target " <> prettyShow hwT - pure (ExitFailure 2) - (_, _, True) -> do - putStrLn $ - "HITL test case timeout (≥" - <> show testTimeoutMs - <> "ms) for hardware target " - <> prettyShow hwT - pure (ExitFailure 3) - _ -> do - threadDelay 1000 -- In μs - pollTestDone - pollTestDone - - -- Print summary of test case - timeSpentMs <- calcTimeSpentMs - putStrLn $ - "HITL test case'" - <> name - <> "' passed on " - <> show (length (filter (== ExitSuccess) exitCodes)) - <> " out of " - <> show (length exitCodes) - <> " hardware targets in " - <> show timeSpentMs - <> "ms." - pure (maximum exitCodes) - where - -- \| Timeout specifying how long we should wait for a test to finish before - -- considering it a failed test. - -- TODO: Allow the user to specify the timeout for a test. - testTimeoutMs = 60000 :: Integer - -runHitlTest :: - -- | The HITL test group to execute - HitlTestGroup -> - -- | Hardware server URL - String -> - -- | Path to the generated probes file - FilePath -> - -- | Filepath the the ILA data dump directory - FilePath -> - IO ExitCode -runHitlTest test@HitlTestGroup{topEntity, testCases} url probesFilePath ilaDataDir = do - putStrLn $ - "Starting HITL test for FPGA design '" - <> show topEntity - <> "' with " - <> show (length testCases) - <> " test cases..." - result <- try @TclException $ with $ \v -> do - let testCaseNames = name <$> testCases - when (anySame testCaseNames) $ - error $ - "HITL test case names must be unique within their test. Offenders: " - <> show (testCaseNames \\ nubOrd testCaseNames) - execCmd_ v "set_msg_config" ["-severity {CRITICAL WARNING}", "-new_severity ERROR"] - execCmd_ v "open_hw_manager" [] - execCmd_ v "connect_hw_server" ["-url " <> url] - refToHwTMap <- resolveHwTRefs v (hwTargetRefsFromHitlTestGroup test) - - testResults <- forM (zip [1 :: Int ..] testCases) $ \(nr, HitlTestCase{..}) -> do - putStrLn $ - "Starting HITL test case " - <> show nr - <> " out of " - <> show (length testCases) - <> " named '" - <> name - <> "'..." - let requestedIds = map idFromHwTRef (Map.keys parameters) - when (anySame requestedIds) $ - error $ - "Multiple references to the same hardware target: " - <> show (requestedIds \\ nubOrd requestedIds) - -- Resolve the test case definition by replacing the references to - -- hardware targets with the actual hardware targets. - let resolvedTestCase = - HitlTestCase - { parameters = mapKeys (fromJust . (`Map.lookup` refToHwTMap)) parameters - , .. - } - exitCode <- runHitlTestCase v resolvedTestCase probesFilePath ilaDataDir - pure (name, exitCode) - - let failedTestCaseNames = fst <$> filter ((/= ExitSuccess) . snd) testResults - if null failedTestCaseNames - then do - putStrLn $ "All " <> show (length testCases) <> " HITL test cases passed." - else do - putStrLn $ - show (length failedTestCaseNames) - <> " out of " - <> show (length testCases) - <> " HITL test cases failed or timed out, namely:" - mapM_ (putStrLn . ('\t' :)) failedTestCaseNames - pure $ maximum $ map snd testResults - - case result of - Left e@TclException{retCode} -> do - print e - pure $ ExitFailure (fromMaybe 1 (readMaybe @Int retCode)) - Right exitCode -> pure exitCode - --- | Runs one test case of a HITL test group -runHitlTestCase :: - forall a b. - -- | Handle to a Vivado object that is to execute the Tcl - VivadoHandle -> - -- | The HITL test case to run - HitlTestCase HwTarget a b -> - -- | Path to the generated probes file - FilePath -> - -- | Filepath the the ILA data dump directory - FilePath -> - IO ExitCode -runHitlTestCase v testCase@HitlTestCase{..} probesFilePath ilaDataDir = do - if null parameters - then do - putStrLn - "WARNING: The HITL test case does not reference any hardware targets. Exiting." - pure ExitSuccess - else do - openHwT v (head (keys parameters)) - verifyHwIlas v - -- XXX: We should not rely on start probe assertion order. - -- See https://github.com/bittide/bittide-hardware/issues/638. - forM_ (sortOn (prettyShow . fst) (toAscList parameters)) $ \(hwT, param) -> do - openHwT v hwT - execCmd_ v "set_property" ["PROBES.FILE", embrace probesFilePath, "[current_hw_device]"] - refresh_hw_device v [] - let paramBitSize = natToNatural @(BitSize a) - verifyHitlVio v paramBitSize - - execCmd_ v "set_property" ["OUTPUT_VALUE", "0", getProbeTestStartTcl] - commit_hw_vio v ["[get_hw_vios]"] - refresh_hw_vio v ["[get_hw_vios]"] - done <- execCmd v "get_property" ["INPUT_VALUE", "$probe_test_done"] - when (done /= "0") $ - error $ - "Hardware target '" - <> prettyShow hwT - <> "' asserted its HITL VIO probe done before the test was started." - unless (paramBitSize == 0) $ do - hexWidth <- execCmd v "expr" [embrace ("(3 + " <> show paramBitSize <> ")/4")] - vioValue <- - execCmd - v - "format" - ["%0" <> hexWidth <> "llX " <> show (BitVector.unsafeToNatural (pack param))] - putStrLn $ "Setting probe_test_data to " <> vioValue <> "..." - execCmd_ v "set_property" ["OUTPUT_VALUE", vioValue, getProbeTestDataTcl] - - -- Activate the trigger for each ILA. - putStrLn "Verifying ILAs..." - ilas <- get_hw_ilas v [] - unless (null ilas) $ - putStrLn "Configuring and arming ILAs..." - forM_ ilas $ \ila -> do - _ <- current_hw_ila v [show ila] - - -- Set trigger probe (active high boolean) - -- TODO get probe from Tcl dictionary? - let triggerProbe = "[get_hw_probes -of_objects [current_hw_ila] */trigger*]" - execCmd_ v "set_property" ["trigger_compare_value", "eq1'b1", triggerProbe] - - -- Enable capture control and set capture probe (active high boolean) - execCmd_ v "set_property" ["control.capture_mode", "BASIC", "[current_hw_ila]"] - let captureProbe = "[get_hw_probes -of_objects [current_hw_ila] */capture*]" - execCmd_ v "set_property" ["capture_compare_value", "eq1'b1", captureProbe] - - -- Set the trigger position - execCmd_ v "set_property" ["control.trigger_position", "0", "[current_hw_ila]"] - - run_hw_ila v ["[current_hw_ila]"] - - -- Deassert HitlVio start probe - -- XXX: We should not rely on start probe values to be asserted after a - -- test ends. See https://github.com/bittide/bittide-hardware/issues/639. - execCmd_ v "set_property" ["OUTPUT_VALUE", "0", getProbeTestStartTcl] - commit_hw_vio v ["[get_hw_vios]"] - - -- Assert HitlVio start probe - execCmd_ v "set_property" ["OUTPUT_VALUE", "1", getProbeTestStartTcl] - commit_hw_vio v ["[get_hw_vios]"] - putStrLn $ "Started test case for hardware target " <> prettyShow hwT <> "." - - putStrLn $ "Waiting for test case '" <> name <> "' to end..." - testCaseExitCode <- waitTestCaseEnd v testCase probesFilePath - - putStrLn "Saving captured ILA data (if relevant)..." - forM_ (keys parameters) $ \hwT -> do - openHwT v hwT - execCmd_ v "set_property" ["PROBES.FILE", embrace probesFilePath, "[current_hw_device]"] - refresh_hw_device v ["-quiet"] - ilas <- get_hw_ilas v [] - let dir = ilaDataDir name prettyShow hwT - unless (null ilas) $ - putStrLn $ - "Saving captured ILA data to: " <> dir - forM_ ilas $ \ila -> do - _ <- current_hw_ila v [show ila] - ilaShortName <- getCurrentIlaShortName v - createDirectoryIfMissing True dir - execCmd_ v "current_hw_ila_data" ["[upload_hw_ila_data [current_hw_ila]]"] - -- Legacy CSV excludes radix information - execCmd_ v "write_hw_ila_data" ["-force", "-legacy_csv_file " <> dir ilaShortName] - execCmd_ v "write_hw_ila_data" ["-force", "-vcd_file " <> dir ilaShortName] - - pure testCaseExitCode diff --git a/bittide-shake/src/Development/Shake/Extra.hs b/bittide-shake/src/Development/Shake/Extra.hs deleted file mode 100644 index 0a1d10923..000000000 --- a/bittide-shake/src/Development/Shake/Extra.hs +++ /dev/null @@ -1,20 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Development.Shake.Extra where - -import Prelude - -import Data.Maybe (fromJust) -import Development.Shake - -import qualified Data.Aeson as Aeson - -suppressOutput :: [CmdOption] -suppressOutput = [EchoStdout False, EchoStderr True] - -decodeFile :: (Aeson.FromJSON a) => FilePath -> Action a -decodeFile path = do - need [path] - fromJust <$> liftIO (Aeson.decodeFileStrict path) diff --git a/bittide-shake/src/Paths/Bittide/Shake.hs b/bittide-shake/src/Paths/Bittide/Shake.hs deleted file mode 100644 index c58262142..000000000 --- a/bittide-shake/src/Paths/Bittide/Shake.hs +++ /dev/null @@ -1,9 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Paths.Bittide.Shake ( - module Paths_bittide_shake, -) where - -import Paths_bittide_shake diff --git a/bittide-shake/tests/doctests.hs b/bittide-shake/tests/doctests.hs deleted file mode 100644 index 16e12f876..000000000 --- a/bittide-shake/tests/doctests.hs +++ /dev/null @@ -1,14 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import System.Environment (getArgs) -import Test.DocTest (mainFromCabal) - -main :: IO () -main = do - -- We use Nix to setup tooling, not to provide GHC packages so we need to set --no-nix - args <- getArgs - mainFromCabal "bittide-shake" ("--no-nix" : args) diff --git a/bittide-tools/LICENSE b/bittide-tools/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/bittide-tools/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/bittide-tools/bittide-tools.cabal b/bittide-tools/bittide-tools.cabal deleted file mode 100644 index 3cf2374ae..000000000 --- a/bittide-tools/bittide-tools.cabal +++ /dev/null @@ -1,126 +0,0 @@ -cabal-version: 2.4 -name: bittide-tools -synopsis: - Bittide specific tools required for development, CI, - and testing - -version: 0.1 -license: Apache-2.0 -license-file: LICENSE -author: QBayLogic B.V. -maintainer: devops@qbaylogic.com -copyright: Copyright © 2024 Google LLC - -common common-options - default-extensions: - -- TemplateHaskell is used to support convenience functions such as - -- 'listToVecTH' and 'bLit'. - -- - -- `NoImplicitPrelude` is used because Clash offers Clash.Prelude - BangPatterns - BinaryLiterals - ConstraintKinds - DataKinds - DefaultSignatures - DeriveAnyClass - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveLift - DeriveTraversable - DerivingStrategies - ImportQualifiedPost - InstanceSigs - KindSignatures - LambdaCase - NoStarIsType - PolyKinds - QuasiQuotes - RankNTypes - ScopedTypeVariables - StandaloneDeriving - TemplateHaskell - TupleSections - TypeApplications - TypeFamilies - TypeOperators - ViewPatterns - - ghc-options: - -- Plugins to support type-level constraint solving on naturals: - -- - GHC.TypeLits.Extra.Solver - -- - GHC.TypeLits.Normalise - -- - GHC.TypeLits.KnownNat.Solver - -- Clash needs access to the source code in compiled modules: - -- -fexpose-all-unfoldings - -- Worker wrappers introduce unstable names for functions that might have - -- blackboxes attached for them. You can disable this, but be sure to add - -- a no-specialize pragma to every function with a blackbox. - -- -fno-worker-wrapper - -Wall - -Wcompat - -haddock - -fplugin=GHC.TypeLits.Extra.Solver - -fplugin=GHC.TypeLits.Normalise - -fplugin=GHC.TypeLits.KnownNat.Solver - -fexpose-all-unfoldings - -fno-worker-wrapper - - build-depends: - -- clash-prelude will set suitable version bounds for the plugins - Cabal, - base, - clash-prelude >=1.7.0 && <1.10, - ghc-typelits-extra, - ghc-typelits-knownnat, - ghc-typelits-natnormalise, - - default-language: Haskell2010 - -executable program-stream - import: common-options - ghc-options: - -Wall - -Wcompat - -threaded - - main-is: program/stream/Main.hs - build-depends: - base, - bittide, - bytestring, - clash-prelude, - -executable cc-plot - import: common-options - main-is: clockcontrol/plot/Main.hs - build-depends: - aeson, - array, - bittide, - bittide-experiments, - bittide-extra, - bittide-instances, - bytestring, - cassava, - cassava-conduit, - conduit, - containers, - data-default, - directory, - extra, - filepath, - text, - typelits-witnesses, - unordered-containers, - utf8-string, - vector, - - default-extensions: ImplicitPrelude - -- enable rtsopts so we can setup memory limits - ghc-options: - -Wall - -Wcompat - -threaded - -rtsopts diff --git a/bittide-tools/bittide-tools.cabal.license b/bittide-tools/bittide-tools.cabal.license deleted file mode 100644 index 2b4d55897..000000000 --- a/bittide-tools/bittide-tools.cabal.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/bittide-tools/clockcontrol/plot/Main.hs b/bittide-tools/clockcontrol/plot/Main.hs deleted file mode 100644 index 10830bedc..000000000 --- a/bittide-tools/clockcontrol/plot/Main.hs +++ /dev/null @@ -1,774 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PackageImports #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -Wno-orphans #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-} -{-# OPTIONS_GHC -fplugin GHC.TypeLits.Extra.Solver #-} -{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-} -{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-} - -module Main (main, knownTestsWithCcConf) where - -import Clash.Prelude ( - BitPack (..), - Index, - KnownDomain, - SNat (..), - Vec (..), - checkedTruncateB, - extend, - natToNum, - snatProxy, - ) - -import Clash.Signal.Internal (Femtoseconds (..)) -import Clash.Sized.Vector qualified as Vec - -import Data.Type.Equality ((:~:) (..)) -import GHC.TypeLits hiding (SNat) -import GHC.TypeLits.Compare ((:<=?) (..)) -import GHC.TypeLits.Witnesses ((%<=?)) -import GHC.TypeLits.Witnesses qualified as TLW (SNat (..)) - -import Clash.Prelude qualified as C -import Conduit ( - ConduitT, - Void, - await, - dropC, - mapC, - runConduit, - scanlC, - sinkList, - sourceHandle, - yield, - (.|), - ) -import Control.Arrow (first) -import Control.Exception (Exception (..), catch, throw) -import Control.Monad (filterM, forM, forM_, unless, when) -import Control.Monad.Extra (ifM, unlessM) -import Data.Bifunctor (bimap) -import Data.ByteString qualified as BS -import Data.ByteString.Char8 qualified as BSC -import Data.ByteString.Lazy qualified as BSL -import Data.ByteString.UTF8 qualified as UTF8 -import Data.Csv ( - FromField (..), - FromNamedRecord (..), - ToNamedRecord (..), - defaultDecodeOptions, - encodeByName, - (.:), - ) -import Data.Csv.Conduit ( - CsvStreamHaltParseError (..), - CsvStreamRecordParseError (..), - fromNamedCsvStreamError, - ) -import Data.HashMap.Strict qualified as HashMap -import Data.List (isSuffixOf, unzip4) -import Data.Map qualified as Map -import Data.Maybe (catMaybes, fromJust, fromMaybe, mapMaybe) -import Data.Proxy (Proxy (..)) -import Data.Set qualified as Set -import Data.String (fromString) -import Data.Text qualified as Text -import Data.Typeable (cast) -import Data.Vector qualified as Vector -import GHC.IO.Exception (IOErrorType (..), IOException (..)) -import GHC.Stack (HasCallStack) -import System.Directory ( - canonicalizePath, - createDirectoryIfMissing, - doesDirectoryExist, - listDirectory, - ) -import System.Environment (getArgs, getProgName) -import System.Exit (die) -import System.FilePath ( - takeBaseName, - takeExtensions, - takeFileName, - (), - ) -import System.IO ( - BufferMode (..), - Handle, - IOMode (..), - hClose, - hFlush, - hPutStr, - hSetBuffering, - openFile, - withFile, - ) -import Text.Read (readMaybe) -import "bittide-extra" Numeric.Extra (parseHex) - -import Bittide.Arithmetic.PartsPer (PartsPer (..), cyclesToPartsPerI, ppm) -import Bittide.ClockControl -import Bittide.ClockControl.StabilityChecker -import Bittide.Github.Artifacts -import Bittide.Hitl -import Bittide.Instances.Domains -import Bittide.Instances.Hitl.IlaPlot -import Bittide.Instances.Hitl.Setup -import Bittide.Plot -import Bittide.Report.ClockControl -import Bittide.Simulate.Config (CcConf, saveCcConfig, simTopologyFileName) -import Bittide.Topology - -import Bittide.Instances.Hitl.Tests (hitlTests) -import Bittide.Simulate.Config qualified as CcConf - --- A newtype wrapper for working with hex encoded types. -newtype Hex a = Hex {fromHex :: a} - deriving newtype (BitPack) - -instance (BitPack a) => FromField (Hex a) where - parseField = either fail pure . parseHex . UTF8.toString - -{- | The captured data entries, as they are dumped by the ILA of -'Bittide.Instances.Hitl.IlaPlot.callistoClockControlWithIla'. Fields marked with -an underscore are not used in the post processing. --} -data Capture (nodeCount :: Nat) (compressedElasticBufferBits :: Nat) = Capture - { sampleInBuffer :: Int - , _sampleInWindow :: Int - , _trigger :: Hex Bool - , _triggerSignal :: Hex Bool - , _capture :: Hex Bool - , captureCond :: Hex CaptureCondition - , globalTimestamp :: Hex (GlobalTimestamp Basic125) - , localTimestamp :: Hex (DiffResult (LocalTimestamp GthTx)) - , plotData :: Hex (PlotData (nodeCount - 1) compressedElasticBufferBits) - } - -instance - ( KnownNat nodeCount - , KnownNat compressedElasticBufferBits - , 1 <= nodeCount - ) => - FromNamedRecord (Capture nodeCount compressedElasticBufferBits) - where - parseNamedRecord v = - if HashMap.size v /= 3 + Vec.length ilaProbeNames - then fail "Row with more than 8 fields" - else - Capture - <$> v .: "Sample in Buffer" - <*> v .: "Sample in Window" - <*> v .: "TRIGGER" - <*> v .: portName 0 - <*> v .: portName 1 - <*> v .: portName 2 - <*> v .: portName 3 - <*> v .: portName 4 - <*> v .: portName 5 - where - portName = portName# ilaProbeNames - - portName# :: (KnownNat n) => Vec n String -> Index n -> BS.ByteString - portName# names = fromString . (names Vec.!!) - --- | A data point resulting from post processing the captured data. -data DataPoint (nodeCount :: Nat) (decompressedElasticBufferBits :: Nat) = DataPoint - { dpIndex :: Int - -- ^ the index of the corresponding sample of the dump - , dpGlobalLast :: GlobalTimestamp Basic125 - -- ^ the global time stamp of the previous scheduled capture - , dpGlobalTime :: Femtoseconds - -- ^ the absolute number of femtoseconds since the start of the - -- dump according to the global synchronized clock - , dpDrift :: PartsPer - -- ^ clock frequency difference in 'PartsPer' - , dpCCChanges :: Int - -- ^ the accumulated number FINC/FDECs integrated over time - , dpRfStage :: ReframingStage - -- ^ the reframing stage - , dpDataCounts :: - Vec - nodeCount - (Maybe (RelDataCount decompressedElasticBufferBits)) - -- ^ the elastic buffer data counts of the available links - , dpStability :: Vec nodeCount (Maybe StabilityIndication) - -- ^ the stability indicators for each of the elastic buffers - -- of the available links - } - -instance - ( KnownNat nodeCount - , KnownNat decompressedElasticBufferBits - , 1 <= nodeCount - ) => - ToNamedRecord (DataPoint nodeCount decompressedElasticBufferBits) - where - toNamedRecord DataPoint{..} = - HashMap.fromList $ - fmap (bimap BSC.pack BSC.pack) $ - [ ("Index", show dpIndex) - , ("Synchronized Time (fs)", show $ toInteger dpGlobalTime) - , ("Clock Period Drift (ppt)", case dpDrift of Ppt ppt -> show ppt) - , ("Integrated FINC/FDECs", show dpCCChanges) - , ("Reframing State", rf2bs dpRfStage) - ] - <> [ ("EB " <> show i, show $ toInteger x) - | (i, x) <- topologyView dpDataCounts - ] - <> [ (show i <> " is stable", b2bs $ stable x) - | (i, x) <- topologyView dpStability - ] - <> [ (show i <> " is settled", b2bs $ settled x) - | (i, x) <- topologyView dpStability - ] - where - topologyView = - catMaybes - . Vec.toList - . fmap (\(i, x) -> (i,) <$> x) - . Vec.zip Vec.indicesI - - b2bs = \case - False -> "0" - True -> "1" - - rf2bs = \case - RSDetect -> "Detect" - RSWait -> "Wait" - RSDone -> "Done" - -{- | Multiplies some 'Femtoseconds' with any numerical value. Note -that this operation can produce negative values, which is -intentional. --} -infix 9 ~* - -(~*) :: (Integral a) => a -> Femtoseconds -> Femtoseconds -a ~* (Femtoseconds b) = Femtoseconds $ fromInteger (toInteger a) * b - -deriving newtype instance Num Femtoseconds -deriving newtype instance Real Femtoseconds -deriving newtype instance Enum Femtoseconds -deriving newtype instance Integral Femtoseconds - -type CsvParseError = CsvParseError# CsvStreamRecordParseError -data CsvParseError# e = CsvParseError Int e deriving (Show) -instance Exception CsvParseError - --- | Data post processor. -postProcess :: - forall - topologySize - -- \^ the size of the topology underlying the data to be processed - decompressedElasticBufferBits - -- \^ the bitsize of the elastic buffer entries after decompression - utilizedFpgaCount - -- \^ the number of hardware nodes used to generated the data - compressedElasticBufferBits - -- \^ the bitsize of the elastic buffer entries before decompression - anyMonad. - ( KnownNat topologySize - , KnownNat decompressedElasticBufferBits - , KnownNat utilizedFpgaCount - , KnownNat compressedElasticBufferBits - , Monad anyMonad - , compressedElasticBufferBits <= decompressedElasticBufferBits - , 1 <= topologySize - , 1 <= utilizedFpgaCount - , topologySize <= utilizedFpgaCount - ) => - Topology topologySize -> - Index topologySize -> - Vec (utilizedFpgaCount - 1) (Index utilizedFpgaCount) -> - ConduitT - (Capture utilizedFpgaCount compressedElasticBufferBits) - (DataPoint topologySize decompressedElasticBufferBits) - anyMonad - () -postProcess t i links = - scanlC process initDummy - -- finally drop the dummy, the trigger and the calibration entries - .| (dropC 4 >> mapC id) - where - topologyView :: - Vec (utilizedFpgaCount - 1) a -> - Vec topologySize (Maybe a) - topologyView = - foldr (\(j, x) -> Vec.replace j $ Just x) (Vec.repeat Nothing) - . filter (hasEdge t i . fst) - . fmap - ( first $ - checkedTruncateB @topologySize @(utilizedFpgaCount - topologySize) - ) - . filter ((<= natToNum @(topologySize - 1)) . fst) - . Vec.toList - . Vec.zip links - - process - prevDP - Capture - { globalTimestamp - , captureCond = fromHex -> captureType - , localTimestamp = fromHex -> localTimestamp - , sampleInBuffer - , plotData = fromHex -> PlotData{dSpeedChange, dEBData, dRfStageChange} - } = - let - globalTime = globalTsToFs $ fromHex globalTimestamp - - driftPartsPer :: PartsPer - driftPartsPer - | isScheduledCaptureCondition captureType = - cyclesToPartsPerI - (Proxy @GthTx) - (Proxy @ScheduledCapturePeriod) - ( case localTimestamp of - NoReference -> error $ "process: NoReference:" <> show sampleInBuffer - TooLarge -> error $ "process: TooLarge:" <> show sampleInBuffer - Difference x -> x + 1 - ) - | otherwise = - dpDrift prevDP - - ccChanges = - dpCCChanges prevDP - + natToNum @AccWindowHeight * sign dSpeedChange - - dataCounts = - ( \a b -> - ( \(x, _, _) y -> - extend - @_ - @compressedElasticBufferBits - @( decompressedElasticBufferBits - - compressedElasticBufferBits - ) - x - + y - ) - <$> a - <*> b - ) - <$> topologyView dEBData - <*> dpDataCounts prevDP - in - DataPoint - { dpIndex = sampleInBuffer - , dpGlobalLast = - case captureType of - DataChange -> dpGlobalLast prevDP - _ -> fromHex globalTimestamp - , dpGlobalTime = globalTime - , dpDrift = driftPartsPer - , dpCCChanges = ccChanges - , dpRfStage = - case dRfStageChange of - Stable -> dpRfStage prevDP - ToDetect -> RSDetect - ToWait -> RSWait - ToDone -> RSDone - , dpDataCounts = dataCounts - , dpStability = - let combine (Just (_, st, se)) (Just (StabilityIndication{..})) = - Just - StabilityIndication - { stable = fromMaybe stable st - , settled = fromMaybe settled se - } - combine _ _ = Nothing - in Vec.zipWith - combine - (topologyView dEBData) - (dpStability prevDP) - } - - initDummy = - DataPoint - { dpIndex = -1 - , dpGlobalLast = (0, 0) - , dpGlobalTime = 0 - , dpDrift = 0 - , dpCCChanges = 0 - , dpRfStage = RSDetect - , dpDataCounts = topologyView $ Vec.repeat 0 - , dpStability = - topologyView $ - Vec.repeat - StabilityIndication - { stable = False - , settled = False - } - } - - globalTsToFs :: GlobalTimestamp Basic125 -> Femtoseconds - globalTsToFs (pulses, cycles) = - (pulses ~* syncPulsePeriodFs) - + (cycles ~* clockPeriodFs (Proxy @Basic125)) - where - syncPulsePeriodFs = Femtoseconds $ natToNum @(1000 * SyncPulsePeriod) - -fromCsvDump :: - forall - decompressedElasticBufferBits - -- \^ the bitsize of the elastic buffer entries after decompression - topologySize - -- \^ the size of the topology underlying the data to be processed - utilizedFpgaCount. - -- \^ the number of hardware nodes used to generated the data - - ( KnownNat decompressedElasticBufferBits - , CompressedBufferSize <= decompressedElasticBufferBits - , KnownNat topologySize - , KnownNat utilizedFpgaCount - , 1 <= topologySize - , 1 <= utilizedFpgaCount - , topologySize <= utilizedFpgaCount - ) => - Topology topologySize -> - Index topologySize -> - Vec (utilizedFpgaCount - 1) (Index utilizedFpgaCount) -> - (Handle, FilePath) -> - ConduitT () Void IO [DataPoint topologySize decompressedElasticBufferBits] -fromCsvDump t i links (csvHandle, csvFile) = - -- turn the input file into a conduit source - sourceHandle csvHandle - -- plugin the CSV parser - .| fromNamedCsvStreamError defaultDecodeOptions toIOE - -- drop the first two header lines and ensure that the remaining - -- data entries are valid - .| (dropC 1 >> checkForErrors 0) - -- post process the data - .| postProcess - @topologySize - @decompressedElasticBufferBits - @utilizedFpgaCount - @CompressedBufferSize - t - i - links - -- return as a list - .| sinkList - where - toIOE (HaltingCsvParseError _ msg) = - IOError - { ioe_handle = Just csvHandle - , ioe_type = SystemError - , ioe_location = csvFile - , ioe_description = Text.unpack msg - , ioe_errno = Nothing - , ioe_filename = Just csvFile - } - - checkForErrors n = - await >>= \case - Nothing -> return () - Just (Left e) -> throw $ CsvParseError n e - Just (Right x) -> yield x >> checkForErrors (n + 1) - -{- | The HITL tests, whose post proc data offers a simulation config -for plotting. --} -knownTestsWithCcConf :: (HasCallStack) => Map.Map String [(String, CcConf)] -knownTestsWithCcConf = Map.fromList (mapMaybe go hitlTests) - where - justOrDie _ (Just x) = Just x - justOrDie k Nothing = error $ "No CcConf for " <> show k - - go HitlTestGroup{topEntity, testCases = iters :: [HitlTestCase HwTargetRef q r]} = - case cast @[HitlTestCase HwTargetRef q r] @[HitlTestCase HwTargetRef q CcConf] iters of - Just q -> - Just - ( show topEntity - , Map.toList (Map.mapMaybeWithKey justOrDie (mGetPPD @CcConf @HwTargetRef q)) - ) - Nothing -> Nothing - -{- | Calculate an offset such that the clocks start at their set offsets. That is -to say, we consider the reference clock to be at 0 fs by definition. The offsets -of the other clocks are then measured relative to this reference clock. We don't -particularly care about the reference clocks in our plots however, it is much more -useful to shift it in such a way that the other clocks start at their set offsets. -This concept is explained visually in: https://github.com/bittide/bittide-hardware/issues/607. - -This function will return an error ('Left') if the clocks cannot be shifted in -such a way that they all start at their set offsets. If this happens, something -is seriously wrong. --} -getOffsetCorrection :: - (KnownNat nNodes) => - -- | Post processed data - one per FPGA - -- - -- TODO: Nicer data structure - Vec nNodes [(a, PartsPer {- relative offset -}, b, c)] -> - -- | The desired offsets for the clocks - Vec nNodes PartsPer -> - -- | Correction, if a sensible one can be found - IO (Either String PartsPer) -getOffsetCorrection postProcessData desiredOffsets = do - -- Gather first sampled offset for each node - measuredOffsets <- forM (C.zip C.indicesI postProcessData) $ - \(nodeNo, unzip4 -> (_, offsets, _, _)) -> do - case offsets of - [] -> die $ "No offsets for node " <> show nodeNo - (offset : _) -> pure offset - - let zippedOffsets = C.zip measuredOffsets desiredOffsets - - case zippedOffsets of - Nil -> die "No offsets, nNodes ~ 0?" - (measuredOffset0, desiredOffset0) `Cons` _ -> pure $ do - let correction = desiredOffset0 - measuredOffset0 - forM_ zippedOffsets $ \(measuredOffset, desiredOffset) -> do - when (abs (measuredOffset + correction - desiredOffset) >= ppm 5) $ - Left $ - unlines - [ "Clocks did not start at their set offsets." - , "" - , "Measured offsets: " <> show measuredOffsets - , "Desired offsets: " <> show desiredOffsets - , "Corrected offsets: " <> show (((+) correction) <$> measuredOffsets) - , "Correction: " <> show correction - ] - pure () - pure correction - -plotTest :: - (KnownDomain refDom) => - Proxy refDom -> - FilePath -> - CcConf -> - FilePath -> - FilePath -> - IO () -plotTest refDom testDir cfg dir globalOutDir = do - checkDependencies >>= maybe (return ()) die - putStrLn $ "Creating plots for test case: " <> testName - - let - knownId = - flip Set.member $ - Set.fromList $ - Vec.toList $ - Vec.imap (\i a -> show i <> "_" <> fst a) fpgaSetup - topFromDirs = - listDirectory dir - >>= filterM (doesDirectoryExist . (dir )) - >>= return . fromJust . someNatVal . toInteger . length . filter knownId - >>= \case - SomeNat n -> return $ STop $ complete $ snatProxy n - - STop (t :: Topology topologySize) <- - case CcConf.ccTopologyType cfg of - Random{} -> topFromDirs - DotFile f -> readFile f >>= either die return . fromDot - tt -> froccTopologyType tt >>= either die return - - case TLW.SNat @topologySize %<=? TLW.SNat @FpgaCount of - LE Refl -> case TLW.SNat @1 %<=? TLW.SNat @topologySize of - LE Refl -> do - let fpgas = - Vec.toList $ - Vec.imap (,) $ - Vec.take @topologySize @(FpgaCount - topologySize) - SNat - fpgaSetup - - postProcessData <- do - forM fpgas $ \(i, (fpgaId, links)) -> - concat . filter (not . null) <$> do - let d = dir (show i <> "_" <> fpgaId) - unlessM (doesDirectoryExist d) $ die $ "No directory: " <> d - csvFiles <- checkCsvFilesExist d <$> listDirectory d - forM csvFiles $ \f -> do - h <- openFile f ReadMode - rs <- catch - ( do - rs <- runConduit $ fromCsvDump @CccBufferSize t i links (h, f) - putStrLn ("Using " <> (takeBaseName d takeFileName f)) - return rs - ) - $ \(err :: CsvParseError) -> case err of - -- Ignore additional CSV files that may have - -- been produced by other ILAs. They are - -- identified via an error while parsing the - -- header row. - CsvParseError 0 _ -> return [] - CsvParseError n (CsvStreamRecordParseError msg) -> - error $ - unlines - [ "Error while parsing" - , "" - , " " <> f - , "" - , "Line " <> show (n + 3) <> ", " <> Text.unpack msg - ] - hClose h - - let - ls = show <$> filter (hasEdge t i) (Vec.toList Vec.indicesI) - header = - Vector.fromList $ - map BSC.pack $ - [ "Index" - , "Synchronized Time (fs)" - , "Clock Period Drift (ppt)" - , "Integrated FINC/FDECs" - , "Reframing State" - ] - <> (("EB " <>) <$> ls) - <> ((<> " is stable") <$> ls) - <> ((<> " is settled") <$> ls) - - unless (null rs) $ do - createDirectoryIfMissing True outDir - BSL.writeFile (outDir (takeFileName d <> ".csv")) $ - encodeByName header rs - - return (toPlotData <$> rs) - - let postProcessDataVec = Vec.unsafeFromList postProcessData - - -- Calculate offset correction for readability purposes. See: - -- https://github.com/bittide/bittide-hardware/issues/607 - (maybeError, maybeOffsetCorrection) <- - case cfg.clockOffsets of - Nothing -> pure (Nothing, Nothing) - Just (Vec.unsafeFromList -> offsets) -> - getOffsetCorrection postProcessDataVec offsets >>= \case - Left err -> pure (Just err, Nothing) - Right correction -> pure (Nothing, Just correction) - - createDirectoryIfMissing True outDir - plot maybeOffsetCorrection outDir t postProcessDataVec - - let - allStable = - all ((\(_, _, _, xs) -> all (stable . snd) xs) . last) postProcessData - cfg1 = - cfg - { CcConf.outDir = outDir - , CcConf.stable = Just allStable - } - ids = bimap toInteger fst <$> fpgas - - case CcConf.ccTopologyType cfg of - Random{} -> writeTop Nothing - DotFile f -> readFile f >>= writeTop . Just - tt -> froccTopologyType tt >>= either die (`saveCcConfig` cfg1) - checkIntermediateResults outDir - >>= maybe (generateReport refDom "HITLT Report" outDir ids cfg1) die - - -- Fail if clocks did not start at their set offsets. We purposely fail - -- after generating the report, because the report generation is very - -- useful for debugging. - maybe (return ()) die maybeError - _ -> die "Empty topology" - _ -> die "Topology is larger than expected" - where - testName = takeFileName testDir - outDir = globalOutDir testName - - checkCsvFilesExist d xs = - let ys = filter ((== ".csv") . takeExtensions) $ fmap (d ) xs - in case ys of - [] -> error $ d <> " does not contain any *.csv files. Aborting." - _ -> ys - - toPlotData :: - DataPoint n decompressedElasticBufferBits -> - ( Femtoseconds - , PartsPer - , ReframingStage - , [ ( RelDataCount decompressedElasticBufferBits - , StabilityIndication - ) - ] - ) - toPlotData DataPoint{..} = - ( dpGlobalTime - , dpDrift - , dpRfStage - , mapMaybe (uncurry $ liftA2 (,)) $ - Vec.toList $ - Vec.zip dpDataCounts dpStability - ) - - writeTop (fromMaybe "digraph{}" -> str) = - withFile (outDir simTopologyFileName) WriteMode $ \h -> do - hSetBuffering h NoBuffering - hPutStr h str - hFlush h - hClose h - -{- | Try to parse a run artifact reference. - ->>> parseArtifactRef "123:build-debug" -Just (123,"build-debug") ->>> parseArtifactRef "123_my_dir" -Nothing --} -parseArtifactRef :: String -> Maybe (Int, String) -parseArtifactRef arg = case span (/= ':') arg of - (readMaybe -> Just jobId, _ : jobName) -> Just (jobId, jobName) - _ -> Nothing - -{- | Given either a Github artifact reference (see 'parseArtifactRef') or a local -directory, return the fully qualified test name and the directory containing -a folder called \"ila-data\". --} -getSourceData :: String -> IO (String, FilePath) -getSourceData artifactRef | Just (jobId, jobName) <- parseArtifactRef artifactRef = do - -- Get artifact from Github - let fullArtifactName = "_build-" <> jobName <> "-debug" - artifactResult <- retrieveArtifact (show jobId) fullArtifactName ("_build" "plot") - case artifactResult of - Just err -> die (unlines ["Cannot retrieve artifact.", show err]) - Nothing -> do - let vivadoDir = "_build" "plot" "vivado" - dirs <- listDirectory vivadoDir - case filter (('.' : jobName) `isSuffixOf`) dirs of - [dir] -> getSourceData (vivadoDir dir) - _ -> - die $ "No or multiple directories with name containing " <> jobName <> " in " <> vivadoDir -getSourceData dir = do - -- Get artifact from local directory - let ilaDataDir = dir "ila-data" - fullyQualifiedTestName <- takeFileName <$> canonicalizePath dir - ifM - (doesDirectoryExist ilaDataDir) - (return (fullyQualifiedTestName, ilaDataDir)) - (die $ "No 'ila-data' directory in " <> dir) - -main :: IO () -main = - getArgs >>= \case - [plotDataSource, outputDir] -> do - (fullyQualifiedTestName, plotDataDir) <- getSourceData plotDataSource - ccConfs <- case Map.lookup fullyQualifiedTestName knownTestsWithCcConf of - Nothing -> die $ "Could not find test config: " <> fullyQualifiedTestName - Just ccConfs -> pure ccConfs - - forM_ ccConfs $ \(testName, ccConf) -> do - plotTest (Proxy @Basic125) testName ccConf (plotDataDir testName) outputDir - _ -> wrongNumberOfArguments - where - wrongNumberOfArguments = do - name <- getProgName - die $ - "Wrong number of arguments. Aborting.\n\n" - <> "Usage: " - <> name - <> " " diff --git a/bittide-tools/program/stream/Main.hs b/bittide-tools/program/stream/Main.hs deleted file mode 100644 index bb505ec55..000000000 --- a/bittide-tools/program/stream/Main.hs +++ /dev/null @@ -1,28 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -import Prelude - -import Bittide.ProcessingElement.ProgramStream - -import Clash.Prelude qualified as C -import Data.ByteString qualified as BS - -import System.Environment (getArgs) -import System.Exit (exitFailure, exitSuccess) -import System.IO (hPutStrLn, stderr) - -main :: IO () -main = do - args <- getArgs - case args of - [path] -> do - elfContent <- BS.readFile path - let streamBytes = elfStream elfContent - let stream = BS.pack (C.bitCoerce <$> streamBytes) - BS.putStr stream - exitSuccess - _ -> do - hPutStrLn stderr "This program requires exactly one argument, a path to an ELF" - exitFailure diff --git a/bittide/LICENSE b/bittide/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/bittide/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/bittide/bittide.cabal b/bittide/bittide.cabal deleted file mode 100644 index 15a6ce76d..000000000 --- a/bittide/bittide.cabal +++ /dev/null @@ -1,244 +0,0 @@ -cabal-version: 2.4 -name: bittide -synopsis: - Bittide hardware descriptions, which should be - target independent, loosely connected / standalone - -version: 0.1 -license: Apache-2.0 -license-file: LICENSE -author: QBayLogic B.V. -maintainer: devops@qbaylogic.com -copyright: Copyright © 2022-2024 Google LLC -data-files: - data/clock_configs/*.csv - -common common-options - default-extensions: - -- TemplateHaskell is used to support convenience functions such as - -- 'listToVecTH' and 'bLit'. - -- - -- `NoImplicitPrelude` is used because Clash offers Clash.Prelude - BangPatterns - BinaryLiterals - ConstraintKinds - DataKinds - DefaultSignatures - DeriveAnyClass - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveLift - DeriveTraversable - DerivingStrategies - InstanceSigs - KindSignatures - LambdaCase - NoImplicitPrelude - NoStarIsType - PolyKinds - QuasiQuotes - RankNTypes - ScopedTypeVariables - StandaloneDeriving - TemplateHaskell - TupleSections - TypeApplications - TypeFamilies - TypeOperators - ViewPatterns - - ghc-options: - -- Plugins to support type-level constraint solving on naturals: - -- - GHC.TypeLits.Extra.Solver - -- - GHC.TypeLits.Normalise - -- - GHC.TypeLits.KnownNat.Solver - -- Clash needs access to the source code in compiled modules: - -- -fexpose-all-unfoldings - -- Worker wrappers introduce unstable names for functions that might have - -- blackboxes attached for them. You can disable this, but be sure to add - -- a no-specialize pragma to every function with a blackbox. - -- -fno-worker-wrapper - -- Strict annotations - while sometimes preventing space leaks - trigger - -- optimizations Clash can't deal with. See: - -- - -- https://github.com/clash-lang/clash-compiler/issues/2361 - -- - -- These flags disables the optimization: - -- -fno-unbox-small-strict-fields - -- -fno-unbox-strict-fields - -Wall - -Wcompat - -fplugin=GHC.TypeLits.Extra.Solver - -fplugin=GHC.TypeLits.Normalise - -fplugin=GHC.TypeLits.KnownNat.Solver - -fconstraint-solver-iterations=8 - -fexpose-all-unfoldings - -fno-worker-wrapper - -fno-unbox-small-strict-fields - -fno-unbox-strict-fields - - default-language: Haskell2010 - build-depends: - ghc-typelits-extra >=0.4.4, - ghc-typelits-knownnat >=0.7.7, - ghc-typelits-natnormalise >=0.7.7, - -library - import: common-options - hs-source-dirs: src - build-depends: - Cabal, - aeson, - array, - base, - bittide-extra, - bytestring, - cassava, - clash-cores, - clash-lib >=1.6.3 && <1.10, - clash-prelude >=1.6.3 && <1.10, - clash-protocols, - clash-protocols-base, - clash-vexriscv, - constraints >=0.13.3 && <0.15, - containers >=0.4.0 && <0.7, - directory, - elf, - exceptions, - extra, - filepath, - infinite-list, - mtl, - pretty-show, - process, - random, - string-interpolate ^>=0.3, - template-haskell, - text, - - exposed-modules: - Bittide.Arithmetic.PartsPer - Bittide.Arithmetic.Time - Bittide.Axi4 - Bittide.Axi4.Internal - Bittide.Calendar - Bittide.ClockControl - Bittide.ClockControl.Callisto - Bittide.ClockControl.Callisto.Types - Bittide.ClockControl.Callisto.Util - Bittide.ClockControl.ParseRegisters - Bittide.ClockControl.Registers - Bittide.ClockControl.Si5391A - Bittide.ClockControl.Si5395J - Bittide.ClockControl.Si539xSpi - Bittide.ClockControl.StabilityChecker - Bittide.Counter - Bittide.DoubleBufferedRam - Bittide.ElasticBuffer - Bittide.Ethernet.Mac - Bittide.Node - Bittide.ProcessingElement - Bittide.ProcessingElement.DeviceTreeCompiler - Bittide.ProcessingElement.ProgramStream - Bittide.ProcessingElement.ReadElf - Bittide.ProcessingElement.Util - Bittide.ScatterGather - Bittide.SharedTypes - Bittide.Switch - Bittide.Transceiver - Bittide.Transceiver.Cdc - Bittide.Transceiver.Comma - Bittide.Transceiver.Prbs - Bittide.Transceiver.ResetManager - Bittide.Transceiver.WordAlign - Bittide.Wishbone - Clash.Cores.Extra - Clash.Cores.UART.Extra - Clash.Cores.Xilinx.Extra - Clash.Cores.Xilinx.GTH - Clash.Cores.Xilinx.GTH.BlackBoxes - Clash.Cores.Xilinx.GTH.Internal - Clash.Cores.Xilinx.SystemMonitor - Clash.Cores.Xilinx.Xpm.Cdc.Handshake.Extra - Clash.Explicit.Reset.Extra - Clash.Functor.Extra - Clash.Sized.Extra - Data.Constraint.Nat.Extra - System.IO.Temp.Extra - - other-modules: - Paths_bittide - -test-suite doctests - type: exitcode-stdio-1.0 - default-language: Haskell2010 - main-is: doctests.hs - ghc-options: - -Wall - -Wcompat - -threaded - - hs-source-dirs: tests - build-depends: - base, - bittide, - doctest-parallel >=0.3.0.1 && <0.4, - filepath, - -test-suite unittests - import: common-options - hs-source-dirs: tests - type: exitcode-stdio-1.0 - main-is: UnitTests.hs - ghc-options: - -Wall - -Wcompat - -threaded - -rtsopts - -with-rtsopts=-N12 - - other-modules: - Tests.Axi4 - Tests.Axi4.Generators - Tests.Axi4.Properties - Tests.Axi4.Types - Tests.Calendar - Tests.ClockControl.Si539xSpi - Tests.Counter - Tests.DoubleBufferedRam - Tests.ElasticBuffer - Tests.Haxioms - Tests.ProcessingElement.ReadElf - Tests.ScatterGather - Tests.Shared - Tests.StabilityChecker - Tests.Switch - Tests.Transceiver - Tests.Transceiver.Prbs - Tests.Transceiver.WordAlign - Tests.Wishbone - - build-depends: - HUnit, - base, - bittide, - bittide-extra, - bytestring, - clash-cores, - clash-lib, - clash-prelude, - clash-prelude-hedgehog >=1.6 && <1.10, - clash-protocols, - constraints >=0.13.3 && <0.15, - containers, - elf, - extra, - hedgehog >=1.0 && <1.5, - lifted-async, - tasty >=1.4 && <1.6, - tasty-expected-failure, - tasty-hedgehog >=1.2 && <1.5, - tasty-hunit, - tasty-th, diff --git a/bittide/bittide.cabal.license b/bittide/bittide.cabal.license deleted file mode 100644 index 848612f0e..000000000 --- a/bittide/bittide.cabal.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/bittide/data/clock_configs/README.md b/bittide/data/clock_configs/README.md deleted file mode 100644 index e28681091..000000000 --- a/bittide/data/clock_configs/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Clock configs -Clock configurations are built with [ClockBuilder Pro](https://www.skyworksinc.com/Application-Pages/Clockbuilder-Pro-Software). The `.slabtimeproj` can be imported by ClockBuilder Pro, the CSVs can be generated using it. In turn, CSVs can be read by `Bittide.ClockControl.ParseRegisters.parseFromFileToRegisterMap`. diff --git a/bittide/data/clock_configs/Si5395J-200MHz-100ppb-Registers.csv b/bittide/data/clock_configs/Si5395J-200MHz-100ppb-Registers.csv deleted file mode 100644 index d6cf67860..000000000 --- a/bittide/data/clock_configs/Si5395J-200MHz-100ppb-Registers.csv +++ /dev/null @@ -1,622 +0,0 @@ -# Si534x/7x/8x/9x Registers Script -# -# Part: Si5395 -# Project File: \\VBOXSVR\win_vm_shared\Si5395J-200MHz-100ppb.slabtimeproj -# Design ID: 200_100b -# Includes Pre/Post Download Control Register Writes: Yes -# Device Revision: A -# Creator: ClockBuilder Pro v4.11.0.1 [2023-09-14] -# Created On: 2024-04-10 14:33:52 GMT+02:00 -Address,Data -# -# Start configuration preamble -0x0B24,0xC0 -0x0B25,0x00 -0x0540,0x01 -# End configuration preamble -# -# Delay 300 msec -# Delay is worst case time for device to complete any calibration -# that is running due to device state change previous to this script -# being processed. -# -# Start configuration registers -0x0006,0x00 -0x0007,0x00 -0x0008,0x00 -0x000B,0x68 -0x0016,0x02 -0x0017,0xDC -0x0018,0xFF -0x0019,0xFF -0x001A,0xFF -0x0023,0xFF -0x0024,0x0F -0x0025,0x00 -0x0026,0x00 -0x0027,0x00 -0x0028,0x00 -0x002B,0x02 -0x002C,0x00 -0x002D,0x00 -0x002E,0x00 -0x002F,0x00 -0x0030,0x00 -0x0031,0x00 -0x0032,0x00 -0x0033,0x00 -0x0034,0x00 -0x0035,0x00 -0x0036,0x00 -0x0037,0x00 -0x0038,0x00 -0x0039,0x00 -0x003A,0x00 -0x003B,0x00 -0x003C,0x00 -0x003D,0x00 -0x003E,0x00 -0x003F,0x00 -0x0040,0x04 -0x0041,0x00 -0x0042,0x00 -0x0043,0x00 -0x0044,0x00 -0x0045,0x0C -0x0046,0x00 -0x0047,0x00 -0x0048,0x00 -0x0049,0x00 -0x004A,0x00 -0x004B,0x00 -0x004C,0x00 -0x004D,0x00 -0x004E,0x00 -0x004F,0x00 -0x0050,0x0F -0x0051,0x00 -0x0052,0x00 -0x0053,0x00 -0x0054,0x00 -0x0055,0x00 -0x0056,0x00 -0x0057,0x00 -0x0058,0x00 -0x0059,0x00 -0x005A,0x00 -0x005B,0x00 -0x005C,0x00 -0x005D,0x00 -0x005E,0x00 -0x005F,0x00 -0x0060,0x00 -0x0061,0x00 -0x0062,0x00 -0x0063,0x00 -0x0064,0x00 -0x0065,0x00 -0x0066,0x00 -0x0067,0x00 -0x0068,0x00 -0x0069,0x00 -0x0092,0x00 -0x0093,0x00 -0x0095,0x00 -0x0096,0x00 -0x0098,0x00 -0x009A,0x00 -0x009B,0x00 -0x009D,0x00 -0x009E,0x00 -0x00A0,0x00 -0x00A2,0x00 -0x00A9,0x00 -0x00AA,0x00 -0x00AB,0x00 -0x00AC,0x00 -0x00E5,0x01 -0x00EA,0x00 -0x00EB,0x00 -0x00EC,0x00 -0x00ED,0x00 -0x0102,0x01 -0x0103,0x06 -0x0104,0x09 -0x0105,0x3E -0x0106,0x18 -0x0108,0x01 -0x0109,0x09 -0x010A,0x3B -0x010B,0x28 -0x010D,0x01 -0x010E,0x09 -0x010F,0x3B -0x0110,0x28 -0x0112,0x01 -0x0113,0x09 -0x0114,0x3B -0x0115,0x28 -0x0117,0x01 -0x0118,0x09 -0x0119,0x3B -0x011A,0x28 -0x011C,0x01 -0x011D,0x09 -0x011E,0x3B -0x011F,0x28 -0x0121,0x01 -0x0122,0x09 -0x0123,0x3B -0x0124,0x28 -0x0126,0x01 -0x0127,0x09 -0x0128,0x3B -0x0129,0x28 -0x012B,0x01 -0x012C,0x09 -0x012D,0x3B -0x012E,0x28 -0x0130,0x01 -0x0131,0x09 -0x0132,0x3B -0x0133,0x28 -0x0135,0x02 -0x0136,0x09 -0x0137,0x3E -0x0138,0x19 -0x013A,0x06 -0x013B,0x09 -0x013C,0x3E -0x013D,0x19 -0x013F,0x00 -0x0140,0x00 -0x0141,0x40 -0x0142,0xFF -0x0206,0x00 -0x0208,0x00 -0x0209,0x00 -0x020A,0x00 -0x020B,0x00 -0x020C,0x00 -0x020D,0x00 -0x020E,0x00 -0x020F,0x00 -0x0210,0x00 -0x0211,0x00 -0x0212,0x00 -0x0213,0x00 -0x0214,0x00 -0x0215,0x00 -0x0216,0x00 -0x0217,0x00 -0x0218,0x00 -0x0219,0x00 -0x021A,0x00 -0x021B,0x00 -0x021C,0x00 -0x021D,0x00 -0x021E,0x00 -0x021F,0x00 -0x0220,0x00 -0x0221,0x00 -0x0222,0x00 -0x0223,0x00 -0x0224,0x00 -0x0225,0x00 -0x0226,0x00 -0x0227,0x00 -0x0228,0x00 -0x0229,0x00 -0x022A,0x00 -0x022B,0x00 -0x022C,0x00 -0x022D,0x00 -0x022E,0x00 -0x022F,0x00 -0x0231,0x0B -0x0232,0x0B -0x0233,0x0B -0x0234,0x0B -0x0235,0x00 -0x0236,0x00 -0x0237,0x00 -0x0238,0xC0 -0x0239,0x89 -0x023A,0x00 -0x023B,0x00 -0x023C,0x00 -0x023D,0x00 -0x023E,0x80 -0x0247,0x00 -0x0248,0x00 -0x0249,0x00 -0x024A,0x00 -0x024B,0x00 -0x024C,0x00 -0x024D,0x00 -0x024E,0x00 -0x024F,0x00 -0x0250,0x00 -0x0251,0x00 -0x0252,0x00 -0x0253,0x00 -0x0254,0x00 -0x0255,0x00 -0x0256,0x00 -0x0257,0x00 -0x0258,0x00 -0x0259,0x00 -0x025A,0x00 -0x025B,0x00 -0x025C,0x00 -0x025D,0x00 -0x025E,0x00 -0x025F,0x00 -0x0260,0x00 -0x0261,0x00 -0x0262,0x00 -0x0263,0x00 -0x0264,0x00 -0x0265,0x0F -0x0266,0x00 -0x0267,0x00 -0x0268,0x00 -0x0269,0x00 -0x026A,0x00 -0x026B,0x32 -0x026C,0x30 -0x026D,0x30 -0x026E,0x5F -0x026F,0x31 -0x0270,0x30 -0x0271,0x30 -0x0272,0x62 -0x028A,0x00 -0x028B,0x00 -0x028C,0x00 -0x028D,0x00 -0x028E,0x00 -0x028F,0x00 -0x0290,0x00 -0x0291,0x00 -0x0292,0x3F -0x0293,0x2F -0x0294,0x80 -0x0296,0x00 -0x0297,0x00 -0x0299,0x00 -0x029D,0x00 -0x029E,0x00 -0x029F,0x00 -0x02A9,0x00 -0x02AA,0x00 -0x02AB,0x00 -0x02B7,0xFF -0x02BC,0x00 -0x0302,0x00 -0x0303,0x00 -0x0304,0x00 -0x0305,0xD4 -0x0306,0x19 -0x0307,0x00 -0x0308,0x00 -0x0309,0x00 -0x030A,0x00 -0x030B,0xC8 -0x030C,0x00 -0x030D,0x00 -0x030E,0x00 -0x030F,0x00 -0x0310,0xD4 -0x0311,0x19 -0x0312,0x00 -0x0313,0x00 -0x0314,0x00 -0x0315,0x00 -0x0316,0xC8 -0x0317,0x00 -0x0318,0x00 -0x0319,0x00 -0x031A,0x00 -0x031B,0x00 -0x031C,0x00 -0x031D,0x00 -0x031E,0x00 -0x031F,0x00 -0x0320,0x00 -0x0321,0x00 -0x0322,0x00 -0x0323,0x00 -0x0324,0x00 -0x0325,0x00 -0x0326,0x00 -0x0327,0x00 -0x0328,0x00 -0x0329,0x00 -0x032A,0x00 -0x032B,0x00 -0x032C,0x00 -0x032D,0x00 -0x032E,0x00 -0x032F,0x00 -0x0330,0x00 -0x0331,0x00 -0x0332,0x00 -0x0333,0x00 -0x0334,0x00 -0x0335,0x00 -0x0336,0x00 -0x0337,0x00 -0x0338,0x00 -0x0339,0x1C -0x033B,0x55 -0x033C,0x2B -0x033D,0x00 -0x033E,0x00 -0x033F,0x00 -0x0340,0x00 -0x0341,0x55 -0x0342,0x2B -0x0343,0x00 -0x0344,0x00 -0x0345,0x00 -0x0346,0x00 -0x0347,0x00 -0x0348,0x00 -0x0349,0x00 -0x034A,0x00 -0x034B,0x00 -0x034C,0x00 -0x034D,0x00 -0x034E,0x00 -0x034F,0x00 -0x0350,0x00 -0x0351,0x00 -0x0352,0x00 -0x0353,0x00 -0x0354,0x00 -0x0355,0x00 -0x0356,0x00 -0x0357,0x00 -0x0358,0x00 -0x0359,0x00 -0x035A,0x00 -0x035B,0x00 -0x035C,0x00 -0x035D,0x00 -0x035E,0x00 -0x035F,0x00 -0x0360,0x00 -0x0361,0x00 -0x0362,0x00 -0x0487,0x00 -0x0508,0x00 -0x0509,0x00 -0x050A,0x00 -0x050B,0x00 -0x050C,0x00 -0x050D,0x00 -0x050E,0x00 -0x050F,0x00 -0x0510,0x00 -0x0511,0x00 -0x0512,0x00 -0x0513,0x00 -0x0515,0x00 -0x0516,0x00 -0x0517,0x00 -0x0518,0x00 -0x0519,0x00 -0x051A,0x00 -0x051B,0x00 -0x051C,0x00 -0x051D,0x00 -0x051E,0x00 -0x051F,0x00 -0x0521,0x2B -0x052A,0x01 -0x052B,0x01 -0x052C,0x0F -0x052D,0x03 -0x052E,0x00 -0x052F,0x00 -0x0531,0x00 -0x0532,0x00 -0x0533,0x04 -0x0534,0x00 -0x0535,0x01 -0x0536,0x04 -0x0537,0x00 -0x0538,0x00 -0x0539,0x00 -0x053D,0x0A -0x053E,0x06 -0x0588,0x00 -0x0589,0x0C -0x058A,0x00 -0x058B,0x00 -0x058C,0x00 -0x058D,0x00 -0x059B,0x18 -0x059C,0x0C -0x059D,0x00 -0x059E,0x00 -0x059F,0x00 -0x05A0,0x00 -0x05A1,0x00 -0x05A2,0x00 -0x05A4,0x20 -0x05A5,0x00 -0x05A6,0x00 -0x05AC,0x00 -0x05AD,0x00 -0x05AE,0x00 -0x05B1,0x00 -0x05B2,0x00 -0x0802,0x35 -0x0803,0x05 -0x0804,0x01 -0x0805,0x00 -0x0806,0x00 -0x0807,0x00 -0x0808,0x00 -0x0809,0x00 -0x080A,0x00 -0x080B,0x00 -0x080C,0x00 -0x080D,0x00 -0x080E,0x00 -0x080F,0x00 -0x0810,0x00 -0x0811,0x00 -0x0812,0x00 -0x0813,0x00 -0x0814,0x00 -0x0815,0x00 -0x0816,0x00 -0x0817,0x00 -0x0818,0x00 -0x0819,0x00 -0x081A,0x00 -0x081B,0x00 -0x081C,0x00 -0x081D,0x00 -0x081E,0x00 -0x081F,0x00 -0x0820,0x00 -0x0821,0x00 -0x0822,0x00 -0x0823,0x00 -0x0824,0x00 -0x0825,0x00 -0x0826,0x00 -0x0827,0x00 -0x0828,0x00 -0x0829,0x00 -0x082A,0x00 -0x082B,0x00 -0x082C,0x00 -0x082D,0x00 -0x082E,0x00 -0x082F,0x00 -0x0830,0x00 -0x0831,0x00 -0x0832,0x00 -0x0833,0x00 -0x0834,0x00 -0x0835,0x00 -0x0836,0x00 -0x0837,0x00 -0x0838,0x00 -0x0839,0x00 -0x083A,0x00 -0x083B,0x00 -0x083C,0x00 -0x083D,0x00 -0x083E,0x00 -0x083F,0x00 -0x0840,0x00 -0x0841,0x00 -0x0842,0x00 -0x0843,0x00 -0x0844,0x00 -0x0845,0x00 -0x0846,0x00 -0x0847,0x00 -0x0848,0x00 -0x0849,0x00 -0x084A,0x00 -0x084B,0x00 -0x084C,0x00 -0x084D,0x00 -0x084E,0x00 -0x084F,0x00 -0x0850,0x00 -0x0851,0x00 -0x0852,0x00 -0x0853,0x00 -0x0854,0x00 -0x0855,0x00 -0x0856,0x00 -0x0857,0x00 -0x0858,0x00 -0x0859,0x00 -0x085A,0x00 -0x085B,0x00 -0x085C,0x00 -0x085D,0x00 -0x085E,0x00 -0x085F,0x00 -0x0860,0x00 -0x0861,0x00 -0x090E,0x02 -0x0943,0x01 -0x0949,0x00 -0x094A,0x00 -0x094E,0x49 -0x094F,0xF2 -0x095E,0x00 -0x0A02,0x00 -0x0A03,0x03 -0x0A04,0x00 -0x0A05,0x03 -0x0A14,0x00 -0x0A1A,0x00 -0x0A20,0x00 -0x0A26,0x00 -0x0A2C,0x00 -0x0A38,0x00 -0x0A39,0x00 -0x0A3A,0x00 -0x0A3C,0x00 -0x0A3D,0x00 -0x0A3E,0x00 -0x0A40,0x00 -0x0A41,0x00 -0x0A42,0x00 -0x0A44,0x00 -0x0A45,0x00 -0x0A46,0x00 -0x0A48,0x00 -0x0A49,0x00 -0x0A4A,0x00 -0x0A4C,0x00 -0x0A4D,0x00 -0x0A4E,0x00 -0x0A4F,0x00 -0x0A50,0x00 -0x0A51,0x00 -0x0A52,0x00 -0x0A53,0x00 -0x0A54,0x00 -0x0A55,0x00 -0x0A56,0x00 -0x0A57,0x00 -0x0A58,0x00 -0x0A59,0x00 -0x0A5A,0x00 -0x0A5B,0x00 -0x0A5C,0x00 -0x0A5D,0x00 -0x0A5E,0x00 -0x0A5F,0x00 -0x0B44,0x0F -0x0B46,0x00 -0x0B47,0x0F -0x0B48,0x0F -0x0B4A,0x1C -0x0B57,0x0E -0x0B58,0x01 -0x0C02,0x03 -0x0C03,0x00 -0x0C07,0x00 -0x0C08,0x00 -# End configuration registers -# -# Start configuration postamble -0x0514,0x01 -0x001C,0x01 -0x0540,0x00 -0x0B24,0xC3 -0x0B25,0x02 -# End configuration postamble diff --git a/bittide/data/clock_configs/Si5395J-200MHz-100ppb.slabtimeproj b/bittide/data/clock_configs/Si5395J-200MHz-100ppb.slabtimeproj deleted file mode 100644 index c5ae033d2d0ce1ef13b9b456cc994eeacc551c93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18376 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 diff --git a/bittide/data/clock_configs/Si5395J-200MHz-10ppb-Registers.csv b/bittide/data/clock_configs/Si5395J-200MHz-10ppb-Registers.csv deleted file mode 100644 index 439e2f1b5..000000000 --- a/bittide/data/clock_configs/Si5395J-200MHz-10ppb-Registers.csv +++ /dev/null @@ -1,622 +0,0 @@ -# Si534x/7x/8x/9x Registers Script -# -# Part: Si5395 -# Project File: \\VBOXSVR\win_vm_shared\Si5395J-200MHz-10ppb.slabtimeproj -# Design ID: 200_10b -# Includes Pre/Post Download Control Register Writes: Yes -# Device Revision: A -# Creator: ClockBuilder Pro v4.11.0.1 [2023-09-14] -# Created On: 2024-04-10 14:30:05 GMT+02:00 -Address,Data -# -# Start configuration preamble -0x0B24,0xC0 -0x0B25,0x00 -0x0540,0x01 -# End configuration preamble -# -# Delay 300 msec -# Delay is worst case time for device to complete any calibration -# that is running due to device state change previous to this script -# being processed. -# -# Start configuration registers -0x0006,0x00 -0x0007,0x00 -0x0008,0x00 -0x000B,0x68 -0x0016,0x02 -0x0017,0xDC -0x0018,0xFF -0x0019,0xFF -0x001A,0xFF -0x0023,0xFF -0x0024,0x0F -0x0025,0x00 -0x0026,0x00 -0x0027,0x00 -0x0028,0x00 -0x002B,0x02 -0x002C,0x00 -0x002D,0x00 -0x002E,0x00 -0x002F,0x00 -0x0030,0x00 -0x0031,0x00 -0x0032,0x00 -0x0033,0x00 -0x0034,0x00 -0x0035,0x00 -0x0036,0x00 -0x0037,0x00 -0x0038,0x00 -0x0039,0x00 -0x003A,0x00 -0x003B,0x00 -0x003C,0x00 -0x003D,0x00 -0x003E,0x00 -0x003F,0x00 -0x0040,0x04 -0x0041,0x00 -0x0042,0x00 -0x0043,0x00 -0x0044,0x00 -0x0045,0x0C -0x0046,0x00 -0x0047,0x00 -0x0048,0x00 -0x0049,0x00 -0x004A,0x00 -0x004B,0x00 -0x004C,0x00 -0x004D,0x00 -0x004E,0x00 -0x004F,0x00 -0x0050,0x0F -0x0051,0x00 -0x0052,0x00 -0x0053,0x00 -0x0054,0x00 -0x0055,0x00 -0x0056,0x00 -0x0057,0x00 -0x0058,0x00 -0x0059,0x00 -0x005A,0x00 -0x005B,0x00 -0x005C,0x00 -0x005D,0x00 -0x005E,0x00 -0x005F,0x00 -0x0060,0x00 -0x0061,0x00 -0x0062,0x00 -0x0063,0x00 -0x0064,0x00 -0x0065,0x00 -0x0066,0x00 -0x0067,0x00 -0x0068,0x00 -0x0069,0x00 -0x0092,0x00 -0x0093,0x00 -0x0095,0x00 -0x0096,0x00 -0x0098,0x00 -0x009A,0x00 -0x009B,0x00 -0x009D,0x00 -0x009E,0x00 -0x00A0,0x00 -0x00A2,0x00 -0x00A9,0x00 -0x00AA,0x00 -0x00AB,0x00 -0x00AC,0x00 -0x00E5,0x01 -0x00EA,0x00 -0x00EB,0x00 -0x00EC,0x00 -0x00ED,0x00 -0x0102,0x01 -0x0103,0x06 -0x0104,0x09 -0x0105,0x3E -0x0106,0x18 -0x0108,0x01 -0x0109,0x09 -0x010A,0x3B -0x010B,0x28 -0x010D,0x01 -0x010E,0x09 -0x010F,0x3B -0x0110,0x28 -0x0112,0x01 -0x0113,0x09 -0x0114,0x3B -0x0115,0x28 -0x0117,0x01 -0x0118,0x09 -0x0119,0x3B -0x011A,0x28 -0x011C,0x01 -0x011D,0x09 -0x011E,0x3B -0x011F,0x28 -0x0121,0x01 -0x0122,0x09 -0x0123,0x3B -0x0124,0x28 -0x0126,0x01 -0x0127,0x09 -0x0128,0x3B -0x0129,0x28 -0x012B,0x01 -0x012C,0x09 -0x012D,0x3B -0x012E,0x28 -0x0130,0x01 -0x0131,0x09 -0x0132,0x3B -0x0133,0x28 -0x0135,0x02 -0x0136,0x09 -0x0137,0x3E -0x0138,0x19 -0x013A,0x06 -0x013B,0x09 -0x013C,0x3E -0x013D,0x19 -0x013F,0x00 -0x0140,0x00 -0x0141,0x40 -0x0142,0xFF -0x0206,0x00 -0x0208,0x00 -0x0209,0x00 -0x020A,0x00 -0x020B,0x00 -0x020C,0x00 -0x020D,0x00 -0x020E,0x00 -0x020F,0x00 -0x0210,0x00 -0x0211,0x00 -0x0212,0x00 -0x0213,0x00 -0x0214,0x00 -0x0215,0x00 -0x0216,0x00 -0x0217,0x00 -0x0218,0x00 -0x0219,0x00 -0x021A,0x00 -0x021B,0x00 -0x021C,0x00 -0x021D,0x00 -0x021E,0x00 -0x021F,0x00 -0x0220,0x00 -0x0221,0x00 -0x0222,0x00 -0x0223,0x00 -0x0224,0x00 -0x0225,0x00 -0x0226,0x00 -0x0227,0x00 -0x0228,0x00 -0x0229,0x00 -0x022A,0x00 -0x022B,0x00 -0x022C,0x00 -0x022D,0x00 -0x022E,0x00 -0x022F,0x00 -0x0231,0x0B -0x0232,0x0B -0x0233,0x0B -0x0234,0x0B -0x0235,0x00 -0x0236,0x00 -0x0237,0x00 -0x0238,0xC0 -0x0239,0x89 -0x023A,0x00 -0x023B,0x00 -0x023C,0x00 -0x023D,0x00 -0x023E,0x80 -0x0247,0x00 -0x0248,0x00 -0x0249,0x00 -0x024A,0x00 -0x024B,0x00 -0x024C,0x00 -0x024D,0x00 -0x024E,0x00 -0x024F,0x00 -0x0250,0x00 -0x0251,0x00 -0x0252,0x00 -0x0253,0x00 -0x0254,0x00 -0x0255,0x00 -0x0256,0x00 -0x0257,0x00 -0x0258,0x00 -0x0259,0x00 -0x025A,0x00 -0x025B,0x00 -0x025C,0x00 -0x025D,0x00 -0x025E,0x00 -0x025F,0x00 -0x0260,0x00 -0x0261,0x00 -0x0262,0x00 -0x0263,0x00 -0x0264,0x00 -0x0265,0x0F -0x0266,0x00 -0x0267,0x00 -0x0268,0x00 -0x0269,0x00 -0x026A,0x00 -0x026B,0x32 -0x026C,0x30 -0x026D,0x30 -0x026E,0x5F -0x026F,0x31 -0x0270,0x30 -0x0271,0x62 -0x0272,0x00 -0x028A,0x00 -0x028B,0x00 -0x028C,0x00 -0x028D,0x00 -0x028E,0x00 -0x028F,0x00 -0x0290,0x00 -0x0291,0x00 -0x0292,0x3F -0x0293,0x2F -0x0294,0x80 -0x0296,0x00 -0x0297,0x00 -0x0299,0x00 -0x029D,0x00 -0x029E,0x00 -0x029F,0x00 -0x02A9,0x00 -0x02AA,0x00 -0x02AB,0x00 -0x02B7,0xFF -0x02BC,0x00 -0x0302,0x00 -0x0303,0x00 -0x0304,0x00 -0x0305,0xD4 -0x0306,0x19 -0x0307,0x00 -0x0308,0x00 -0x0309,0x00 -0x030A,0x00 -0x030B,0xC8 -0x030C,0x00 -0x030D,0x00 -0x030E,0x00 -0x030F,0x00 -0x0310,0xD4 -0x0311,0x19 -0x0312,0x00 -0x0313,0x00 -0x0314,0x00 -0x0315,0x00 -0x0316,0xC8 -0x0317,0x00 -0x0318,0x00 -0x0319,0x00 -0x031A,0x00 -0x031B,0x00 -0x031C,0x00 -0x031D,0x00 -0x031E,0x00 -0x031F,0x00 -0x0320,0x00 -0x0321,0x00 -0x0322,0x00 -0x0323,0x00 -0x0324,0x00 -0x0325,0x00 -0x0326,0x00 -0x0327,0x00 -0x0328,0x00 -0x0329,0x00 -0x032A,0x00 -0x032B,0x00 -0x032C,0x00 -0x032D,0x00 -0x032E,0x00 -0x032F,0x00 -0x0330,0x00 -0x0331,0x00 -0x0332,0x00 -0x0333,0x00 -0x0334,0x00 -0x0335,0x00 -0x0336,0x00 -0x0337,0x00 -0x0338,0x00 -0x0339,0x1C -0x033B,0x55 -0x033C,0x04 -0x033D,0x00 -0x033E,0x00 -0x033F,0x00 -0x0340,0x00 -0x0341,0x55 -0x0342,0x04 -0x0343,0x00 -0x0344,0x00 -0x0345,0x00 -0x0346,0x00 -0x0347,0x00 -0x0348,0x00 -0x0349,0x00 -0x034A,0x00 -0x034B,0x00 -0x034C,0x00 -0x034D,0x00 -0x034E,0x00 -0x034F,0x00 -0x0350,0x00 -0x0351,0x00 -0x0352,0x00 -0x0353,0x00 -0x0354,0x00 -0x0355,0x00 -0x0356,0x00 -0x0357,0x00 -0x0358,0x00 -0x0359,0x00 -0x035A,0x00 -0x035B,0x00 -0x035C,0x00 -0x035D,0x00 -0x035E,0x00 -0x035F,0x00 -0x0360,0x00 -0x0361,0x00 -0x0362,0x00 -0x0487,0x00 -0x0508,0x00 -0x0509,0x00 -0x050A,0x00 -0x050B,0x00 -0x050C,0x00 -0x050D,0x00 -0x050E,0x00 -0x050F,0x00 -0x0510,0x00 -0x0511,0x00 -0x0512,0x00 -0x0513,0x00 -0x0515,0x00 -0x0516,0x00 -0x0517,0x00 -0x0518,0x00 -0x0519,0x00 -0x051A,0x00 -0x051B,0x00 -0x051C,0x00 -0x051D,0x00 -0x051E,0x00 -0x051F,0x00 -0x0521,0x2B -0x052A,0x01 -0x052B,0x01 -0x052C,0x0F -0x052D,0x03 -0x052E,0x00 -0x052F,0x00 -0x0531,0x00 -0x0532,0x00 -0x0533,0x04 -0x0534,0x00 -0x0535,0x01 -0x0536,0x04 -0x0537,0x00 -0x0538,0x00 -0x0539,0x00 -0x053D,0x0A -0x053E,0x06 -0x0588,0x00 -0x0589,0x0C -0x058A,0x00 -0x058B,0x00 -0x058C,0x00 -0x058D,0x00 -0x059B,0x18 -0x059C,0x0C -0x059D,0x00 -0x059E,0x00 -0x059F,0x00 -0x05A0,0x00 -0x05A1,0x00 -0x05A2,0x00 -0x05A4,0x20 -0x05A5,0x00 -0x05A6,0x00 -0x05AC,0x00 -0x05AD,0x00 -0x05AE,0x00 -0x05B1,0x00 -0x05B2,0x00 -0x0802,0x35 -0x0803,0x05 -0x0804,0x01 -0x0805,0x00 -0x0806,0x00 -0x0807,0x00 -0x0808,0x00 -0x0809,0x00 -0x080A,0x00 -0x080B,0x00 -0x080C,0x00 -0x080D,0x00 -0x080E,0x00 -0x080F,0x00 -0x0810,0x00 -0x0811,0x00 -0x0812,0x00 -0x0813,0x00 -0x0814,0x00 -0x0815,0x00 -0x0816,0x00 -0x0817,0x00 -0x0818,0x00 -0x0819,0x00 -0x081A,0x00 -0x081B,0x00 -0x081C,0x00 -0x081D,0x00 -0x081E,0x00 -0x081F,0x00 -0x0820,0x00 -0x0821,0x00 -0x0822,0x00 -0x0823,0x00 -0x0824,0x00 -0x0825,0x00 -0x0826,0x00 -0x0827,0x00 -0x0828,0x00 -0x0829,0x00 -0x082A,0x00 -0x082B,0x00 -0x082C,0x00 -0x082D,0x00 -0x082E,0x00 -0x082F,0x00 -0x0830,0x00 -0x0831,0x00 -0x0832,0x00 -0x0833,0x00 -0x0834,0x00 -0x0835,0x00 -0x0836,0x00 -0x0837,0x00 -0x0838,0x00 -0x0839,0x00 -0x083A,0x00 -0x083B,0x00 -0x083C,0x00 -0x083D,0x00 -0x083E,0x00 -0x083F,0x00 -0x0840,0x00 -0x0841,0x00 -0x0842,0x00 -0x0843,0x00 -0x0844,0x00 -0x0845,0x00 -0x0846,0x00 -0x0847,0x00 -0x0848,0x00 -0x0849,0x00 -0x084A,0x00 -0x084B,0x00 -0x084C,0x00 -0x084D,0x00 -0x084E,0x00 -0x084F,0x00 -0x0850,0x00 -0x0851,0x00 -0x0852,0x00 -0x0853,0x00 -0x0854,0x00 -0x0855,0x00 -0x0856,0x00 -0x0857,0x00 -0x0858,0x00 -0x0859,0x00 -0x085A,0x00 -0x085B,0x00 -0x085C,0x00 -0x085D,0x00 -0x085E,0x00 -0x085F,0x00 -0x0860,0x00 -0x0861,0x00 -0x090E,0x02 -0x0943,0x01 -0x0949,0x00 -0x094A,0x00 -0x094E,0x49 -0x094F,0xF2 -0x095E,0x00 -0x0A02,0x00 -0x0A03,0x03 -0x0A04,0x00 -0x0A05,0x03 -0x0A14,0x00 -0x0A1A,0x00 -0x0A20,0x00 -0x0A26,0x00 -0x0A2C,0x00 -0x0A38,0x00 -0x0A39,0x00 -0x0A3A,0x00 -0x0A3C,0x00 -0x0A3D,0x00 -0x0A3E,0x00 -0x0A40,0x00 -0x0A41,0x00 -0x0A42,0x00 -0x0A44,0x00 -0x0A45,0x00 -0x0A46,0x00 -0x0A48,0x00 -0x0A49,0x00 -0x0A4A,0x00 -0x0A4C,0x00 -0x0A4D,0x00 -0x0A4E,0x00 -0x0A4F,0x00 -0x0A50,0x00 -0x0A51,0x00 -0x0A52,0x00 -0x0A53,0x00 -0x0A54,0x00 -0x0A55,0x00 -0x0A56,0x00 -0x0A57,0x00 -0x0A58,0x00 -0x0A59,0x00 -0x0A5A,0x00 -0x0A5B,0x00 -0x0A5C,0x00 -0x0A5D,0x00 -0x0A5E,0x00 -0x0A5F,0x00 -0x0B44,0x0F -0x0B46,0x00 -0x0B47,0x0F -0x0B48,0x0F -0x0B4A,0x1C -0x0B57,0x0E -0x0B58,0x01 -0x0C02,0x03 -0x0C03,0x00 -0x0C07,0x00 -0x0C08,0x00 -# End configuration registers -# -# Start configuration postamble -0x0514,0x01 -0x001C,0x01 -0x0540,0x00 -0x0B24,0xC3 -0x0B25,0x02 -# End configuration postamble diff --git a/bittide/data/clock_configs/Si5395J-200MHz-10ppb-and-out1.csv b/bittide/data/clock_configs/Si5395J-200MHz-10ppb-and-out1.csv deleted file mode 100644 index 349c82e41..000000000 --- a/bittide/data/clock_configs/Si5395J-200MHz-10ppb-and-out1.csv +++ /dev/null @@ -1,616 +0,0 @@ -# Si534x/7x/8x/9x Registers Script -# -# Part: Si5395 -# Project File: C:\Users\User\Desktop\clock configs\Si5395-200MHz-10ppb-and-out1.slabtimeproj -# Design ID: 200_100b -# Includes Pre/Post Download Control Register Writes: Yes -# Die Revision: C0 -# Creator: ClockBuilder Pro v4.7 [2022-11-18] -# Created On: 2024-09-11 08:09:56 GMT-07:00 -Address,Data -# -# Start configuration preamble -0x0B24,0xC0 -0x0B25,0x00 -0x0540,0x01 -# End configuration preamble -# -# Delay 300 msec -# Delay is worst case time for device to complete any calibration -# that is running due to device state change previous to this script -# being processed. -# -# Start configuration registers -0x0006,0x00 -0x0007,0x00 -0x0008,0x00 -0x000B,0x68 -0x0016,0x02 -0x0017,0xDC -0x0018,0xFF -0x0019,0xFF -0x001A,0xFF -0x002B,0x02 -0x002C,0x00 -0x002D,0x00 -0x002E,0x00 -0x002F,0x00 -0x0030,0x00 -0x0031,0x00 -0x0032,0x00 -0x0033,0x00 -0x0034,0x00 -0x0035,0x00 -0x0036,0x00 -0x0037,0x00 -0x0038,0x00 -0x0039,0x00 -0x003A,0x00 -0x003B,0x00 -0x003C,0x00 -0x003D,0x00 -0x003E,0x00 -0x003F,0x00 -0x0040,0x04 -0x0041,0x00 -0x0042,0x00 -0x0043,0x00 -0x0044,0x00 -0x0045,0x0C -0x0046,0x00 -0x0047,0x00 -0x0048,0x00 -0x0049,0x00 -0x004A,0x00 -0x004B,0x00 -0x004C,0x00 -0x004D,0x00 -0x004E,0x00 -0x004F,0x00 -0x0050,0x0F -0x0051,0x00 -0x0052,0x00 -0x0053,0x00 -0x0054,0x00 -0x0055,0x00 -0x0056,0x00 -0x0057,0x00 -0x0058,0x00 -0x0059,0x00 -0x005A,0x00 -0x005B,0x00 -0x005C,0x00 -0x005D,0x00 -0x005E,0x00 -0x005F,0x00 -0x0060,0x00 -0x0061,0x00 -0x0062,0x00 -0x0063,0x00 -0x0064,0x00 -0x0065,0x00 -0x0066,0x00 -0x0067,0x00 -0x0068,0x00 -0x0069,0x00 -0x0092,0x00 -0x0093,0x00 -0x0095,0x00 -0x0096,0x00 -0x0098,0x00 -0x009A,0x00 -0x009B,0x00 -0x009D,0x00 -0x009E,0x00 -0x00A0,0x00 -0x00A2,0x00 -0x00A9,0x00 -0x00AA,0x00 -0x00AB,0x00 -0x00AC,0x00 -0x00E5,0x01 -0x00EA,0x00 -0x00EB,0x00 -0x00EC,0x00 -0x00ED,0x00 -0x0102,0x01 -0x0103,0x02 -0x0104,0x09 -0x0105,0x3E -0x0106,0x18 -0x0108,0x01 -0x0109,0x09 -0x010A,0x3E -0x010B,0x18 -0x010D,0x02 -0x010E,0x09 -0x010F,0x3E -0x0110,0x18 -0x0112,0x01 -0x0113,0x09 -0x0114,0x3B -0x0115,0x28 -0x0117,0x01 -0x0118,0x09 -0x0119,0x3B -0x011A,0x28 -0x011C,0x01 -0x011D,0x09 -0x011E,0x3B -0x011F,0x28 -0x0121,0x01 -0x0122,0x09 -0x0123,0x3B -0x0124,0x28 -0x0126,0x01 -0x0127,0x09 -0x0128,0x3B -0x0129,0x28 -0x012B,0x01 -0x012C,0x09 -0x012D,0x3B -0x012E,0x28 -0x0130,0x01 -0x0131,0x09 -0x0132,0x3B -0x0133,0x28 -0x0135,0x02 -0x0136,0x09 -0x0137,0x3E -0x0138,0x18 -0x013A,0x02 -0x013B,0x09 -0x013C,0x3E -0x013D,0x18 -0x013F,0x00 -0x0140,0x00 -0x0141,0x40 -0x0142,0xFF -0x0206,0x00 -0x0208,0x00 -0x0209,0x00 -0x020A,0x00 -0x020B,0x00 -0x020C,0x00 -0x020D,0x00 -0x020E,0x00 -0x020F,0x00 -0x0210,0x00 -0x0211,0x00 -0x0212,0x00 -0x0213,0x00 -0x0214,0x00 -0x0215,0x00 -0x0216,0x00 -0x0217,0x00 -0x0218,0x00 -0x0219,0x00 -0x021A,0x00 -0x021B,0x00 -0x021C,0x00 -0x021D,0x00 -0x021E,0x00 -0x021F,0x00 -0x0220,0x00 -0x0221,0x00 -0x0222,0x00 -0x0223,0x00 -0x0224,0x00 -0x0225,0x00 -0x0226,0x00 -0x0227,0x00 -0x0228,0x00 -0x0229,0x00 -0x022A,0x00 -0x022B,0x00 -0x022C,0x00 -0x022D,0x00 -0x022E,0x00 -0x022F,0x00 -0x0231,0x0B -0x0232,0x0B -0x0233,0x0B -0x0234,0x0B -0x0235,0x00 -0x0236,0x00 -0x0237,0x00 -0x0238,0xC0 -0x0239,0x89 -0x023A,0x00 -0x023B,0x00 -0x023C,0x00 -0x023D,0x00 -0x023E,0x80 -0x0247,0x01 -0x0248,0x00 -0x0249,0x00 -0x024A,0x00 -0x024B,0x00 -0x024C,0x00 -0x024D,0x01 -0x024E,0x00 -0x024F,0x00 -0x0250,0x00 -0x0251,0x00 -0x0252,0x00 -0x0253,0x00 -0x0254,0x00 -0x0255,0x00 -0x0256,0x00 -0x0257,0x00 -0x0258,0x00 -0x0259,0x00 -0x025A,0x00 -0x025B,0x00 -0x025C,0x00 -0x025D,0x00 -0x025E,0x00 -0x025F,0x00 -0x0260,0x00 -0x0261,0x00 -0x0262,0x00 -0x0263,0x00 -0x0264,0x00 -0x0265,0x13 -0x0266,0x00 -0x0267,0x00 -0x0268,0x01 -0x0269,0x00 -0x026A,0x00 -0x026B,0x32 -0x026C,0x30 -0x026D,0x30 -0x026E,0x5F -0x026F,0x31 -0x0270,0x30 -0x0271,0x30 -0x0272,0x62 -0x028A,0x00 -0x028B,0x00 -0x028C,0x00 -0x028D,0x00 -0x028E,0x00 -0x028F,0x00 -0x0290,0x00 -0x0291,0x00 -0x0292,0x3F -0x0293,0x2F -0x0294,0x80 -0x0296,0x00 -0x0297,0x00 -0x0299,0x00 -0x029D,0x00 -0x029E,0x00 -0x029F,0x00 -0x02A9,0x00 -0x02AA,0x00 -0x02AB,0x00 -0x02B7,0xFF -0x02BC,0x00 -0x0302,0x00 -0x0303,0x00 -0x0304,0x00 -0x0305,0xEA -0x0306,0x0C -0x0307,0x00 -0x0308,0x00 -0x0309,0x00 -0x030A,0x00 -0x030B,0xC8 -0x030C,0x00 -0x030D,0x00 -0x030E,0x00 -0x030F,0x00 -0x0310,0x00 -0x0311,0x00 -0x0312,0x00 -0x0313,0x00 -0x0314,0x00 -0x0315,0x00 -0x0316,0x00 -0x0317,0x00 -0x0318,0x00 -0x0319,0x00 -0x031A,0x00 -0x031B,0x00 -0x031C,0x00 -0x031D,0x00 -0x031E,0x00 -0x031F,0x00 -0x0320,0x00 -0x0321,0x00 -0x0322,0x00 -0x0323,0x00 -0x0324,0x00 -0x0325,0x00 -0x0326,0x00 -0x0327,0x00 -0x0328,0x00 -0x0329,0x00 -0x032A,0x00 -0x032B,0x00 -0x032C,0x00 -0x032D,0x00 -0x032E,0x00 -0x032F,0x00 -0x0330,0x00 -0x0331,0x00 -0x0332,0x00 -0x0333,0x00 -0x0334,0x00 -0x0335,0x00 -0x0336,0x00 -0x0337,0x00 -0x0338,0x00 -0x0339,0x1E -0x033B,0x2B -0x033C,0x02 -0x033D,0x00 -0x033E,0x00 -0x033F,0x00 -0x0340,0x00 -0x0341,0x00 -0x0342,0x00 -0x0343,0x00 -0x0344,0x00 -0x0345,0x00 -0x0346,0x00 -0x0347,0x00 -0x0348,0x00 -0x0349,0x00 -0x034A,0x00 -0x034B,0x00 -0x034C,0x00 -0x034D,0x00 -0x034E,0x00 -0x034F,0x00 -0x0350,0x00 -0x0351,0x00 -0x0352,0x00 -0x0353,0x00 -0x0354,0x00 -0x0355,0x00 -0x0356,0x00 -0x0357,0x00 -0x0358,0x00 -0x0359,0x00 -0x035A,0x00 -0x035B,0x00 -0x035C,0x00 -0x035D,0x00 -0x035E,0x00 -0x035F,0x00 -0x0360,0x00 -0x0361,0x00 -0x0362,0x00 -0x0487,0x00 -0x0508,0x00 -0x0509,0x00 -0x050A,0x00 -0x050B,0x00 -0x050C,0x00 -0x050D,0x00 -0x050E,0x00 -0x050F,0x00 -0x0510,0x00 -0x0511,0x00 -0x0512,0x00 -0x0513,0x00 -0x0515,0x00 -0x0516,0x00 -0x0517,0x00 -0x0518,0x00 -0x0519,0x00 -0x051A,0x00 -0x051B,0x00 -0x051C,0x00 -0x051D,0x00 -0x051E,0x00 -0x051F,0x00 -0x0521,0x2B -0x052A,0x01 -0x052B,0x01 -0x052C,0x0F -0x052D,0x03 -0x052E,0x00 -0x052F,0x00 -0x0531,0x00 -0x0532,0x00 -0x0533,0x04 -0x0534,0x00 -0x0535,0x01 -0x0536,0x04 -0x0537,0x00 -0x0538,0x00 -0x0539,0x00 -0x053D,0x0A -0x053E,0x06 -0x0588,0x00 -0x0589,0x0C -0x058A,0x00 -0x058B,0x00 -0x058C,0x00 -0x058D,0x00 -0x059B,0x18 -0x059C,0x0C -0x059D,0x00 -0x059E,0x00 -0x059F,0x00 -0x05A0,0x00 -0x05A1,0x00 -0x05A2,0x00 -0x05A4,0x20 -0x05A5,0x00 -0x05A6,0x00 -0x05AC,0x00 -0x05AD,0x00 -0x05AE,0x00 -0x05B1,0x00 -0x05B2,0x00 -0x0802,0x35 -0x0803,0x05 -0x0804,0x01 -0x0805,0x00 -0x0806,0x00 -0x0807,0x00 -0x0808,0x00 -0x0809,0x00 -0x080A,0x00 -0x080B,0x00 -0x080C,0x00 -0x080D,0x00 -0x080E,0x00 -0x080F,0x00 -0x0810,0x00 -0x0811,0x00 -0x0812,0x00 -0x0813,0x00 -0x0814,0x00 -0x0815,0x00 -0x0816,0x00 -0x0817,0x00 -0x0818,0x00 -0x0819,0x00 -0x081A,0x00 -0x081B,0x00 -0x081C,0x00 -0x081D,0x00 -0x081E,0x00 -0x081F,0x00 -0x0820,0x00 -0x0821,0x00 -0x0822,0x00 -0x0823,0x00 -0x0824,0x00 -0x0825,0x00 -0x0826,0x00 -0x0827,0x00 -0x0828,0x00 -0x0829,0x00 -0x082A,0x00 -0x082B,0x00 -0x082C,0x00 -0x082D,0x00 -0x082E,0x00 -0x082F,0x00 -0x0830,0x00 -0x0831,0x00 -0x0832,0x00 -0x0833,0x00 -0x0834,0x00 -0x0835,0x00 -0x0836,0x00 -0x0837,0x00 -0x0838,0x00 -0x0839,0x00 -0x083A,0x00 -0x083B,0x00 -0x083C,0x00 -0x083D,0x00 -0x083E,0x00 -0x083F,0x00 -0x0840,0x00 -0x0841,0x00 -0x0842,0x00 -0x0843,0x00 -0x0844,0x00 -0x0845,0x00 -0x0846,0x00 -0x0847,0x00 -0x0848,0x00 -0x0849,0x00 -0x084A,0x00 -0x084B,0x00 -0x084C,0x00 -0x084D,0x00 -0x084E,0x00 -0x084F,0x00 -0x0850,0x00 -0x0851,0x00 -0x0852,0x00 -0x0853,0x00 -0x0854,0x00 -0x0855,0x00 -0x0856,0x00 -0x0857,0x00 -0x0858,0x00 -0x0859,0x00 -0x085A,0x00 -0x085B,0x00 -0x085C,0x00 -0x085D,0x00 -0x085E,0x00 -0x085F,0x00 -0x0860,0x00 -0x0861,0x00 -0x090E,0x02 -0x0943,0x01 -0x0949,0x00 -0x094A,0x00 -0x094E,0x49 -0x094F,0xF2 -0x095E,0x00 -0x0A02,0x00 -0x0A03,0x01 -0x0A04,0x00 -0x0A05,0x01 -0x0A14,0x00 -0x0A1A,0x00 -0x0A20,0x00 -0x0A26,0x00 -0x0A2C,0x00 -0x0A38,0x00 -0x0A39,0x00 -0x0A3A,0x00 -0x0A3C,0x00 -0x0A3D,0x00 -0x0A3E,0x00 -0x0A40,0x00 -0x0A41,0x00 -0x0A42,0x00 -0x0A44,0x00 -0x0A45,0x00 -0x0A46,0x00 -0x0A48,0x00 -0x0A49,0x00 -0x0A4A,0x00 -0x0A4C,0x00 -0x0A4D,0x00 -0x0A4E,0x00 -0x0A4F,0x00 -0x0A50,0x00 -0x0A51,0x00 -0x0A52,0x00 -0x0A53,0x00 -0x0A54,0x00 -0x0A55,0x00 -0x0A56,0x00 -0x0A57,0x00 -0x0A58,0x00 -0x0A59,0x00 -0x0A5A,0x00 -0x0A5B,0x00 -0x0A5C,0x00 -0x0A5D,0x00 -0x0A5E,0x00 -0x0A5F,0x00 -0x0B44,0x0F -0x0B46,0x00 -0x0B47,0x0F -0x0B48,0x0F -0x0B4A,0x1E -0x0B57,0x0E -0x0B58,0x01 -0x0C02,0x03 -0x0C03,0x00 -0x0C07,0x00 -0x0C08,0x00 -# End configuration registers -# -# Start configuration postamble -0x0514,0x01 -0x001C,0x01 -0x0540,0x00 -0x0B24,0xC3 -0x0B25,0x02 -# End configuration postamble diff --git a/bittide/data/clock_configs/Si5395J-200MHz-10ppb-and-out1.slabtimeproj b/bittide/data/clock_configs/Si5395J-200MHz-10ppb-and-out1.slabtimeproj deleted file mode 100644 index 2e09430eec2adec3ee3a251598569bb6821aed34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12368 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 Cj!B&W diff --git a/bittide/data/clock_configs/Si5395J-200MHz-10ppb.slabtimeproj b/bittide/data/clock_configs/Si5395J-200MHz-10ppb.slabtimeproj deleted file mode 100644 index dec031ef0565f626e2e53b1a19a9ebd07fa4ba3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18376 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 diff --git a/bittide/data/clock_configs/Si5395J-200MHz-1ppb-Registers.csv b/bittide/data/clock_configs/Si5395J-200MHz-1ppb-Registers.csv deleted file mode 100644 index 978d242b9..000000000 --- a/bittide/data/clock_configs/Si5395J-200MHz-1ppb-Registers.csv +++ /dev/null @@ -1,622 +0,0 @@ -# Si534x/7x/8x/9x Registers Script -# -# Part: Si5395 -# Project File: C:\Users\User\Desktop\Si5395J-200MHz-1ppb.slabtimeproj -# Design ID: 200_1ppb -# Includes Pre/Post Download Control Register Writes: Yes -# Device Revision: A -# Creator: ClockBuilder Pro v4.11.0.1 [2023-09-14] -# Created On: 2024-03-22 15:01:30 GMT+01:00 -Address,Data -# -# Start configuration preamble -0x0B24,0xC0 -0x0B25,0x00 -0x0540,0x01 -# End configuration preamble -# -# Delay 300 msec -# Delay is worst case time for device to complete any calibration -# that is running due to device state change previous to this script -# being processed. -# -# Start configuration registers -0x0006,0x00 -0x0007,0x00 -0x0008,0x00 -0x000B,0x68 -0x0016,0x02 -0x0017,0xDC -0x0018,0xFF -0x0019,0xFF -0x001A,0xFF -0x0023,0xFF -0x0024,0x0F -0x0025,0x00 -0x0026,0x00 -0x0027,0x00 -0x0028,0x00 -0x002B,0x02 -0x002C,0x00 -0x002D,0x00 -0x002E,0x00 -0x002F,0x00 -0x0030,0x00 -0x0031,0x00 -0x0032,0x00 -0x0033,0x00 -0x0034,0x00 -0x0035,0x00 -0x0036,0x00 -0x0037,0x00 -0x0038,0x00 -0x0039,0x00 -0x003A,0x00 -0x003B,0x00 -0x003C,0x00 -0x003D,0x00 -0x003E,0x00 -0x003F,0x00 -0x0040,0x04 -0x0041,0x00 -0x0042,0x00 -0x0043,0x00 -0x0044,0x00 -0x0045,0x0C -0x0046,0x00 -0x0047,0x00 -0x0048,0x00 -0x0049,0x00 -0x004A,0x00 -0x004B,0x00 -0x004C,0x00 -0x004D,0x00 -0x004E,0x00 -0x004F,0x00 -0x0050,0x0F -0x0051,0x00 -0x0052,0x00 -0x0053,0x00 -0x0054,0x00 -0x0055,0x00 -0x0056,0x00 -0x0057,0x00 -0x0058,0x00 -0x0059,0x00 -0x005A,0x00 -0x005B,0x00 -0x005C,0x00 -0x005D,0x00 -0x005E,0x00 -0x005F,0x00 -0x0060,0x00 -0x0061,0x00 -0x0062,0x00 -0x0063,0x00 -0x0064,0x00 -0x0065,0x00 -0x0066,0x00 -0x0067,0x00 -0x0068,0x00 -0x0069,0x00 -0x0092,0x00 -0x0093,0x00 -0x0095,0x00 -0x0096,0x00 -0x0098,0x00 -0x009A,0x00 -0x009B,0x00 -0x009D,0x00 -0x009E,0x00 -0x00A0,0x00 -0x00A2,0x00 -0x00A9,0x00 -0x00AA,0x00 -0x00AB,0x00 -0x00AC,0x00 -0x00E5,0x01 -0x00EA,0x00 -0x00EB,0x00 -0x00EC,0x00 -0x00ED,0x00 -0x0102,0x01 -0x0103,0x06 -0x0104,0x09 -0x0105,0x3E -0x0106,0x18 -0x0108,0x01 -0x0109,0x09 -0x010A,0x3B -0x010B,0x28 -0x010D,0x01 -0x010E,0x09 -0x010F,0x3B -0x0110,0x28 -0x0112,0x01 -0x0113,0x09 -0x0114,0x3B -0x0115,0x28 -0x0117,0x01 -0x0118,0x09 -0x0119,0x3B -0x011A,0x28 -0x011C,0x01 -0x011D,0x09 -0x011E,0x3B -0x011F,0x28 -0x0121,0x01 -0x0122,0x09 -0x0123,0x3B -0x0124,0x28 -0x0126,0x01 -0x0127,0x09 -0x0128,0x3B -0x0129,0x28 -0x012B,0x01 -0x012C,0x09 -0x012D,0x3B -0x012E,0x28 -0x0130,0x01 -0x0131,0x09 -0x0132,0x3B -0x0133,0x28 -0x0135,0x02 -0x0136,0x09 -0x0137,0x3E -0x0138,0x19 -0x013A,0x06 -0x013B,0x09 -0x013C,0x3E -0x013D,0x19 -0x013F,0x00 -0x0140,0x00 -0x0141,0x40 -0x0142,0xFF -0x0206,0x00 -0x0208,0x00 -0x0209,0x00 -0x020A,0x00 -0x020B,0x00 -0x020C,0x00 -0x020D,0x00 -0x020E,0x00 -0x020F,0x00 -0x0210,0x00 -0x0211,0x00 -0x0212,0x00 -0x0213,0x00 -0x0214,0x00 -0x0215,0x00 -0x0216,0x00 -0x0217,0x00 -0x0218,0x00 -0x0219,0x00 -0x021A,0x00 -0x021B,0x00 -0x021C,0x00 -0x021D,0x00 -0x021E,0x00 -0x021F,0x00 -0x0220,0x00 -0x0221,0x00 -0x0222,0x00 -0x0223,0x00 -0x0224,0x00 -0x0225,0x00 -0x0226,0x00 -0x0227,0x00 -0x0228,0x00 -0x0229,0x00 -0x022A,0x00 -0x022B,0x00 -0x022C,0x00 -0x022D,0x00 -0x022E,0x00 -0x022F,0x00 -0x0231,0x0B -0x0232,0x0B -0x0233,0x0B -0x0234,0x0B -0x0235,0x00 -0x0236,0x00 -0x0237,0x00 -0x0238,0xC0 -0x0239,0x89 -0x023A,0x00 -0x023B,0x00 -0x023C,0x00 -0x023D,0x00 -0x023E,0x80 -0x0247,0x00 -0x0248,0x00 -0x0249,0x00 -0x024A,0x00 -0x024B,0x00 -0x024C,0x00 -0x024D,0x00 -0x024E,0x00 -0x024F,0x00 -0x0250,0x00 -0x0251,0x00 -0x0252,0x00 -0x0253,0x00 -0x0254,0x00 -0x0255,0x00 -0x0256,0x00 -0x0257,0x00 -0x0258,0x00 -0x0259,0x00 -0x025A,0x00 -0x025B,0x00 -0x025C,0x00 -0x025D,0x00 -0x025E,0x00 -0x025F,0x00 -0x0260,0x00 -0x0261,0x00 -0x0262,0x00 -0x0263,0x00 -0x0264,0x00 -0x0265,0x0F -0x0266,0x00 -0x0267,0x00 -0x0268,0x00 -0x0269,0x00 -0x026A,0x00 -0x026B,0x32 -0x026C,0x30 -0x026D,0x30 -0x026E,0x5F -0x026F,0x31 -0x0270,0x70 -0x0271,0x70 -0x0272,0x62 -0x028A,0x00 -0x028B,0x00 -0x028C,0x00 -0x028D,0x00 -0x028E,0x00 -0x028F,0x00 -0x0290,0x00 -0x0291,0x00 -0x0292,0x3F -0x0293,0x2F -0x0294,0x80 -0x0296,0x00 -0x0297,0x00 -0x0299,0x00 -0x029D,0x00 -0x029E,0x00 -0x029F,0x00 -0x02A9,0x00 -0x02AA,0x00 -0x02AB,0x00 -0x02B7,0xFF -0x02BC,0x00 -0x0302,0x00 -0x0303,0x00 -0x0304,0x00 -0x0305,0xD4 -0x0306,0x19 -0x0307,0x00 -0x0308,0x00 -0x0309,0x00 -0x030A,0x00 -0x030B,0xC8 -0x030C,0x00 -0x030D,0x00 -0x030E,0x00 -0x030F,0x00 -0x0310,0xD4 -0x0311,0x19 -0x0312,0x00 -0x0313,0x00 -0x0314,0x00 -0x0315,0x00 -0x0316,0xC8 -0x0317,0x00 -0x0318,0x00 -0x0319,0x00 -0x031A,0x00 -0x031B,0x00 -0x031C,0x00 -0x031D,0x00 -0x031E,0x00 -0x031F,0x00 -0x0320,0x00 -0x0321,0x00 -0x0322,0x00 -0x0323,0x00 -0x0324,0x00 -0x0325,0x00 -0x0326,0x00 -0x0327,0x00 -0x0328,0x00 -0x0329,0x00 -0x032A,0x00 -0x032B,0x00 -0x032C,0x00 -0x032D,0x00 -0x032E,0x00 -0x032F,0x00 -0x0330,0x00 -0x0331,0x00 -0x0332,0x00 -0x0333,0x00 -0x0334,0x00 -0x0335,0x00 -0x0336,0x00 -0x0337,0x00 -0x0338,0x00 -0x0339,0x1C -0x033B,0x6F -0x033C,0x00 -0x033D,0x00 -0x033E,0x00 -0x033F,0x00 -0x0340,0x00 -0x0341,0x6F -0x0342,0x00 -0x0343,0x00 -0x0344,0x00 -0x0345,0x00 -0x0346,0x00 -0x0347,0x00 -0x0348,0x00 -0x0349,0x00 -0x034A,0x00 -0x034B,0x00 -0x034C,0x00 -0x034D,0x00 -0x034E,0x00 -0x034F,0x00 -0x0350,0x00 -0x0351,0x00 -0x0352,0x00 -0x0353,0x00 -0x0354,0x00 -0x0355,0x00 -0x0356,0x00 -0x0357,0x00 -0x0358,0x00 -0x0359,0x00 -0x035A,0x00 -0x035B,0x00 -0x035C,0x00 -0x035D,0x00 -0x035E,0x00 -0x035F,0x00 -0x0360,0x00 -0x0361,0x00 -0x0362,0x00 -0x0487,0x00 -0x0508,0x00 -0x0509,0x00 -0x050A,0x00 -0x050B,0x00 -0x050C,0x00 -0x050D,0x00 -0x050E,0x00 -0x050F,0x00 -0x0510,0x00 -0x0511,0x00 -0x0512,0x00 -0x0513,0x00 -0x0515,0x00 -0x0516,0x00 -0x0517,0x00 -0x0518,0x00 -0x0519,0x00 -0x051A,0x00 -0x051B,0x00 -0x051C,0x00 -0x051D,0x00 -0x051E,0x00 -0x051F,0x00 -0x0521,0x2B -0x052A,0x01 -0x052B,0x01 -0x052C,0x0F -0x052D,0x03 -0x052E,0x00 -0x052F,0x00 -0x0531,0x00 -0x0532,0x00 -0x0533,0x04 -0x0534,0x00 -0x0535,0x01 -0x0536,0x04 -0x0537,0x00 -0x0538,0x00 -0x0539,0x00 -0x053D,0x0A -0x053E,0x06 -0x0588,0x00 -0x0589,0x0C -0x058A,0x00 -0x058B,0x00 -0x058C,0x00 -0x058D,0x00 -0x059B,0x18 -0x059C,0x0C -0x059D,0x00 -0x059E,0x00 -0x059F,0x00 -0x05A0,0x00 -0x05A1,0x00 -0x05A2,0x00 -0x05A4,0x20 -0x05A5,0x00 -0x05A6,0x00 -0x05AC,0x00 -0x05AD,0x00 -0x05AE,0x00 -0x05B1,0x00 -0x05B2,0x00 -0x0802,0x35 -0x0803,0x05 -0x0804,0x01 -0x0805,0x00 -0x0806,0x00 -0x0807,0x00 -0x0808,0x00 -0x0809,0x00 -0x080A,0x00 -0x080B,0x00 -0x080C,0x00 -0x080D,0x00 -0x080E,0x00 -0x080F,0x00 -0x0810,0x00 -0x0811,0x00 -0x0812,0x00 -0x0813,0x00 -0x0814,0x00 -0x0815,0x00 -0x0816,0x00 -0x0817,0x00 -0x0818,0x00 -0x0819,0x00 -0x081A,0x00 -0x081B,0x00 -0x081C,0x00 -0x081D,0x00 -0x081E,0x00 -0x081F,0x00 -0x0820,0x00 -0x0821,0x00 -0x0822,0x00 -0x0823,0x00 -0x0824,0x00 -0x0825,0x00 -0x0826,0x00 -0x0827,0x00 -0x0828,0x00 -0x0829,0x00 -0x082A,0x00 -0x082B,0x00 -0x082C,0x00 -0x082D,0x00 -0x082E,0x00 -0x082F,0x00 -0x0830,0x00 -0x0831,0x00 -0x0832,0x00 -0x0833,0x00 -0x0834,0x00 -0x0835,0x00 -0x0836,0x00 -0x0837,0x00 -0x0838,0x00 -0x0839,0x00 -0x083A,0x00 -0x083B,0x00 -0x083C,0x00 -0x083D,0x00 -0x083E,0x00 -0x083F,0x00 -0x0840,0x00 -0x0841,0x00 -0x0842,0x00 -0x0843,0x00 -0x0844,0x00 -0x0845,0x00 -0x0846,0x00 -0x0847,0x00 -0x0848,0x00 -0x0849,0x00 -0x084A,0x00 -0x084B,0x00 -0x084C,0x00 -0x084D,0x00 -0x084E,0x00 -0x084F,0x00 -0x0850,0x00 -0x0851,0x00 -0x0852,0x00 -0x0853,0x00 -0x0854,0x00 -0x0855,0x00 -0x0856,0x00 -0x0857,0x00 -0x0858,0x00 -0x0859,0x00 -0x085A,0x00 -0x085B,0x00 -0x085C,0x00 -0x085D,0x00 -0x085E,0x00 -0x085F,0x00 -0x0860,0x00 -0x0861,0x00 -0x090E,0x02 -0x0943,0x01 -0x0949,0x00 -0x094A,0x00 -0x094E,0x49 -0x094F,0xF2 -0x095E,0x00 -0x0A02,0x00 -0x0A03,0x03 -0x0A04,0x00 -0x0A05,0x03 -0x0A14,0x00 -0x0A1A,0x00 -0x0A20,0x00 -0x0A26,0x00 -0x0A2C,0x00 -0x0A38,0x00 -0x0A39,0x00 -0x0A3A,0x00 -0x0A3C,0x00 -0x0A3D,0x00 -0x0A3E,0x00 -0x0A40,0x00 -0x0A41,0x00 -0x0A42,0x00 -0x0A44,0x00 -0x0A45,0x00 -0x0A46,0x00 -0x0A48,0x00 -0x0A49,0x00 -0x0A4A,0x00 -0x0A4C,0x00 -0x0A4D,0x00 -0x0A4E,0x00 -0x0A4F,0x00 -0x0A50,0x00 -0x0A51,0x00 -0x0A52,0x00 -0x0A53,0x00 -0x0A54,0x00 -0x0A55,0x00 -0x0A56,0x00 -0x0A57,0x00 -0x0A58,0x00 -0x0A59,0x00 -0x0A5A,0x00 -0x0A5B,0x00 -0x0A5C,0x00 -0x0A5D,0x00 -0x0A5E,0x00 -0x0A5F,0x00 -0x0B44,0x0F -0x0B46,0x00 -0x0B47,0x0F -0x0B48,0x0F -0x0B4A,0x1C -0x0B57,0x0E -0x0B58,0x01 -0x0C02,0x03 -0x0C03,0x00 -0x0C07,0x00 -0x0C08,0x00 -# End configuration registers -# -# Start configuration postamble -0x0514,0x01 -0x001C,0x01 -0x0540,0x00 -0x0B24,0xC3 -0x0B25,0x02 -# End configuration postamble diff --git a/bittide/data/clock_configs/Si5395J-200MHz-1ppb.slabtimeproj b/bittide/data/clock_configs/Si5395J-200MHz-1ppb.slabtimeproj deleted file mode 100644 index dabe5205b6339e4cb7369c58d9f1373f3e688a93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18392 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 diff --git a/bittide/linuwial-wrap-types.css b/bittide/linuwial-wrap-types.css deleted file mode 100644 index 9657cf233..000000000 --- a/bittide/linuwial-wrap-types.css +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-FileCopyrightText: 2022 Google LLC -/* -/* SPDX-License-Identifier: Apache-2.0 */ - -@import "linuwial.css"; - -/* Re-enable wordwrapping in (parts of) type signatures */ -#interface td.src { - white-space: normal !important; -} -#interface dfn.src { - white-space: normal !important; -} diff --git a/bittide/proofs/TypeNatProofs.agda b/bittide/proofs/TypeNatProofs.agda deleted file mode 100644 index faee03636..000000000 --- a/bittide/proofs/TypeNatProofs.agda +++ /dev/null @@ -1,267 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - --- Proofs of the type level properties claimed by --- 'Data.Constraint.Nat.Extra'. Checked with agda-2.6.4.1 and --- agda-stdlib-1.2.0. - -open import Data.Bool.Base using (T) -open import Agda.Builtin.Bool using (true; false) -open import Agda.Builtin.Unit using (tt) -open import Data.Empty using (⊥-elim) -open import Data.Nat.Base -open import Data.Nat.DivMod -open import Data.Nat.Logarithm -open import Data.Nat.Properties -open import Function.Base using (_∘_) -open import Relation.Binary.PropositionalEquality -open import Function.Identity.Effectful using (Identity) -open import Relation.Nullary.Negation using (contradiction) - -+-distrib-/-req : (n m : ℕ) .{{_ : NonZero m}} -> m % m + n % m < m -+-distrib-/-req n (suc m) = s≤s - let open ≤-Reasoning in begin - suc m % suc m + n % suc m - ≡⟨ cong (_+ (n % suc m)) (n%n≡0 (suc m)) ⟩ - n % suc m - ≤⟨ s≤s⁻¹ (m%n n ≤ (n + pred m) / m * m -timesDivRU 0 m = z≤n -timesDivRU (suc n) m = - let open ≤-Reasoning in begin - suc n - ≡⟨ cong suc (m≡m%n+[m/n]*n n m) ⟩ - suc (n % m) + n / m * m - ≤⟨ +-monoˡ-≤ (n / m * m) (m%n ⌈log₂ (n * 2) ⌉ ≡ ⌈log₂ n ⌉ + 1 -clogProductRule n - rewrite +-comm ⌈log₂ n ⌉ 1 - rewrite *-comm n 2 - = ⌈log₂2*n⌉≡1+⌈log₂n⌉ n - -DivRU : (n d : ℕ) .{{_ : NonZero d}} -> ℕ -DivRU n d = (n + pred d) / d - -cancelMulDiv : (n m : ℕ) .{{_ : NonZero m}} -> DivRU (n * m) m ≡ n -cancelMulDiv n (suc m) = - let open ≡-Reasoning in begin - (n * suc m + m) / suc m - ≡⟨ +-distrib-/ (n * suc m) m (lem₁ n m) ⟩ - n * suc m / suc m + m / suc m - ≡⟨ cong ((n * suc m / _) +_) (m (n * suc m) % suc m + m % suc m < suc m - lem₁ n m = s≤s - let open ≤-Reasoning in begin - (n * suc m) % suc m + m % suc m - ≡⟨ cong ((n * suc m) % suc m +_) (m - k ≤ pred m -> (n * m + k) / m ≡ n -divWithRemainder n (suc m) k k≤m-1 = - let open ≡-Reasoning in begin - (n * suc m + k) / suc m - ≡⟨ +-distrib-/ (n * suc m) k (lem₁ n m k≤m-1) ⟩ - n * suc m / suc m + k / suc m - ≡⟨ cong (n * suc m / suc m +_) (m k ≤ m -> n * suc m % suc m + k % suc m < suc m - lem₁ n m k≤m = s≤s - let open ≤-Reasoning in begin - n * suc m % suc m + k % suc m - ≡⟨ cong (n * suc m % suc m +_) (m≤n⇒m%n≡m k≤m) ⟩ - n * suc m % suc m + k - ≡⟨ cong (_+ k) ([m+kn]%n≡m%n 0 n (suc m))⟩ - k - ≤⟨ k≤m ⟩ - m - ∎ - -leMaxLeft : (n m k : ℕ) -> n ≤ ((n + m) ⊔ k) -leMaxLeft 0 _ _ = z≤n -leMaxLeft (suc n) m 0 = s≤s (m≤m+n n m) -leMaxLeft (suc n) m (suc k) = s≤s (leMaxLeft n m k) - -lessThanMax : (n m k : ℕ) -> n ≤ m -> n ≤ k -> n ≤ m ⊔ k -lessThanMax n m k n≤m n≤k - rewrite (⊔≡⊔′ m k) - with m <ᵇ k -... | false = n≤m -... | true = n≤k - -leMaxRight : (n m k : ℕ) -> n ≤ (m ⊔ (n + k)) -leMaxRight 0 m k = z≤n -leMaxRight (suc n) 0 k = s≤s (m≤m+n n k) -leMaxRight (suc n) (suc m) k = s≤s (leMaxRight n m k) - -strictlyPositiveDivRu : - (n m : ℕ) .{{_ : NonZero n}} .{{_ : NonZero m}} -> - 1 ≤ DivRU n m -strictlyPositiveDivRu (suc n) m = - let open ≤-Reasoning in begin - 1 - ≤⟨ s≤s z≤n ⟩ - suc (n / m) - ≡⟨ cong (_+ n / m) (sym (n/n≡1 m)) ⟩ - m / m + n / m - ≡⟨ sym (+-distrib-/ m n (+-distrib-/-req n m)) ⟩ - (m + n) / m - ≡⟨ cong (_/ _) (cong (_+ _) (sym (suc-pred m)) ) ⟩ - (suc (pred m) + n) / m - ≡⟨ cong (_/ _) (cong suc (+-comm (pred m) n)) ⟩ - suc (n + pred m) / m - ∎ - -euclid3 : (n m k : ℕ) -> n + m ≤ k -> n ≤ k ∸ m -euclid3 n m 0 n+m≤0 - with n+m≡0 <- n≤0⇒n≡0 n+m≤0 - with n≡0 <- m+n≡0⇒m≡0 n n+m≡0 - rewrite n≡0 - = z≤n -euclid3 n 0 (suc k) n+m≤1+k - rewrite +-comm n 0 - = n+m≤1+k -euclid3 n (suc m) (suc k) n+m≤1+k - rewrite +-comm n (suc m) - rewrite +-comm m n - = euclid3 n m k (s≤s⁻¹ n+m≤1+k) - -oneLeCLog2n : (n : ℕ) -> 2 ≤ n -> 1 ≤ ⌈log₂ n ⌉ -oneLeCLog2n 0 2≤0 = contradiction n≮0 λ p -> p 2≤0 -oneLeCLog2n 1 2≤1 = contradiction n≮0 λ p -> p (s≤s⁻¹ 2≤1) -oneLeCLog2n (2+ n) _ = s≤s z≤n - -useLowerLimit : - (n m k : ℕ) .{{_ : NonZero m }} -> - n + m ≤ k -> 1 + n ≤ k -useLowerLimit n (suc m) k n+m≤k = ≤-trans 1+n≤n+m n+m≤k - where - 1+n≤n+m : {n m : ℕ} -> suc n ≤ n + suc m - 1+n≤n+m {n} {m} = - let open ≤-Reasoning in begin - suc n - ≡⟨ +-comm 0 (suc n) ⟩ - suc n + 0 - ≤⟨ +-monoʳ-≤ (suc n) z≤n ⟩ - suc n + m - ≡⟨ cong suc (+-comm n m) ⟩ - suc (m + n) - ≡⟨ +-comm (suc m) n ⟩ - n + suc m - ∎ - -OneMore : ℕ -> ℕ -OneMore 0 = 0 -OneMore (suc _) = 1 - -isOne : - (n m : ℕ) .{{_ : NonZero n}} .{{_ : NonZero m}} -> - n ≤ m -> n / m + OneMore (n % m) ≡ 1 -isOne n m n≤m - with n ≤ᵇ m in n≤ᵇm? -... | false = ⊥-elim (subst Identity (cong T n≤ᵇm?) (≤⇒≤ᵇ n≤m)) -... | true - with n ≡ᵇ m in n≡ᵇm? -... | true - rewrite ≡ᵇ⇒≡ n m (subst T (sym n≡ᵇm?) tt) - rewrite cong (_+ OneMore (m % m)) (n/n≡1 m) - rewrite cong suc (cong OneMore (n%n≡0 m)) - = refl -... | false - with n≢m <- subst T (n≡ᵇm?) ∘ ≡⇒≡ᵇ n m - with n - 1 ≤ n / m + OneMore (n % m) -oneMore n m - with n ≤ᵇ m in n≤ᵇm? -... | true - rewrite isOne n m (≤ᵇ⇒≤ n m (subst T (sym n≤ᵇm?) tt)) - = s≤s z≤n -... | false - with n≰m <- subst T (n≤ᵇm?) ∘ ≤⇒≤ᵇ - rewrite m/n≡1+[m∸n]/n {n} {m} (≰⇒≥ n≰m) - = s≤s z≤n - -SatSubZero : ℕ -> ℕ -> ℕ -SatSubZero 0 _ = 0 -SatSubZero n m - with n ≤ᵇ m -... | true = 0 -... | false = n ∸ m - -satSubZeroMin : (n m : ℕ) -> SatSubZero n m + n ⊓ m ≡ n -satSubZeroMin 0 m = refl -satSubZeroMin (suc n) 0 = cong suc (+-comm n 0) -satSubZeroMin (suc n) (suc m) - rewrite +-comm (SatSubZero n m) (n ⊓ m) - rewrite +-comm (n ⊓ m) (SatSubZero (suc n) (suc m)) - rewrite ⊓≡⊓′ (suc n) (suc m) - with n <ᵇ m in n<ᵇm? | n <ᵇ suc m in n<ᵇ1+m? -... | false | false - rewrite +-comm (n ∸ m) (suc m) - = cong suc (m+[n∸m]≡n {m} {n} (≮⇒≥ (subst T n<ᵇm? ∘ <⇒<ᵇ))) -... | false | true - with n≤m <- s≤s⁻¹ (<ᵇ⇒< n (suc m) (subst T (sym n<ᵇ1+m?) tt)) - = cong suc (sym (≤∧≮⇒≡ n≤m (subst T n<ᵇm? ∘ <⇒<ᵇ))) -... | true | false - with n≤m <- <⇒≤ (<ᵇ⇒< n m (subst T (sym n<ᵇm?) tt)) - = ⊥-elim (subst T n<ᵇ1+m? (<⇒<ᵇ (s≤s n≤m))) -... | true | true - = refl - -minLeq : (n m : ℕ) -> n ⊓ m ≤ m -minLeq n m = m⊓n≤n n m - -maxGeqPlus : (n m k : ℕ) -> n ≤ (n ⊔ m) + k -maxGeqPlus n m 0 - rewrite +-comm (n ⊔ m) 0 - = m≤m⊔n n m -maxGeqPlus n m (suc k) - rewrite +-comm (n ⊔ m) (suc k) - rewrite +-comm k (n ⊔ m) - = m≤n⇒m≤1+n (maxGeqPlus n m k) diff --git a/bittide/src/Bittide/Arithmetic/PartsPer.hs b/bittide/src/Bittide/Arithmetic/PartsPer.hs deleted file mode 100644 index 017b6bc52..000000000 --- a/bittide/src/Bittide/Arithmetic/PartsPer.hs +++ /dev/null @@ -1,110 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE NumericUnderscores #-} - -{- | A more-or-less ad-hoc collection of definitions to work with measuring -clock frequency differences without loss-of-precision. --} -module Bittide.Arithmetic.PartsPer where - -import Bittide.Arithmetic.Time (PeriodToCycles) -import Clash.Prelude hiding (PeriodToCycles) -import Data.Aeson (FromJSON (..), ToJSON (..)) -import Data.Data (Proxy (..)) - -{- $setup ->>> import Clash.Prelude --} - -{- | A relative measure of frequency. E.g., a 'PartsPer' of @1 ppm@ of some ideal -frequency means that for every million ticks in an ideal frequency, there are -a million and one ticks in the observed frequency. - -It is internally represented as a 64-bit signed integer, representing -_parts per trillion_. --} -newtype PartsPer = Ppt (Signed 64) - deriving (Show, Eq, Ord) - deriving newtype (Num) - -instance ToJSON PartsPer where - toJSON (Ppt x) = toJSON (toInteger x) - -instance FromJSON PartsPer where - parseJSON = fmap (Ppt . fromInteger) . parseJSON - -ppt :: Signed 64 -> PartsPer -ppt = Ppt . (* 1) - -ppb :: Signed 64 -> PartsPer -ppb = Ppt . (* 1_000) - -ppm :: Signed 64 -> PartsPer -ppm = Ppt . (* 1_000_000) - -toPpt :: PartsPer -> Float -toPpt = toSteps (ppt 1) - -toPpb :: PartsPer -> Float -toPpb = toSteps (ppb 1) - -toPpm :: PartsPer -> Float -toPpm = toSteps (ppm 1) - -{- | Convert a 'PartsPer' to a 'Float'. Useful for pretty printing 'PartsPer' to -a human-readable step size (usually, 1 ppm). Has no representation in hardware. - ->>> toSteps (ppb 1) (ppm 3) -3000.0 ->>> toSteps (ppm 1) (cyclesToPartsPer (125_000_000 :: Integer) 125_000_879) -7.032 --} -toSteps :: - -- | Step size - PartsPer -> - -- | Value - PartsPer -> - -- | Number of steps to get to the value - Float -toSteps (Ppt stepSize) (Ppt value) = fromIntegral value / fromIntegral stepSize - -{- | Given an ideal number of clock cycles - passed indirectly using a combination -of a domain and a measurement period - and an observed number of clock cycles, -calculate the difference in 'PartsPer'. Has no representation in hardware. --} -cyclesToPartsPerI :: - forall dom ps a. - (KnownDomain dom, KnownNat ps, Integral a) => - -- | Clock domain. Proxy for an ideal frequency. - Proxy dom -> - -- | Period observed for in picoseconds (see 'Seconds', 'Milliseconds', etc.). E.g., - -- if you want to pass in 1 millisecond, you would pass in: - -- - -- > cyclesToPartsPerI (Proxy @dom) (Proxy @(Milliseconds 1)) - Proxy ps -> - -- | Observed number of clock cycles - a -> - PartsPer -cyclesToPartsPerI _ _ = cyclesToPartsPer (natToNum @(PeriodToCycles dom ps)) - -{- | Given an ideal number of clock cycles and an observed number of clock cycles, -calculate the difference in 'PartsPer'. Has no representation in hardware. - ->>> cyclesToPartsPer 125_000_000 125_000_250 -Ppt 2000000 ->>> toPpm (cyclesToPartsPer 125_000_000 125_000_250) -2.0 --} -cyclesToPartsPer :: - (Integral a) => - -- | Ideal number of clock cycles - a -> - -- | Observed number of clock cycles - a -> - PartsPer -cyclesToPartsPer (toInteger -> ideal) (toInteger -> observed) = - Ppt (fromInteger (((observed * factor) `div` ideal) - factor)) - where - factor = 1_000_000_000_000 diff --git a/bittide/src/Bittide/Arithmetic/Time.hs b/bittide/src/Bittide/Arithmetic/Time.hs deleted file mode 100644 index 3da2955f4..000000000 --- a/bittide/src/Bittide/Arithmetic/Time.hs +++ /dev/null @@ -1,153 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE TemplateHaskell #-} -{-# OPTIONS_GHC -Wno-orphans #-} - -module Bittide.Arithmetic.Time where - -import Clash.Explicit.Prelude hiding (PeriodToCycles, natVal) -import GHC.Stack (HasCallStack) - -import Clash.Class.Counter (Counter, countSucc) -import Clash.Signal.Internal (Femtoseconds (Femtoseconds), mapFemtoseconds) -import Data.Data (Proxy (..)) -import Data.Int (Int64) -import Data.Kind (Type) - -import GHC.TypeLits.KnownNat (KnownNat1 (..), SNatKn (..), nameToSymbol) -import GHC.TypeNats (natVal) - -{- | XXX: We currently retain this in favor of @clash-prelude@s 'PeriodToCycles' -until @1 <= DomainPeriod dom@ is trivially true. Related issue: -https://github.com/clash-lang/ghc-typelits-extra/issues/56 - -Number of clock cycles required at the clock frequency of @dom@ before a minimum @period@ has passed. -Is always at least one. --} -type PeriodToCycles dom period = Max 1 (DivRU period (Max 1 (DomainPeriod dom))) - --- Make ghc-typelits-knownnat look through time related type aliases. --- https://github.com/clash-lang/ghc-typelits-knownnat/issues/53 -instance (KnownNat ps) => KnownNat1 $(nameToSymbol ''Picoseconds) ps where - natSing1 = SNatKn (natVal (Proxy @ps)) - {-# NOINLINE natSing1 #-} - -instance (KnownNat ps) => KnownNat1 $(nameToSymbol ''Nanoseconds) ps where - natSing1 = SNatKn (natVal (Proxy @(1_000 * ps))) - {-# NOINLINE natSing1 #-} - -instance (KnownNat ps) => KnownNat1 $(nameToSymbol ''Microseconds) ps where - natSing1 = SNatKn (natVal (Proxy @(1_000_000 * ps))) - {-# NOINLINE natSing1 #-} - -instance (KnownNat ps) => KnownNat1 $(nameToSymbol ''Milliseconds) ps where - natSing1 = SNatKn (natVal (Proxy @(1_000_000_000 * ps))) - {-# NOINLINE natSing1 #-} - -instance (KnownNat ps) => KnownNat1 $(nameToSymbol ''Seconds) ps where - natSing1 = SNatKn (natVal (Proxy @(1_000_000_000_000 * ps))) - {-# NOINLINE natSing1 #-} - -{- | 'Index' with its 'maxBound' corresponding to the number of cycles needed to -wait for /n/ milliseconds. --} -type IndexMs dom n = Index (PeriodToCycles dom (Milliseconds n)) - -seconds :: Int64 -> Femtoseconds -seconds s = mapFemtoseconds (* 1000) (milliseconds s) - -milliseconds :: Int64 -> Femtoseconds -milliseconds s = mapFemtoseconds (* 1000) (microseconds s) -{-# INLINE milliseconds #-} - -microseconds :: Int64 -> Femtoseconds -microseconds s = mapFemtoseconds (* 1000) (nanoseconds s) -{-# INLINE microseconds #-} - -nanoseconds :: Int64 -> Femtoseconds -nanoseconds s = mapFemtoseconds (* 1000) (picoseconds s) -{-# INLINE nanoseconds #-} - -picoseconds :: Int64 -> Femtoseconds -picoseconds s = mapFemtoseconds (* 1000) (femtoseconds s) -{-# INLINE picoseconds #-} - -femtoseconds :: Int64 -> Femtoseconds -femtoseconds = Femtoseconds -{-# INLINE femtoseconds #-} - -{- | Rises after the incoming signal has been 'True' for the specified amount of -time. Use this function if you know the time to wait for at compile time. If -not, use 'trueForSteps'. --} -trueFor :: - forall dom t. - (HasCallStack) => - (KnownDomain dom, KnownNat t) => - -- | Use the type aliases of 'Bittide.Arithmetic.Time' for time span - -- specification. - SNat t -> - Clock dom -> - Reset dom -> - Signal dom Bool -> - Signal dom Bool -trueFor _ clk rst = - moore - clk - rst - enableGen - transF - (== maxBound) - (0 :: Index (PeriodToCycles dom t)) - where - transF counter = \case - True -> satSucc SatBound counter - _ -> 0 - -{- | Rises after the incoming signal has been 'True' for the specified amount of -time given as a configurable number of steps of a given step size (i.e., wait -for @stepSize * numberOfSteps@)). Example invocation: - -> trueForSteps @(Milliseconds 1) Proxy someLimit clk rst signal - -which will wait for @someLimit@ milliseconds. Use 'trueFor' if you know the -time to wait for at compile time. If not, use 'trueForSteps'. --} -trueForSteps :: - forall (stepSize :: Nat) (dom :: Domain) (counter :: Type). - ( HasCallStack - , KnownDomain dom - , KnownNat stepSize - , NFDataX counter - , Bounded counter - , Counter counter - , Eq counter - , Num counter - ) => - -- | Step size. Use the type aliases of 'Bittide.Arithmetic.Time' for time span - -- specification. - Proxy stepSize -> - -- | Number of steps to wait for - counter -> - Clock dom -> - Reset dom -> - Signal dom Bool -> - Signal dom Bool -trueForSteps _ limit clk rst = - moore - clk - rst - enableGen - transF - ((== limit) . fst) - (0, 0 :: Index (PeriodToCycles dom stepSize)) - where - transF cntr@(ms, _) = \case - True - | ms == limit -> cntr - | otherwise -> countSucc cntr - _ -> minBound diff --git a/bittide/src/Bittide/Axi4.hs b/bittide/src/Bittide/Axi4.hs deleted file mode 100644 index 98674cb5e..000000000 --- a/bittide/src/Bittide/Axi4.hs +++ /dev/null @@ -1,786 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -module Bittide.Axi4 ( - -- * Scaling circuits - axiStreamFromByteStream, - axiStreamToByteStream, - axiPacking, - - -- * Wishbone interfaces - wbAxisRxBufferCircuit, - wbToAxiTx, - - -- * Other circuits - axiStreamPacketFifo, - ilaAxi4Stream, - rxReadMasterC, - - -- * Utility functions - combineAxi4Stream, - splitAxi4Stream, - packAxi4Stream, - eqAxi4Stream, - axiUserMap, - axiUserMapC, - isPackedTransfer, - - -- * Internal - mkKeep, -) where - -import Clash.Prelude - -import Bittide.Axi4.Internal -import Bittide.Extra.Maybe -import Bittide.SharedTypes -import Clash.Cores.Xilinx.Ila hiding (Data) -import Clash.Sized.Internal.BitVector (popCountBV) -import Data.Constraint -import Data.Constraint.Nat.Extra -import Data.Maybe -import Data.Proxy -import Protocols -import Protocols.Axi4.Stream as AS -import qualified Protocols.DfConv as DfConv -import Protocols.Wishbone as WB - -{- $setup ->>> import Clash.Prelude ->>> import Protocols.Axi4.Stream --} - -{- | An 'Axi4Stream' without gaps in the data. This means that for each transfer -the following holds: - -* For a transfer with _tlast deasserted, all _tkeep bools are set. -* For a transfer with _tlast asserted, the first /n/ bools of _tkeep are set, - where n is the number of bytes in the transfer. --} -type PackedAxi4Stream dom conf userType = Axi4Stream dom conf userType - -{-# NOINLINE axiStreamFromByteStream #-} - -{- | Transforms an 'Axi4Stream' of 1 byte wide into an 'Axi4Stream' of /n/ bytes -wide. If it encounters '_tlast' or has captured /n/ bytes, it will present -the transfer at the output. Note that if less than /n/ bytes have been -captured, but '_tlast' is set, the component will output the captured bytes -with appropriately set '_tkeep' bits. The '_tuser', _tdest' and '_tid' signals -are blindly routed to the output. This effectively means that all but the -last '_tuser', '_tdest', '_tid' are linked to a valid transfer. - -TODO: Add test that verifies throughput requirements. -TODO: Make user specify the number of bytes to capture, instead of number of - bytes minus one (@addedWidth@). --} -axiStreamFromByteStream :: - forall dom addedWidth idWidth destWidth userType. - ( HiddenClockResetEnable dom - , KnownNat addedWidth - , KnownNat idWidth - , KnownNat destWidth - , Eq userType - , NFDataX userType - , Show userType - ) => - Circuit - (Axi4Stream dom ('Axi4StreamConfig 1 idWidth destWidth) userType) - ( PackedAxi4Stream - dom - ('Axi4StreamConfig (addedWidth + 1) idWidth destWidth) - (Vec (addedWidth + 1) userType) - ) -axiStreamFromByteStream = AS.forceResetSanity |> Circuit (mealyB go Nothing) - where - go axiStored ~(input, Axi4StreamS2M{_tready = outputReady}) = - (axiNext, (Axi4StreamS2M inputReady, output)) - where - undefUser = deepErrorX "axiStreamFromByteStream: _tuser undefined" - - -- Try to append the incoming axi to the stored axi. - dropInput = maybe False (\a -> not (or (_tlast a :> _tkeep a))) input - combinedAxi = axiUserMap (uncurry (:<)) <$> combineAxi4Stream axiStored input - - -- Shift the internal axi towards HEAD by one position. - -- If the head of the pre-shifted axi has its keep bit set, shifting is done. - extendedAxi = fmap (axiUserMap (:< undefUser) . extendAxi @_ @1) axiStored - axiPreShift = combinedAxi <|> extendedAxi - axiPostShift = - snd - $ splitAxi4Stream @1 - $ fmap (axiUserMap (\v -> (head v, tail v))) axiPreShift - - -- Output the pre-shifted axi if we can not shift anymore. - shiftingDone = not dropInput && maybe False isPackedTransfer axiPreShift - capturedLast = maybe False _tlast axiPreShift - output = if shiftingDone then axiPreShift else Nothing - - -- Flow control - (axiNext, inputReady) - | dropInput = (axiStored, True) -- Drop the input - | shiftingDone && outputReady = (Nothing, isJust combinedAxi) -- valid output, accepted - | shiftingDone && not outputReady = (axiStored, False) -- valid output, not accepted - | not shiftingDone && isJust input = (axiPostShift, isJust combinedAxi) -- Shift when input - | not shiftingDone && capturedLast = (axiPostShift, False) -- Shift when captured _tlast - | otherwise = (axiStored, False) -- No input - --- TODO: Add test that verifies throughput requirements. - -{- | Transforms an Axi4 stream of /n/ bytes wide into an Axi4 stream of 1 byte -wide. It stores the incoming transfer and shifts it out one by one. The incoming -transfer is acknowledged when the last byte is acknowledged by the outgoing transfer. -The '_tuser', '_tdest' and '_tid' are blindly routed to the output. --} -axiStreamToByteStream :: - forall dom dataWidth idWidth destWidth userType. - ( HiddenClockResetEnable dom - , KnownNat dataWidth - , KnownNat idWidth - , KnownNat destWidth - , Eq userType - , NFDataX userType - , Show userType - ) => - Circuit - (PackedAxi4Stream dom ('Axi4StreamConfig dataWidth idWidth destWidth) userType) - (Axi4Stream dom ('Axi4StreamConfig 1 idWidth destWidth) userType) -axiStreamToByteStream = AS.forceResetSanity |> Circuit (mealyB go Nothing) - where - go axiStored (input, Axi4StreamS2M{_tready = outputReady}) = (axiNext, (inputReady, output)) - where - (output, axiRest) = splitAxi4Stream @1 (combineAxi4Stream axiStored Nothing) - axiNext - | isNothing output || outputReady = axiRest <|> input - | otherwise = axiStored - inputReady = Axi4StreamS2M{_tready = isNothing axiRest && (isNothing output || outputReady)} - -type EndOfPacket = Bool -type BufferFull = Bool - -data WbAxisRxBufferState bufferDepth wbBytes = WbAxisRxBufferState - { readingBuffer :: Bool - , packetLength :: Index (bufferDepth * wbBytes + 1) - , writeCounter :: Index bufferDepth - , packetComplete :: Bool - , bufferFull :: Bool - , abortPacket :: Bool - } - deriving (Generic, NFDataX, Show) - -{-# NOINLINE wbAxisRxBuffer #-} - --- TODO: Replace with PacketStream - -{- | A wishbone accessible buffer of configurable depth that can store a single Axi4Stream packet. -The wishbone interface offers access to the buffer and exposes a status register that indicates: - * If the buffer contains a packet - * If the buffer is full before, but does not contain a whole packet. - -The wishbone addressing must be 4 byte aligned and is as follows: - * 0 .. 4 * (fifoDepth - 1) = Read-only access into the buffer. - * 4 * fifoDepth = Byte count register. - * 4 * (fifoDepth + 1) = Status register - -After reading a packet, the byte count must be set to 0 and the status register must be -cleared. The incoming Axi4Stream interface contains a side channel that can be used to abort -the incoming packet. If a packet is aborted, the buffer will consume the remaining transfers -until the end of the packet is reached, after which it will reset the buffer to its initial state. --} -wbAxisRxBufferCircuit :: - forall dom wbAddrW wbBytes bufferBytes. - ( HiddenClockResetEnable dom - , KnownNat wbAddrW - , KnownNat wbBytes - , 1 <= wbBytes - , 1 <= bufferBytes - ) => - -- | Number of bytes that can be stored in the buffer. - SNat bufferBytes -> - Circuit - ( Wishbone dom 'Standard wbAddrW (Bytes wbBytes) - , Axi4Stream dom ('Axi4StreamConfig wbBytes 0 0) Bool - ) - (CSignal dom (EndOfPacket, BufferFull)) -wbAxisRxBufferCircuit bytes = - circuit $ \(wb0, axi0) -> do - axi1 <- AS.forceResetSanity -< axi0 - wb1 <- WB.forceResetSanity -< wb0 - circ0 -< (wb1, axi1) - where - circ0 = case cancelMulDiv @wbBytes @8 of - Dict -> Circuit $ \((wbM2S, axiM2S), _) -> do - let (wbS2M, axiS2M, status) = wbAxisRxBuffer bytes wbM2S axiM2S - in ((wbS2M, axiS2M), status) - -wbAxisRxBuffer :: - forall dom wbAddrW wbBytes bufferBytes. - ( HiddenClockResetEnable dom - , KnownNat wbAddrW - , KnownNat wbBytes - , 1 <= wbBytes - , 1 <= bufferBytes - ) => - -- | Minimum number of bytes that can be stored in the buffer, will be rounded up - -- to the nearest multiple of wbBytes. - SNat bufferBytes -> - -- | Wishbone master bus. - "wbM2S" ::: Signal dom (WishboneM2S wbAddrW wbBytes (Bytes wbBytes)) -> - -- | Axi4 Stream master bus. - "axisM2S" ::: Signal dom (Maybe (Axi4StreamM2S ('Axi4StreamConfig wbBytes 0 0) Bool)) -> - -- | - -- 1. Wishbone slave bus - -- 2. Axi4 Stream slave bus - -- 3. Status - "" - ::: ( "wbS2M" ::: Signal dom (WishboneS2M (Bytes wbBytes)) - , "axisS2M" ::: Signal dom Axi4StreamS2M - , "status" ::: Signal dom (EndOfPacket, BufferFull) - ) -wbAxisRxBuffer SNat = case strictlyPositiveDivRu @bufferBytes @wbBytes of - Dict -> case leMult @wbBytes @(DivRU bufferBytes wbBytes) of - Dict -> wbAxisRxBuffer# (SNat @(DivRU bufferBytes wbBytes)) - -{- | A wishbone accessible buffer of configurable depth that can store a single Axi4Stream packet. -A read transfer from the buffer takes at least two cycles to complete. - -The wishbone interface offers access to the buffer and exposes a status register that indicates: - * If the buffer contains a packet - * If the buffer is full before, but does not contain a whole packet. - -The wishbone addressing must be 4 byte aligned and is as follows: - 0 .. (bufferBytes - 1) = Read-only access into the buffer. - bufferBytes = Byte count register. - (bufferBytes + 4) = Status register - -After reading a packet, the byte count must be set to 0 and the status register must be -cleared. The incoming Axi4Stream interface contains a side channel that can be used to abort -the incoming packet. If a packet is aborted, the buffer will consume the remaining transfers -until the end of the packet is reached, after which it will reset the buffer to its initial state. --} -wbAxisRxBuffer# :: - forall dom wbAddrW wbBytes fifoDepth. - ( HiddenClockResetEnable dom - , KnownNat wbAddrW - , KnownNat wbBytes - , 1 <= wbBytes - , 1 <= fifoDepth - , 1 <= wbBytes * fifoDepth - ) => - -- | Depth of the buffer, each entry in the buffer stores `nBytes` bytes. - SNat fifoDepth -> - -- | Wishbone master bus. - "wbM2S" ::: Signal dom (WishboneM2S wbAddrW wbBytes (Bytes wbBytes)) -> - -- | Axi4 Stream master bus. - "axisM2S" ::: Signal dom (Maybe (Axi4StreamM2S ('Axi4StreamConfig wbBytes 0 0) Bool)) -> - -- | - -- 1. Wishbone slave bus - -- 2. Axi4 Stream slave bus - -- 3. Status - "" - ::: ( "wbS2M" ::: Signal dom (WishboneS2M (Bytes wbBytes)) - , "axisS2M" ::: Signal dom Axi4StreamS2M - , "status" ::: Signal dom (EndOfPacket, BufferFull) - ) -wbAxisRxBuffer# fifoDepth@SNat wbM2S axisM2S = (wbS2M, axisS2M, statusReg) - where - fifoOut = - blockRamU - NoClearOnReset - fifoDepth - (const $ errorX "wbAxisRxBuffer: reset function undefined") - bramAddr - bramWrite - (wbS2M, axisS2M, bramAddr, bramWrite, statusReg) = - mealyB go initState (wbM2S, axisM2S, fifoOut) - initState = - WbAxisRxBufferState - { readingBuffer = False - , packetLength = 0 - , writeCounter = 0 - , packetComplete = False - , bufferFull = False - , abortPacket = False - } - go :: - WbAxisRxBufferState fifoDepth wbBytes -> - ( WishboneM2S wbAddrW wbBytes (Bytes wbBytes) - , Maybe (Axi4StreamM2S ('Axi4StreamConfig wbBytes 0 0) Bool) - , Bytes wbBytes - ) -> - ( WbAxisRxBufferState fifoDepth wbBytes - , ( WishboneS2M (Bytes wbBytes) - , Axi4StreamS2M - , Index fifoDepth - , Maybe (Index fifoDepth, Bytes wbBytes) - , (EndOfPacket, BufferFull) - ) - ) - go - WbAxisRxBufferState{..} - ~(WishboneM2S{..}, maybeAxisM2S, wbData) = - (newState, output) - where - masterActive = busCycle && strobe - packetLengthAddress = maxBound - 1 - statusAddress = maxBound - internalAddress = (unpack $ resize addr) :: Index (fifoDepth + 2) - err = masterActive && (addr > resize (pack statusAddress)) - - statusBV = pack (packetComplete, bufferFull) - wbHandshake = masterActive && not err - - -- Since fetching data from the buffer introduces one cycle of latency, we need to - -- wait for the next cycle to acknowledge the read. - (readData, nextReadingBuffer, wbAcknowledge) = case (masterActive, internalAddress) of - (True, (== packetLengthAddress) -> True) -> (resize $ pack packetLength, False, wbHandshake) - (True, (== statusAddress) -> True) -> (resize statusBV, False, wbHandshake) - (True, _) -> (wbData, wbHandshake && not readingBuffer, wbHandshake && readingBuffer) - (False, _) -> (deepErrorX "undefined", False, False) - - axisReady = abortPacket || not (packetComplete || bufferFull) - axisHandshake = axisReady && isJust maybeAxisM2S - - output = - ( (emptyWishboneS2M @(Bytes wbBytes)){readData, err, acknowledge = wbAcknowledge} - , Axi4StreamS2M axisReady - , unpack . resize $ pack internalAddress - , maybeAxisM2S >>= orNothing axisHandshake . (writeCounter,) . pack . reverse . _tdata - , (packetComplete, bufferFull) - ) - - -- Next state - (nextPacketComplete, nextBufferFull) - | wbAcknowledge && writeEnable && internalAddress == statusAddress = - unpack $ resize writeData - | axisHandshake = - ( packetComplete || maybe False _tlast maybeAxisM2S - , bufferFull || writeCounter == maxBound - ) - | otherwise = unpack $ statusBV - - nextWriteCounter - | axisHandshake = satSucc SatBound writeCounter - | packetComplete || bufferFull = 0 - | otherwise = writeCounter - - popCountKeep = leToPlus @1 @wbBytes popCountBV . pack . _tkeep - bytesInStream = maybe (0 :: Index (wbBytes + 1)) popCountKeep maybeAxisM2S - - nextPacketLength - | wbAcknowledge && writeEnable && internalAddress == packetLengthAddress = - unpack $ resize writeData - | axisHandshake = - satAdd SatBound packetLength (bitCoerce $ resize bytesInStream) - | otherwise = - packetLength - - newState - | abortPacket && maybe False _tlast maybeAxisM2S = initState - | otherwise = - WbAxisRxBufferState - { readingBuffer = nextReadingBuffer - , packetLength = nextPacketLength - , writeCounter = nextWriteCounter - , packetComplete = nextPacketComplete - , bufferFull = nextBufferFull - , abortPacket = abortPacket || maybe False _tuser maybeAxisM2S - } - -data BufferState fifoDepth wbBytes - = AwaitingData - | BufferFull - | PacketComplete (Index (wbBytes * fifoDepth + 1)) - deriving (Generic, NFDataX, Show) - -data ReadStateMachine fifoDepth - = Idle - | ReadingPacketSize - | ReadingPacket (Index (fifoDepth + 1)) - | ClearingPacketLength - | ClearingStatus - deriving (Generic, NFDataX, Show) - -{- | Circuit capable of reading the wishbone interface of @wbAxisRxBuffer@ and -extracting Axi packets. Mostly useful for verification, but can be synthesized. -The internal statemachine continuously reads the satus register of the buffer, -if the buffer is full or a packet is complete, it will: - -1. Read the packet length from the buffer. -2. Read the packet from the buffer. -3. Clear the packet length. -4. Clear the status register. --} -rxReadMasterC :: - forall dom nBytes addrWidth bufferBytes. - ( HiddenClockResetEnable dom - , 1 <= bufferBytes - , 1 <= nBytes - , KnownNat addrWidth - , KnownNat nBytes - ) => - SNat bufferBytes -> - Circuit - () - ( Wishbone dom 'Standard addrWidth (Bytes nBytes) - , Axi4Stream dom ('Axi4StreamConfig nBytes 0 0) () - ) -rxReadMasterC s = case cancelMulDiv @nBytes @8 of - Dict -> fromSignals $ \(_, bwd) -> ((), rxReadMaster s bwd) - -{- | Circuit capable of reading the wishbone interface of @wbAxisRxBuffer@ and -extracting Axi packets. Mostly useful for verification, but can be synthesized. -The internal statemachine continuously reads the satus register of the buffer, -if the buffer is full or a packet is complete, it will: - -1. Read the packet length from the buffer. -2. Read the packet from the buffer. -3. Clear the packet length. -4. Clear the status register. --} -rxReadMaster :: - forall dom wbBytes addrWidth bufferBytes. - ( HiddenClockResetEnable dom - , 1 <= bufferBytes - , 1 <= wbBytes - , KnownNat addrWidth - , KnownNat wbBytes - ) => - SNat bufferBytes -> - ( Signal dom (WishboneS2M (Bytes wbBytes)) - , Signal dom Axi4StreamS2M - ) -> - ( Signal dom (WishboneM2S addrWidth wbBytes (Bytes wbBytes)) - , Signal dom (Maybe (Axi4StreamM2S ('Axi4StreamConfig wbBytes 0 0) ())) - ) -rxReadMaster SNat = case strictlyPositiveDivRu @bufferBytes @wbBytes of - Dict -> case leMult @wbBytes @(DivRU bufferBytes wbBytes) of - Dict -> rxReadMaster# (SNat @(DivRU bufferBytes wbBytes)) - -{- | Circuit capable of reading the wishbone interface of @wbAxisRxBuffer@ and -extracting Axi packets. Mostly useful for verification, but can be synthesized. -The internal statemachine continuously reads the satus register of the buffer, -if the buffer is full or a packet is complete, it will: - -1. Read the packet length from the buffer. -2. Read the packet from the buffer. -3. Clear the packet length. -4. Clear the status register. --} -rxReadMaster# :: - forall dom wbBytes addrWidth fifoDepth. - ( HiddenClockResetEnable dom - , 1 <= fifoDepth - , 1 <= wbBytes - , KnownNat addrWidth - , KnownNat wbBytes - ) => - SNat fifoDepth -> - ( Signal dom (WishboneS2M (Bytes wbBytes)) - , Signal dom Axi4StreamS2M - ) -> - ( Signal dom (WishboneM2S addrWidth wbBytes (Bytes wbBytes)) - , Signal dom (Maybe (Axi4StreamM2S ('Axi4StreamConfig wbBytes 0 0) ())) - ) -rxReadMaster# SNat = mealyB go (AwaitingData @fifoDepth @wbBytes, Idle) - where - go - (bufState, readState :: ReadStateMachine fifoDepth) - ~(WishboneS2M{..}, Axi4StreamS2M{..}) = (nextState, (wbM2S, axiM2S)) - where - -- Driving wishbone signals - (writeEnable, addr) = case readState of - Idle -> (False, natToNum @(1 + fifoDepth)) - ClearingStatus -> (True, natToNum @(1 + fifoDepth)) - ReadingPacketSize -> (False, natToNum @fifoDepth) - ClearingPacketLength -> (True, natToNum @fifoDepth) - ReadingPacket i -> (False, checkedResize (pack i)) - - wbM2S = WishboneM2S{..} - busCycle = True - strobe = True - writeData = 0 - busSelect = maxBound - lock = False - cycleTypeIdentifier = Classic - burstTypeExtension = LinearBurst - - -- Driving Axi signals - (_tdata, _tstrb, _tid, _tdest, _tuser) = (reverse $ bitCoerce readData, repeat True, 0, 0, ()) - (_tkeep, _tlast) = case (bufState, readState) of - (PacketComplete s, ReadingPacket i) -> (mkKeep remaining, remaining <= natToNum @wbBytes) - where - remaining = satSub SatBound s (checkedResize i `shiftL` 2) - _ -> (repeat True, False) - - axiM2S = case (readState, acknowledge) of - (ReadingPacket _, True) -> Just Axi4StreamM2S{..} - _ -> Nothing - - -- Statemachine control - nextState = - if not acknowledge - then (bufState, readState) - else case (readState, bufState) of - (Idle, _) -> case (packetComplete, bufferFull) of - (True, _) -> (AwaitingData, ReadingPacketSize) - (_, True) -> (BufferFull, ReadingPacket minBound) - _ -> (AwaitingData, Idle) - where - (packetComplete, bufferFull) = unpack $ resize readData - (ReadingPacketSize, _) -> (PacketComplete packetSize, ReadingPacket 0) - where - packetSize = unpack $ checkedResize readData - (ReadingPacket i, _) - | _tready && lastBytes bufState nextReadState -> (bufState, ClearingPacketLength) - | _tready -> (bufState, nextReadState) - | otherwise -> (bufState, readState) - where - nextReadState = ReadingPacket (satSucc SatBound i) - (ClearingPacketLength, _) -> (bufState, ClearingStatus) - (ClearingStatus, _) -> (AwaitingData, Idle) - - lastBytes (PacketComplete s) (ReadingPacket i) = s <= (4 * checkedResize i) - lastBytes BufferFull (ReadingPacket i) = i == maxBound - lastBytes _ _ = False - -{- | Convert a @n@ number of bytes to an @m@ byte enable Vector to be used with Axi4Stream. - ->>> mkKeep @8 @4 3 -True :> True :> True :> False :> Nil ->>> mkKeep @8 @4 7 -True :> True :> True :> True :> Nil --} -mkKeep :: - forall maxIndex byteEnables. - ( KnownNat maxIndex - , KnownNat byteEnables - ) => - Index maxIndex -> - Vec byteEnables Bool -mkKeep nBytes - -- This can be written more neatly if we fix - -- https://github.com/clash-lang/clash-compiler/issues/2779 - | nBytes < natToNum @byteEnables = fmap (< checkedResize nBytes) indicesI - | otherwise = repeat True - -type AxiStreamBytesOnly nBytes = 'Axi4StreamConfig nBytes 0 0 - --- TODO: Replace with PacketStream - -{- | Wishbone to Axi4Stream interface, write operations to address 0 write to the Axi4Stream. -The _tkeep bits are set based on the busSelect bits, when writing to address 1, a transfer -is created that contains no data, but has the _tlast bit set. --} -wbToAxiTx :: - forall dom addrW nBytes. - (KnownNat addrW, KnownNat nBytes) => - Circuit - (Wishbone dom 'Standard addrW (Bytes nBytes)) - (Axi4Stream dom (AxiStreamBytesOnly nBytes) ()) -wbToAxiTx = case cancelMulDiv @nBytes @8 of - Dict -> Circuit $ unbundle . fmap go . bundle - where - go (WishboneM2S{..}, Axi4StreamS2M{..}) = - (WishboneS2M{readData, err, acknowledge, retry, stall}, axiM2S) - where - masterActive = busCycle && strobe - addrValid = addr <= 1 - err = masterActive && not (addrValid && writeEnable) - acknowledge = masterActive && not err && _tready - readData = 0 - retry = False - stall = False - (_tkeep, _tlast) - | lsb addr == 0 = (reverse $ unpack busSelect, False) - | otherwise = (repeat False, True) - - _tstrb = repeat False - _tid = 0 - _tdest = 0 - _tuser = () - _tdata = reverse $ unpack writeData - axiM2S :: Maybe (Axi4StreamM2S (AxiStreamBytesOnly nBytes) ()) - axiM2S = orNothing (masterActive && not err) $ Axi4StreamM2S{..} - -data AxiPacketFifoState maxPackets = AxiPacketFifoState - { packetCount :: Index (maxPackets + 1) - , newPacketSr :: Vec 2 Bool - , dumpPacket :: Bool - } - deriving (Generic, NFDataX, Show) - --- TODO: Replace with PacketStream - -{- | A Fifo circuit for Axi4Stream that stores an entire packet before -producing the packet at the output. If the fifo is full, it will start transmitting -the packet at the output. --} -axiStreamPacketFifo :: - forall dom nBytes fifoDepth maxPackets userType. - ( HiddenClockResetEnable dom - , 2 <= fifoDepth - , KnownNat nBytes - , 1 <= maxPackets - , NFDataX userType - ) => - SNat maxPackets -> - SNat fifoDepth -> - Circuit - (Axi4Stream dom (AxiStreamBytesOnly nBytes) userType) - (Axi4Stream dom (AxiStreamBytesOnly nBytes) userType) -axiStreamPacketFifo SNat fifoDepth@SNat = AS.forceResetSanity |> Circuit goCircuit - where - goCircuit ~(lhsM2S, fmap _tready -> outputReady) = (Axi4StreamS2M <$> inputReady, output) - where - -- I/O Combinatorials - inputReady = consumeAxi .&&. fifoReady - output = mux produceFifo fifoOut0 (pure Nothing) - fifoIn = mux consumeAxi lhsM2S (pure Nothing) - - -- Fifo - axiProxy = Proxy @(Axi4Stream dom (AxiStreamBytesOnly nBytes) userType) - fifo = DfConv.fifo axiProxy axiProxy fifoDepth - (fmap _tready -> fifoReady, fifoOut0) = - toSignals - fifo - (fifoIn, Axi4StreamS2M <$> (produceFifo .&&. outputReady)) - - -- I/O Control - initState = AxiPacketFifoState 0 (repeat False) False :: AxiPacketFifoState maxPackets - (consumeAxi, produceFifo) = mealyB go initState (lhsM2S, fifoOut0, fifoReady, outputReady) - - go s@AxiPacketFifoState{..} (inpM2S, fifoOut1, fifoReady1, outReady) = - (nextState, (consumeInp, produceOut)) - where - addPacket = maybe False _tlast inpM2S && consumeInp && fifoReady1 - subPacket = maybe False _tlast fifoOut1 && produceOut && outReady - packetCount1 = (if subPacket then satPred SatBound else id) packetCount - packetCount2 = (if head newPacketSr then satSucc SatBound else id) packetCount1 - - stallInp = packetCount == maxBound && or newPacketSr - consumeInp = not stallInp - dumpPacket1 = - (not dumpPacket && isJust inpM2S && not fifoReady1) - || (dumpPacket && maybe False _tlast inpM2S) - produceOut = dumpPacket || packetCount /= minBound - newPacketSr1 = newPacketSr <<+ addPacket - - nextState - | stallInp = s{packetCount = packetCount1} - | otherwise = AxiPacketFifoState packetCount2 newPacketSr1 dumpPacket1 - --- TODO: Add test that verifies throughput requirements. - -{- | Circuit to convert a sparse stream into a contiguous stream while remaining the throughput of -the input stream. --} -axiPacking :: - forall dom dataWidth idWidth destWidth. - ( HiddenClockResetEnable dom - , 1 <= dataWidth - , KnownNat dataWidth - , KnownNat idWidth - , KnownNat destWidth - ) => - Circuit - (Axi4Stream dom ('Axi4StreamConfig dataWidth idWidth destWidth) ()) - (PackedAxi4Stream dom ('Axi4StreamConfig dataWidth idWidth destWidth) ()) -axiPacking = AS.forceResetSanity |> Circuit (mealyB go Nothing) - where - go axiStored ~(input, Axi4StreamS2M{_tready = outputReady}) = - (axiNext, (Axi4StreamS2M inputReady, output)) - where - -- undefUser = deepErrorX "axiStreamFromByteStream: _tuser undefined" - - -- Try to append the incoming axi to the stored axi. - dropInput = maybe False (\a -> not (or (_tlast a :> _tkeep a))) input - combinedAxi = axiUserMap (const ()) <$> combineAxi4Stream axiStored input - - -- Shift the internal axi towards HEAD by one position. - -- If the head of the pre-shifted axi has its keep bit set, shifting is done. - extendedAxi = fmap extendAxi axiStored - packedAxi = fmap packAxi4Stream $ combinedAxi <|> extendedAxi - - (outputBuffer, excessBuffer) = splitAxi4Stream $ fmap (axiUserMap (const ((), ()))) packedAxi - - -- Output the pre-shifted axi if we can not shift anymore. - shiftingDone = not dropInput && maybe False isPackedTransfer outputBuffer - capturedLast = maybe False _tlast outputBuffer - output = if shiftingDone then outputBuffer else Nothing - - -- Flow control - (axiNext, inputReady) - | dropInput = (axiStored, True) -- Drop the input - | shiftingDone && outputReady = (excessBuffer, isJust combinedAxi) -- valid output, accepted - | shiftingDone && not outputReady = (axiStored, False) -- valid output, not accepted - | not shiftingDone && isJust input = (outputBuffer, isJust combinedAxi) -- Shift when input - | not shiftingDone && capturedLast = (outputBuffer, False) -- Shift when captured _tlast - | otherwise = (axiStored, False) -- No input - --- | Integrated logic analyzer for an Axi4Stream bus, it captures the data, keep, ready and last signals. -ilaAxi4Stream :: - forall dom conf userType. - (HiddenClock dom, KnownAxi4StreamConfig conf) => - -- | Number of registers to insert at each probe. Supported values: 0-6. - -- Corresponds to @C_INPUT_PIPE_STAGES@. Default is @0@. - Index 7 -> - -- | Number of samples to store. Corresponds to @C_DATA_DEPTH@. Default set - -- by 'ilaConfig' equals 'D4096'. - Depth -> - Circuit - (Axi4Stream dom conf userType) - (Axi4Stream dom conf userType) -ilaAxi4Stream stages0 depth0 = Circuit $ \(m2s, s2m) -> - let - ilaInst :: Signal dom () - ilaInst = - ila - ( ilaConfig - $ "m2s_tdata" - :> "m2s_tkeep" - :> "m2s_tlast" - :> "s2m_tready" - :> Nil - ) - { advancedTriggers = True - , stages = stages0 - , depth = depth0 - } - hasClock - (_tdata . fromJust <$> m2s) - (_tkeep . fromJust <$> m2s) - (_tlast . fromJust <$> m2s) - (_tready <$> s2m) - in - ilaInst `hwSeqX` (s2m, m2s) - -{- | A packed transfer is a transfer where either: -* _tlast is not set and all _tkeep bits are set. -* _tlast is set and only the first n _tkeep bits are set. ->>> let mkAxi keep last = Axi4StreamM2S @('Axi4StreamConfig 2 0 0) (repeat 0) keep (repeat True) last 0 0 () ->>> isPackedTransfer $ mkAxi (False :> False :> Nil) False -False ->>> isPackedTransfer $ mkAxi (True :> False :> Nil) False -False ->>> isPackedTransfer $ mkAxi (True :> True :> Nil) False -True ->>> isPackedTransfer $ mkAxi (False :> False :> Nil) True -True ->>> isPackedTransfer $ mkAxi (False :> True :> Nil) True -False --} -isPackedTransfer :: (KnownNat (DataWidth conf)) => Axi4StreamM2S conf a -> Bool -isPackedTransfer Axi4StreamM2S{..} - | _tlast = not $ hasGaps _tkeep - | otherwise = and _tkeep - where - rising = snd . mapAccumL (\prevKeep keep -> (keep, not prevKeep && keep)) True - hasGaps = or . rising diff --git a/bittide/src/Bittide/Axi4/Internal.hs b/bittide/src/Bittide/Axi4/Internal.hs deleted file mode 100644 index d014869d6..000000000 --- a/bittide/src/Bittide/Axi4/Internal.hs +++ /dev/null @@ -1,258 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# OPTIONS_HADDOCK hide #-} - -module Bittide.Axi4.Internal where - -import Clash.Prelude - -import Bittide.Extra.Maybe -import Data.Maybe -import Data.Proxy -import Protocols -import Protocols.Axi4.Stream - -import qualified Protocols.DfConv as DfConv - -{- $setup ->>> import Clash.Prelude ->>> import Data.Maybe --} - -{- | Function to move all keep, data and strobes in an Axi4Stream to the front of -the vectors based on the _tkeep field. --} -packAxi4Stream :: - (KnownAxi4StreamConfig conf) => - Axi4StreamM2S conf userType -> - Axi4StreamM2S conf userType -packAxi4Stream axi = output - where - output = axi{_tdata = newData, _tstrb = newStrobe, _tkeep = newKeep} - (newData, newKeep, newStrobe) = - unzip3 - $ fmap (\b -> (maybe 0 fst b, isJust b, maybe False snd b)) (packVec inpVec) - inpVec = orNothing <$> _tkeep axi <*> zip (_tdata axi) (_tstrb axi) - -{- | Function that moves all @Just@ values in a `Vec n (Maybe a)` to the front of -the vector. - ->>> packVec (Just 1 :> Nothing :> Just 2 :> Nil) -Just 1 :> Just 2 :> Nothing :> Nil ->>> packVec (Nothing :> Nothing :> Just 3 :> Nil) -Just 3 :> Nothing :> Nothing :> Nil --} -packVec :: (KnownNat n) => Vec n (Maybe a) -> Vec n (Maybe a) -packVec = foldr f (repeat Nothing) - where - f (Just a) acc = Just a +>> acc - f Nothing acc = acc - -{- | Splits an Axi4StreamM2S into a tuple of two Axi4StreamM2S. The first contains -all lower bytes of the transfer, the second contains the upper bytes. The first -output contains a transfer if at least one of the corresponding keep bits is -high, or none of the keep bits are high. The second output will contain a transfer -only if at least one of the corresponding keep bits is high. A transfer with -only null bytes and _tlast set will produce a transfer with _tlast set in the -first output, the second output will be @Nothing@. --} -splitAxi4Stream :: - forall widthA widthB idWith destWidth userTypeA userTypeB. - ( KnownNat widthA - , KnownNat widthB - ) => - -- | Axi4Stream transfer to split into two transfers. - Maybe - ( Axi4StreamM2S - ('Axi4StreamConfig (widthA + widthB) idWith destWidth) - (userTypeA, userTypeB) - ) -> - -- | - -- 1. Axi4Stream transfer with the first half of the data, keep and strobe vectors. - -- 2. Axi4Stream transfer with the second half of the data, keep and strobe vectors. - ( Maybe (Axi4StreamM2S ('Axi4StreamConfig widthA idWith destWidth) userTypeA) - , Maybe (Axi4StreamM2S ('Axi4StreamConfig widthB idWith destWidth) userTypeB) - ) -splitAxi4Stream Nothing = (Nothing, Nothing) -splitAxi4Stream (Just axi) = (orNothing aValid axiA, orNothing bValid axiB) - where - axiA = - Axi4StreamM2S - { _tdata = dataA - , _tkeep = keepA - , _tstrb = strbA - , _tlast = lastA - , _tid = _tid axi - , _tdest = _tdest axi - , _tuser = fst $ _tuser axi - } - - axiB = - Axi4StreamM2S - { _tdata = dataB - , _tkeep = keepB - , _tstrb = strbB - , _tlast = lastB - , _tid = _tid axi - , _tdest = _tdest axi - , _tuser = snd $ _tuser axi - } - - (dataA, dataB) = splitAtI $ _tdata axi - (keepA, keepB) = splitAtI $ _tkeep axi - (strbA, strbB) = splitAtI $ _tstrb axi - - -- An asserted last signal will be assigned to the "last" valid transfer - lastA = _tlast axi && not bValid - lastB = _tlast axi && bValid - - -- The first output is valid if: - -- \* At least one of the corresponding keep bits is set - -- \* None of the other keep bits are set. - aValid = or keepA || lastA - bValid = or keepB - -{- | Extends an @Axi4StreamM2S@ with null bytes. The lower indices of the vectors containing -data, keep and strobe are copied from the input transfer. The upper indices are filled -with null bytes. The _tlast, _tid, _tdest and _tuser fields are passed through. --} -extendAxi :: - forall widthA widthB idWith destWidth userType. - ( KnownNat widthA - , KnownNat widthB - , KnownNat idWith - , KnownNat destWidth - ) => - Axi4StreamM2S ('Axi4StreamConfig widthA idWith destWidth) userType -> - Axi4StreamM2S ('Axi4StreamConfig (widthA + widthB) idWith destWidth) userType -extendAxi axi = - Axi4StreamM2S - { _tdata = _tdata axi ++ repeat 0 - , _tkeep = _tkeep axi ++ repeat False - , _tstrb = _tstrb axi ++ repeat False - , _tlast = _tlast axi - , _tid = _tid axi - , _tdest = _tdest axi - , _tuser = _tuser axi - } - -{- | Combines two Axi4StreamM2S into a single Axi4StreamM2S. The data, keep and strobe -vectors are concatenated. The first transfer must contain the lower part of the -data, the second transfer must contain the upper part of the data. If _tlast is -set in the first transfer, a second transfer is not allowed and the function -will return @Nothing@. --} -combineAxi4Stream :: - forall widthA widthB idWidth destWidth userTypeA userTypeB. - ( KnownNat widthA - , KnownNat widthB - , KnownNat idWidth - , KnownNat destWidth - , NFDataX userTypeA - , NFDataX userTypeB - ) => - -- | First Axi4Stream transfer, should contain the lower bytes. - Maybe (Axi4StreamM2S ('Axi4StreamConfig widthA idWidth destWidth) userTypeA) -> - -- | Second Axi4Stream transfer, should contain the upper bytes. - Maybe (Axi4StreamM2S ('Axi4StreamConfig widthB idWidth destWidth) userTypeB) -> - -- | Combined Axi4Stream transfer, or @Nothing@ if the transfers are not compatible. - Maybe - ( Axi4StreamM2S - ('Axi4StreamConfig (widthA + widthB) idWidth destWidth) - (userTypeA, userTypeB) - ) -combineAxi4Stream maybeAxiA maybeAxiB = case (maybeAxiA, maybeAxiB) of - (Just axiA, Just axiB) -> orNothing compatibleAxis axiNew - where - axiNew = - Axi4StreamM2S - { _tdata = _tdata axiA ++ _tdata axiB - , _tkeep = _tkeep axiA ++ _tkeep axiB - , _tstrb = _tstrb axiA ++ _tstrb axiB - , _tlast = _tlast axiB - , _tid = _tid axiA - , _tdest = _tdest axiA - , _tuser = (_tuser axiA, _tuser axiB) - } - -- We can only combine two Axi4Streams if they have the same id, dest and the first - -- transfer is not the end of a packet. - compatibleAxis = - _tid axiA == _tid axiB && _tdest axiA == _tdest axiB && not (_tlast axiA) - (Just axi, Nothing) -> - Just - $ Axi4StreamM2S - { _tdata = _tdata axi ++ repeat 0 - , _tkeep = _tkeep axi ++ repeat False - , _tstrb = _tstrb axi ++ repeat False - , _tlast = _tlast axi - , _tid = _tid axi - , _tdest = _tdest axi - , _tuser = (_tuser axi, deepErrorX "combineAxi4Stream: Undefined second _tuser") - } - (Nothing, Just axi) -> - Just - $ Axi4StreamM2S - { _tdata = repeat 0 ++ _tdata axi - , _tkeep = repeat False ++ _tkeep axi - , _tstrb = repeat False ++ _tstrb axi - , _tlast = _tlast axi - , _tid = _tid axi - , _tdest = _tdest axi - , _tuser = (deepErrorX "combineAxi4Stream: Undefined first _tuser", _tuser axi) - } - _ -> Nothing - -{- | A custom of `==` for Axi4StreamM2S that only checks the data bytes if they are valid. -TODO: We should make better use of ADTs in `Axi4StreamM2S` to allow us to use derived -typeclass instances. --} -eqAxi4Stream :: - (Eq userType, KnownAxi4StreamConfig conf) => - Axi4StreamM2S conf userType -> - Axi4StreamM2S conf userType -> - Bool -eqAxi4Stream axiA axiB = lastSame && idSame && destSame && userSame && and keepsSame && and bytesValid - where - keepsSame = (==) <$> _tkeep axiA <*> _tkeep axiB - lastSame = _tlast axiA == _tlast axiB - idSame = _tid axiA == _tid axiB - destSame = _tdest axiA == _tdest axiB - userSame = _tuser axiA == _tuser axiB - - -- For all bytes where the keep is high, the data and strb must be the same. - keeps = (||) <$> _tkeep axiA <*> _tkeep axiB - dataSame = (==) <$> _tdata axiA <*> _tdata axiB - strbSame = (==) <$> _tstrb axiA <*> _tstrb axiB - bytesValid = zipWith3 (\k d s -> (not k) || (d && s)) keeps strbSame dataSame - --- | Extend a `Vec n a` to an arbitrary, larger size by appending `errorX` values.ip Axi4) -extendWithErrorX :: - forall n m a. - (KnownNat n, KnownNat m, NFDataX a) => - Vec n a -> - Vec (n + m) a -extendWithErrorX = (++ deepErrorX "extendWithErrorX: Undefined") - --- | Map a function over the _tuser field of an `Axi4StreamM2S. -axiUserMap :: - forall userTypeA userTypeB conf. - (userTypeA -> userTypeB) -> - Axi4StreamM2S conf userTypeA -> - Axi4StreamM2S conf userTypeB -axiUserMap f axi = axi{_tuser = f (_tuser axi)} - --- | Circuit version of `axiUserMap`, maps a function over the _tuser field of an `Axi4StreamM2S. -axiUserMapC :: - forall dom conf userTypeA userTypeB. - ( KnownAxi4StreamConfig conf - , HiddenClockResetEnable dom - , NFDataX userTypeA - , NFDataX userTypeB - ) => - (userTypeA -> userTypeB) -> - Circuit - (Axi4Stream dom conf userTypeA) - (Axi4Stream dom conf userTypeB) -axiUserMapC f = DfConv.map Proxy Proxy (axiUserMap f) diff --git a/bittide/src/Bittide/Calendar.hs b/bittide/src/Bittide/Calendar.hs deleted file mode 100644 index 623768bd1..000000000 --- a/bittide/src/Bittide/Calendar.hs +++ /dev/null @@ -1,524 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=8 #-} - -{- | -Contains the Bittide Calendar, which is a double buffered memory element that stores -instructions for the 'scatterUnitWb', 'gatherUnitWb' or 'switch'. Implementation is based -on the "Bittide Hardware" document. - -For documentation see 'Bittide.Calendar.calendar'. -| --} -module Bittide.Calendar ( - calendar, - mkCalendar, - CalendarConfig (..), - ValidEntry (..), - ExtraRegs, - Calendar, -) where - -import Clash.Prelude - -import Data.Constraint.Nat.Extra -import Data.Maybe -import Protocols.Wishbone - -import Bittide.Extra.Maybe -import Bittide.SharedTypes - -{- -NOTE [component calendar types] - -The calendar is a component that stores a vector of configurations called its entries. -It can be used by any component that has a periodic configuration that can change every cycle. -The calendar does not do any inspection whatsoever of the entries that it holds and thus -it does not care about the type of its entries, this type depends on the component that -instantiates the calendar. --} - -{- | Tuple of calendar entry @a@ and repetition count @Unsigned repetitionBits@ that -indicates the number of times the calendar entry should be repeated: -0 = no repetition, entry is valid for one cycle. -1 = repeated once, entry is valid for two cycles. --} -data ValidEntry a repetitionBits = ValidEntry - { veEntry :: a - -- ^ Calendar entry - , veRepeat :: Unsigned repetitionBits - -- ^ Number of times the calendar entry should be repeated: - -- 0 = no repetition, entry is valid for one cycle. - -- 1 = repeated once, entry is valid for two cycles. - } - deriving (BitPack, Eq, Generic, NFDataX, Show, ShowX) - -{- | 'Vec' of 'ValidEntry's to be used by a 'calendar'. The duration of the 'Calendar' in -clockCycles is equal to the @size@ of the 'Calendar' plus the 'sum' of all 'veRepeat's -of the 'ValidEntry's. --} -type Calendar size a repetitionBits = Vec size (ValidEntry a repetitionBits) - -{- | Configuration for the calendar, This type satisfies all -relevant constraints imposed by calendar. --} -data CalendarConfig nBytes addrW a where - CalendarConfig :: - ( KnownNat repetitionBits - , 1 <= 2 ^ repetitionBits -- This can be removed after https://github.com/clash-lang/ghc-typelits-natnormalise/issues/65 has been fixed. - , KnownNat bootstrapActive - , 1 <= bootstrapActive - , KnownNat bootstrapShadow - , 1 <= bootstrapShadow - , LessThan bootstrapActive maxCalDepth - , LessThan bootstrapShadow maxCalDepth - , Paddable a - , Show a - , ShowX a - , 2 <= maxCalDepth - ) => - -- | Maximum amount of entries that can be held per calendar. - SNat maxCalDepth -> - -- | Initial contents of the active calendar. - Calendar bootstrapActive a repetitionBits -> - -- | Initial contents of the inactive calendar. - Calendar bootstrapShadow a repetitionBits -> - CalendarConfig nBytes addrW a - --- | Standalone deriving is required because 'CalendarConfig' contains existential type variables. -deriving instance Show (CalendarConfig nBytes addrW a) - -{- | Wrapper function to create a 'calendar' from the given 'CalendarConfig', this way -we prevent the constraints of the type variables used in 'calendar' from leaking into -the rest of the system. --} -mkCalendar :: - ( HiddenClockResetEnable dom - , KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - ) => - -- | Calendar configuration for 'calendar'. - CalendarConfig nBytes addrW calEntry -> - -- | Wishbone interface (master to slave) - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | - -- 1. Currently active entry - -- 2. Metacycle indicator - -- 3. Wishbone interface. (slave to master) - (Signal dom calEntry, Signal dom Bool, Signal dom (WishboneS2M (Bytes nBytes))) -mkCalendar (CalendarConfig maxCalDepth bsActive bsShadow) = - calendar maxCalDepth bsActive bsShadow - -{- | State of the calendar excluding the buffers. It stores the depths of the active and -shadow calendar, the read pointer, buffer selector and a register for first cycle behavior. --} -data CalendarState maxCalDepth repetitionBits = CalendarState - { firstCycle :: Bool - -- ^ is True after reset, becomes false after first cycle. - , selectedBuffer :: AorB - -- ^ Indicates if buffer A or B is active. - , entryTracker :: Index maxCalDepth - -- ^ Read point for the active calendar. - , repetitionCounter :: Unsigned repetitionBits - -- ^ Counts the number of cycles that the current entry has been repeated. - , calDepthA :: Index maxCalDepth - -- ^ Depth of buffer A. - , calDepthB :: Index maxCalDepth - -- ^ Depth of buffer B. - , swapCalendars :: Bool - -- ^ Swaps the active and shadow calendar at the end of the metacycle. - } - deriving (Generic, NFDataX) - -{- | Contains the current active calendar entry along with the metacycle -indicator that is provided at the output of the the calendar component. Furthermore -it contains the shadow entry and depth of the shadow calendar which are provided -to the wishbone output hardware ('wbCalTX'). --} -data CalendarOutput calDepth calEntry = CalendarOutput - { activeEntry :: calEntry - -- ^ Current active entry. - , lastCycle :: Bool - -- ^ True when the last entry of the active calendar is present at the output. - , shadowEntry :: calEntry - -- ^ Current shadow entry - , shadowDepth :: Index calDepth - -- ^ Depth of current shadow calendar. - } - --- | Contains the read and write operations for both buffers. -data BufferControl calDepth calEntry = BufferControl - { readA :: Index calDepth - -- ^ Read address for buffer A. - , writeA :: Maybe (Located calDepth calEntry) - -- ^ Write operation for buffer B. - , readB :: Index calDepth - -- ^ Read address for buffer B - , writeB :: Maybe (Located calDepth calEntry) - -- ^ Write operation for buffer B. - } - -{-# NOINLINE calendar #-} - -{- | Hardware component that stores an active bittide calendar and a shadow bittide calendar. -The entries of the active calendar will be sequentially provided at the output, -the shadow calendar can be read from and written to through the wishbone interface. -The active and shadow calendar can be swapped by setting the shadowSwitch to True. --} -calendar :: - forall dom nBytes addrW maxCalDepth a repetitionBits bootstrapSizeA bootstrapSizeB. - ( HiddenClockResetEnable dom - , KnownNat addrW - , KnownNat bootstrapSizeA - , 1 <= bootstrapSizeA - , KnownNat bootstrapSizeB - , 1 <= bootstrapSizeB - , KnownNat nBytes - , 1 <= nBytes - , KnownNat repetitionBits - , 2 <= maxCalDepth - , LessThan bootstrapSizeA maxCalDepth - , LessThan bootstrapSizeB maxCalDepth - , Paddable a - , ShowX a - , Show a - ) => - -- | The maximum amount of entries that can be stored in the individual calendars. - SNat maxCalDepth -> - -- | Bootstrap calendar for the active buffer. - Calendar bootstrapSizeA a repetitionBits -> - -- | Bootstrap calendar for the shadow buffer. - Calendar bootstrapSizeB a repetitionBits -> - -- | Incoming wishbone interface - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | Currently active entry, Metacycle indicator and outgoing wishbone interface. - (Signal dom a, Signal dom Bool, Signal dom (WishboneS2M (Bytes nBytes))) -calendar SNat bootstrapActive bootstrapShadow wbIn = - (veEntry . activeEntry <$> calOut, lastCycle <$> calOut, wbOut) - where - ctrl :: Signal dom (CalendarControl maxCalDepth (ValidEntry a repetitionBits) nBytes) - ctrl = wbCalRX wbIn - wbOut = wbCalTX <$> ctrl <*> calOut - - -- XXX: Ideally we'd pad with 'errorX', but Vivado generates a critical warning - -- on undefined initial contents. We tried using the Clash flag - -- `-fclash-force-undefined=0`, but this triggered a bug: - -- - -- https://github.com/clash-lang/clash-compiler/issues/2360 - -- - bootstrapA = - bootstrapActive - ++ repeat @(maxCalDepth - bootstrapSizeA) - ValidEntry{veEntry = unpack 0, veRepeat = 0} - bootstrapB = - bootstrapShadow - ++ repeat @(maxCalDepth - bootstrapSizeA) - ValidEntry{veEntry = unpack 0, veRepeat = 0} - - bufA = blockRam bootstrapA (readA <$> bufCtrl) (writeA <$> bufCtrl) - bufB = blockRam bootstrapB (readB <$> bufCtrl) (writeB <$> bufCtrl) - - (bufCtrl, calOut) = mealyB go initState (ctrl, bufA, bufB) - - -- We can safely derive the initial calDepths from the bootStrap sizes because - -- we have the calDepth <= bootstrapSize constraints. Furthermore using resize - -- does not require additional constraints. - initState = - CalendarState - { firstCycle = True - , selectedBuffer = A - , entryTracker = 0 - , repetitionCounter = 0 - , calDepthA = resize (maxBound :: Index bootstrapSizeA) - , calDepthB = resize (maxBound :: Index bootstrapSizeB) - , swapCalendars = False - } - - go :: - CalendarState maxCalDepth repetitionBits -> - ( CalendarControl maxCalDepth (ValidEntry a repetitionBits) nBytes - , ValidEntry a repetitionBits - , ValidEntry a repetitionBits - ) -> - ( CalendarState maxCalDepth repetitionBits - , ( BufferControl maxCalDepth (ValidEntry a repetitionBits) - , CalendarOutput maxCalDepth (ValidEntry a repetitionBits) - ) - ) - go CalendarState{..} (CalendarControl{..}, bufAIn, bufBIn) = - (calState, (bufCtrl1, calOut1)) - where - selectedBuffer1 - | swapCalendars && lastCycle = swapAorB selectedBuffer - | otherwise = selectedBuffer - - lastCycle = not entryStillValid && entryTracker == activeDepth - - entryStillValid = repetitionCounter < veRepeat - - entryTracker1 - | entryStillValid = entryTracker - | not lastCycle = satSucc SatWrap entryTracker - | otherwise = 0 - - repetitionCounter1 - | entryStillValid = satSucc SatWrap repetitionCounter - | otherwise = 0 - - (activeEntry1, shadowEntry) - | A <- selectedBuffer = (bufAIn, bufBIn) - | B <- selectedBuffer = (bufBIn, bufAIn) - - (activeDepth, shadowDepth) - | A <- selectedBuffer = (calDepthA, calDepthB) - | B <- selectedBuffer = (calDepthB, calDepthA) - - (calDepthA1, calDepthB1) = - case (selectedBuffer, newShadowDepth) of - (A, Just newDepthB) -> (calDepthA, newDepthB) - (B, Just newDepthA) -> (newDepthA, calDepthB) - _ -> (calDepthA, calDepthB) - - (readA, writeA, readB, writeB) = - case (selectedBuffer1, isJust newShadowEntry) of - (A, True) -> (entryTracker1, Nothing, shadowReadAddr, newShadowEntry) - (A, _) -> (entryTracker1, Nothing, shadowReadAddr, Nothing) - (B, True) -> (shadowReadAddr, newShadowEntry, entryTracker1, Nothing) - (B, _) -> (shadowReadAddr, Nothing, entryTracker1, Nothing) - - activeEntry@(ValidEntry{..}) - | firstCycle = bootstrapA !! (0 :: Index 1) - | otherwise = activeEntry1 - - bufCtrl1 = BufferControl{readA, writeA, readB, writeB} - calOut1 = CalendarOutput{activeEntry, lastCycle, shadowEntry, shadowDepth} - calState = - CalendarState - { firstCycle = False - , selectedBuffer = selectedBuffer1 - , entryTracker = entryTracker1 - , repetitionCounter = repetitionCounter1 - , calDepthA = calDepthA1 - , calDepthB = calDepthB1 - , swapCalendars = armCalendarSwap || (not lastCycle && swapCalendars) - } - -{- | State of the calendar RX hardware, contains registers to store a new entry and -the shadow read address. --} -data WishboneRXState regSize calEntry calDepth = WishboneRXState - { calStRegisters :: RegisterBank regSize calEntry 'LittleEndian - -- ^ Write entry for the shadow calendar - , calStReadAddr :: RegisterBank regSize (Index calDepth) 'LittleEndian - -- ^ Read address for the shadow calendar. - } - deriving (Generic) - -instance - ( KnownNat regSize - , 1 <= regSize - , KnownNat calDepth - , Paddable calEntry - , 1 <= CLog 2 calDepth - , 1 <= calDepth - ) => - NFDataX (WishboneRXState regSize calEntry calDepth) - -{- | Control signals produced by the wishbone RX hardware for the calendar. -The calendar's wishbone address space is as follows: - * address 0 to n -> Registers that store a calEntry, due to the polymorphic nature of - calEntry, multiple addresses may be required to write an entry to the shadow calendar. - * address (n + 1) -> Writing to this address writes the calEntry stored in the registers - at address 0 to n to the shadow calendar at the location provided by writeData. - * address (n + 2) -> Register that stores the read address for the shadow calendar. - * address (n + 3) -> Writing to this address updates the depth (counter wrap around point) - for the shadow calendar. - * address (n + 4) -> Arm the calendar to swap the active and shadow calendars at the - end of the metacycle. --} -data CalendarControl calDepth calEntry nBytes = CalendarControl - { newShadowDepth :: Maybe (Index calDepth) - -- ^ The size of the next calendar - , newShadowEntry :: Maybe (Located calDepth calEntry) - -- ^ The next entry and its write address - , shadowReadAddr :: Index calDepth - -- ^ The next address to read from in the shadow calendar - , wishboneActive :: Bool - -- ^ Is the wishbone interface currently performing an operation - , wishboneError :: Bool - -- ^ Is the wishbone interface in an illegal state - , wishboneAddress :: WbAddress calEntry nBytes - -- ^ Address for the wishbone interface. - , armCalendarSwap :: Bool - -- ^ Swap the active and shadow calendar at the end of the metacycle. - } - -{- | Interface that decodes incoming wishbone operations into useful signals for the -calendar. --} -wbCalRX :: - forall dom calEntry calDepth addrW nBytes. - ( HiddenClockResetEnable dom - , Paddable calEntry - , ShowX calEntry - , KnownNat calDepth - , 2 <= calDepth - , KnownNat addrW - , KnownNat nBytes - , 1 <= nBytes - ) => - -- | Incoming wishbone signals - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | Calendar control signals. - Signal dom (CalendarControl calDepth calEntry nBytes) -wbCalRX = case oneLeCLog2n @calDepth of - Dict -> mealy go initState - where - initState = - WishboneRXState - { calStRegisters = deepErrorX "wbCalRX: calStRegisters undefined." - , calStReadAddr = deepErrorX "wbCalRX: calStReadAddr undefined." - } - - go :: - WishboneRXState (nBytes * 8) calEntry calDepth -> - WishboneM2S addrW nBytes (Bytes nBytes) -> - ( WishboneRXState (nBytes * 8) calEntry calDepth - , CalendarControl calDepth calEntry nBytes - ) - go wbState@WishboneRXState{..} WishboneM2S{..} = (wbState1, calControl) - where - calEntryRegs = natToNum @(Regs calEntry (nBytes * 8)) - - wbAddrValid = addr <= (resize $ pack (maxBound :: WbAddress calEntry nBytes)) - wishboneAddress = unpack $ resize addr - wishboneActive = busCycle && strobe - wishboneError = wishboneActive && not wbAddrValid - wbWriting = wishboneActive && writeEnable && not wishboneError - wbNewCalEntry = wbWriting && wishboneAddress < calEntryRegs - - wbNewShadowWriteAddr = wbWriting && wishboneAddress == shadowWriteWbAddr - wbNewShadowReadAddr = wbWriting && wishboneAddress == shadowReadWbAddr - wbNewShadowDepth = wbWriting && wishboneAddress == shadowDepthWbAddr - armCalendarSwap = wbWriting && wishboneAddress == calSwapWbAddr - shadowReadAddr = getDataLe @(nBytes * 8) calStReadAddr - shadowEntryData = getDataLe @(nBytes * 8) calStRegisters - - wbState1 = - wbState - { calStRegisters = newPartialCalEntry - , calStReadAddr = newShadowReadAddr - } - - newPartialCalEntry - | wbNewCalEntry = updateRegisters wishboneAddress calStRegisters - | otherwise = calStRegisters - newShadowReadAddr - | wbNewShadowReadAddr = updateRegisters (0 :: Int) calStReadAddr - | otherwise = calStReadAddr - - updateRegisters :: - forall i a. - (Enum i, KnownNat (BitSize a)) => - i -> - RegisterBank (nBytes * 8) a 'LittleEndian -> - RegisterBank (nBytes * 8) a 'LittleEndian - updateRegisters i = updateRegBank i busSelect writeData - - calAddr = bitCoerce $ resize writeData - - newShadowDepth = orNothing wbNewShadowDepth calAddr - newShadowEntry = orNothing wbNewShadowWriteAddr (calAddr, shadowEntryData) - - calControl = - CalendarControl - { newShadowDepth - , newShadowEntry - , shadowReadAddr - , wishboneActive - , wishboneError - , wishboneAddress - , armCalendarSwap - } - -{- | Wishbone interface that drives the outgoing wishbone data based on the received -wishbone address. Can be used to read one of the following registers: - * The shadow calendar entry register - * The shadow calendar read address register - * The shadow calendar depth register --} -wbCalTX :: - forall calDepth calEntry nBytes. - ( Paddable (Index calDepth) - , Paddable calEntry - , Show calEntry - , KnownNat nBytes - , 1 <= nBytes - ) => - CalendarControl calDepth calEntry nBytes -> - CalendarOutput calDepth calEntry -> - WishboneS2M (Bytes nBytes) -wbCalTX - CalendarControl{shadowReadAddr, wishboneActive, wishboneError, wishboneAddress} - CalendarOutput{shadowEntry, shadowDepth} = wbOut - where - readData = - case (getRegsLe shadowEntry, getRegsLe shadowReadAddr, getRegsLe shadowDepth) of - (RegisterBank entryVec, RegisterBank readAddrVec, RegisterBank depthVec) -> - ((entryVec :< 0b0) ++ readAddrVec ++ depthVec) !! wishboneAddress - wbOut = - (emptyWishboneS2M @(Bytes nBytes)) - { acknowledge = wishboneActive - , err = wishboneError - , readData - } - -updateRegBank :: - ( Enum i - , KnownNat nBytes - , 1 <= nBytes - , KnownNat (BitSize a) - ) => - i -> - BitVector nBytes -> - Bytes nBytes -> - RegisterBank (nBytes * 8) a 'LittleEndian -> - RegisterBank (nBytes * 8) a 'LittleEndian -updateRegBank i byteSelect newBV (RegisterBank vec) = RegisterBank newVec - where - newVec = replace i (regUpdate byteSelect (vec !! i) newBV) vec - -regUpdate :: - (KnownNat nBytes) => - BitVector nBytes -> - Bytes nBytes -> - Bytes nBytes -> - Bytes nBytes -regUpdate byteEnable oldEntry newEntry = - bitCoerce - $ (\e (o, n :: BitVector 8) -> if e then n else o) - <$> bitCoerce byteEnable - <*> zip (bitCoerce oldEntry) (bitCoerce newEntry) - -type WbAddress calEntry nBytes = Index (Regs calEntry (nBytes * 8) + ExtraRegs) -type ExtraRegs = 4 - -shadowWriteWbAddr :: forall n. (KnownNat n, 4 <= n) => Index n -shadowWriteWbAddr = natToNum @(n - 4) - -shadowReadWbAddr :: forall n. (KnownNat n, 3 <= n) => Index n -shadowReadWbAddr = natToNum @(n - 3) - -shadowDepthWbAddr :: forall n. (KnownNat n, 2 <= n) => Index n -shadowDepthWbAddr = natToNum @(n - 2) - -calSwapWbAddr :: forall n. (KnownNat n, 1 <= n) => Index n -calSwapWbAddr = natToNum @(n - 1) diff --git a/bittide/src/Bittide/ClockControl.hs b/bittide/src/Bittide/ClockControl.hs deleted file mode 100644 index 86f63169c..000000000 --- a/bittide/src/Bittide/ClockControl.hs +++ /dev/null @@ -1,199 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -Wno-orphans -fconstraint-solver-iterations=10 #-} - --- | Clock controller types and some constants/defaults. -module Bittide.ClockControl ( - ClockControlConfig (..), - RelDataCount, - SettlePeriod, - SpeedChange (..), - clockPeriodFs, - defClockConfig, - settleCycles, - sign, - speedChangeToFincFdec, - targetDataCount, -) -where - -import Clash.Explicit.Prelude hiding (PeriodToCycles) -import Clash.Signal.Internal (Femtoseconds (..)) -import Data.Aeson (ToJSON (toJSON)) -import Data.Csv -import Data.Proxy (Proxy (..)) -import GHC.Stack (HasCallStack) - -import Bittide.Arithmetic.Time (PeriodToCycles, microseconds) - -type SettlePeriod = Femtoseconds - --- | Configuration passed to 'clockControl' -data ClockControlConfig dom n m c = ClockControlConfig - { cccSettleCycles :: Unsigned 32 - -- ^ Like 'cccPessimisticPeriod', but expressed as number of cycles. - -- - -- TODO: Should be removed, it follows from other fields + domain. - , cccSettlePeriod :: Femtoseconds - -- ^ Period it takes for a clock frequency request to settle. This is not - -- modelled, but an error is thrown if a request is submitted more often than - -- this. 'clockControl' should therefore not request changes more often. - -- - -- This is a PLL property. - , cccBufferSize :: SNat n - -- ^ Size of elastic buffers. Used to observe bounds and 'targetDataCount'. - , cccStabilityCheckerMargin :: SNat m - -- ^ Bound on the number of elements the elastic buffer is allowed - -- to deviate from while still being considered "stable". - , cccStabilityCheckerFramesize :: SNat c - -- ^ The minimum number of clock cycles an elastic buffer must - -- remain within the @cccStabilityCheckerMargin@ to be considered - -- "stable". - , cccEnableReframing :: Bool - -- ^ Enable reframing. Reframing allows a system to resettle buffers around - -- their midpoints, without dropping any frames. For more information, see - -- [arXiv:2303.11467](https://arxiv.org/abs/2303.11467). - , cccReframingWaitTime :: Unsigned 32 - -- ^ Number of pessimistic settle cycles to wait until reframing - -- takes place after stability has been detected, as it is used by - -- the "detect, store, and wait" reframing approach - } - deriving (Lift) - -{- | The (virtual) type of the FIFO's data counter. Setting this to -'Unsigned' captures the real implementation of the FIFO, while -setting it to 'Signed' results in a virtual correction shifting the -FIFO's center to be always at @0@. - -_(remember to also modify 'targetDataCount' below if the -representation of 'RelDataCount' gets changed.)_ --} -type RelDataCount n = Signed n - -{- | The target data count within a (virtual) FIFO. It is usually set -to be at the FIFO's center. - -_(recommended values are @0@ if 'RelDataCount' is 'Signed' and @shiftR -maxBound 1 + 1@ if it is 'Unsigned')_ --} -targetDataCount :: (KnownNat n) => RelDataCount n -targetDataCount = 0 - --- | Safer version of FINC/FDEC signals present on the Si5395/Si5391 clock multipliers. -data SpeedChange - = NoChange - | SlowDown - | SpeedUp - deriving (Eq, Show, Generic, BitPack, ShowX, NFDataX) - -{- | Converts speed changes into a normalized scalar, which reflects -their effect on clock control. --} -sign :: (Num a) => SpeedChange -> a -sign = \case - SpeedUp -> 1 - NoChange -> 0 - SlowDown -> -1 - -data ToFincFdecState dom - = Wait (Index (PeriodToCycles dom (Microseconds 1))) - | Pulse (Index (PeriodToCycles dom (Nanoseconds 100))) SpeedChange - | Idle - deriving (Generic, NFDataX) - -{- | Convert 'SpeedChange' to a pair of (FINC, FDEC). This is currently hardcoded -to work on the Si5395 constraints: - - * Minimum Pulse Width: 100 ns - * Update Rate: 1 us - -TODO: De-hardcode --} -speedChangeToFincFdec :: - forall dom. - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Signal dom SpeedChange -> - Signal dom (Bool, Bool) -speedChangeToFincFdec clk rst = - dflipflop clk . fmap conv . mealy clk rst enableGen go (Wait maxBound) - where - go :: ToFincFdecState dom -> SpeedChange -> (ToFincFdecState dom, SpeedChange) - go (Wait n) _s - | n == 0 = (Idle, NoChange) - | otherwise = (Wait (n - 1), NoChange) - go (Pulse n s) _s - | n == 0 = (Wait maxBound, s) - | otherwise = (Pulse (n - 1) s, s) - go Idle NoChange = (Idle, NoChange) - go Idle s = (Pulse maxBound s, NoChange) - - -- FINC FDEC - conv NoChange = (False, False) - conv SpeedUp = (True, False) - conv SlowDown = (False, True) - -instance ToField SpeedChange where - toField SpeedUp = "speedUp" - toField SlowDown = "slowDown" - toField NoChange = "noChange" - -instance (KnownNat n) => ToField (Unsigned n) where - toField = toField . toInteger - -instance (KnownNat n) => ToJSON (Unsigned n) where - toJSON = toJSON . toInteger - -instance (KnownNat n) => ToField (Signed n) where - toField = toField . toInteger - -instance (KnownNat n) => ToJSON (Signed n) where - toJSON = toJSON . toInteger - -instance ToField Femtoseconds where - toField (Femtoseconds fs) = toField fs - -instance ToJSON Femtoseconds where - toJSON (Femtoseconds fs) = toJSON fs - -clockPeriodFs :: forall dom. (KnownDomain dom) => Proxy dom -> Femtoseconds -clockPeriodFs Proxy = Femtoseconds (1000 * snatToNum (clockPeriod @dom)) - -defClockConfig :: forall dom. (KnownDomain dom) => ClockControlConfig dom 12 8 1500000 -defClockConfig = - ClockControlConfig - { cccSettleCycles = settleCycles self - , cccSettlePeriod = microseconds 1 - , cccBufferSize = d12 -- 2**12 ~ 4096 - , cccStabilityCheckerMargin = SNat - , cccStabilityCheckerFramesize = SNat - , cccEnableReframing = True - , cccReframingWaitTime = 160000 - } - where - self = defClockConfig @dom - -{- | Number of cycles to wait on a given clock frequency and clock settings in -order for the settle period to pass. --} -settleCycles :: - forall dom n m c. - ( HasCallStack - , KnownDomain dom - ) => - ClockControlConfig dom n m c -> - -- | It would take a 10 GHz clock only a 10_000 cycles to wait 1 µs. This can be - -- met by an @Unsigned 14@: @2^14 ~ 16384@. To massively overkill it we bump it - -- up to 32 bits. - Unsigned 32 -settleCycles ClockControlConfig{cccSettlePeriod} = - checkedFromIntegral nCycles - where - nCycles = (settlePeriod `div` period) + 1 - Femtoseconds settlePeriod = cccSettlePeriod - Femtoseconds period = clockPeriodFs @dom Proxy diff --git a/bittide/src/Bittide/ClockControl/Callisto.hs b/bittide/src/Bittide/ClockControl/Callisto.hs deleted file mode 100644 index 00eef7275..000000000 --- a/bittide/src/Bittide/ClockControl/Callisto.hs +++ /dev/null @@ -1,246 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} - -module Bittide.ClockControl.Callisto ( - CallistoResult (..), - ReframingState (..), - callistoClockControl, -) where - -import Clash.Prelude - -import Data.Constraint -import Data.Constraint.Nat (leTrans) -import Data.Constraint.Nat.Extra (euclid3, useLowerLimit) - -import Bittide.ClockControl -import Bittide.ClockControl.Callisto.Types -import Bittide.ClockControl.Callisto.Util -import Bittide.ClockControl.StabilityChecker -import Bittide.Extra.Maybe - -import qualified Clash.Cores.Xilinx.Floating as F -import qualified Clash.Signal.Delayed as D - -{-# NOINLINE callistoClockControl #-} - -{- | Determines how to influence clock frequency given statistics provided by -all elastic buffers. See 'callisto' for more information. --} -callistoClockControl :: - forall n m dom margin framesize. - ( KnownDomain dom - , KnownNat n - , KnownNat m - , KnownNat margin - , KnownNat framesize - , 1 <= n - , 1 <= m - , n + m <= 32 - , 1 <= framesize - ) => - Clock dom -> - Reset dom -> - Enable dom -> - -- | Configuration for this component, see individual fields for more info. - ClockControlConfig dom m margin framesize -> - -- | Link availability mask - Signal dom (BitVector n) -> - -- | Statistics provided by elastic buffers. - Vec n (Signal dom (RelDataCount m)) -> - Signal dom (CallistoResult n) -callistoClockControl clk rst ena ClockControlConfig{..} mask allDataCounts = - withClockResetEnable clk rst ena - $ let - dataCounts = filterCounts <$> fmap bv2v mask <*> bundle allDataCounts - updateCounter = wrappingCounter cccSettleCycles - shouldUpdate = updateCounter .==. 0 - scs = bundle $ map stabilityCheck $ unbundle dataCounts - allStable = allAvailable stable <$> mask <*> scs - allSettled = allAvailable settled <$> mask <*> scs - state = register initState state' - - state' = - mux - shouldUpdate - (callisto controlConfig mask scs dataCounts state) - state - - stabilityCheck = - stabilityChecker - cccStabilityCheckerMargin - cccStabilityCheckerFramesize - in - CallistoResult - <$> (orNothing <$> shouldUpdate <*> fmap _b_k state') - <*> scs - <*> allStable - <*> allSettled - <*> (rfState <$> state') - where - controlConfig = - ControlConfig - { reframingEnabled = cccEnableReframing - , waitTime = cccReframingWaitTime - , targetCount = targetDataCount - } - - initState = - ControlSt - { _z_k = 0 - , _b_k = NoChange - , _steadyStateTarget = 0.0 - , rfState = Detect - } - - filterCounts vMask vCounts = flip map (zip vMask vCounts) - $ \(isActive, count) -> if isActive == high then count else 0 - - allAvailable f x y = - and $ zipWith ((||) . not) (bitToBool <$> bv2v x) (f <$> y) - -{- | Clock correction strategy based on: - - https://github.com/bittide/Callisto.jl - -Note that this is an incredibly wasteful implementation: it instantiates -numerous floating point multipliers and adders, even though they're not doing -any useful work 99% of the time. Furthermore, 'RelDataCount' isn't properly -scaled to match elastic buffer sizes, resulting in unnecessarily big integer -adders. Optimization work has been postponed because: - - * It isn't clear yet whether this will be the final clock control algorithm. - * These algorithms will probably run on a Risc core in the future. --} -callisto :: - forall m n dom. - ( HiddenClockResetEnable dom - , KnownNat n - , KnownNat m - , 1 <= n - , 1 <= m - , -- 'callisto' sums incoming 'RelDataCount's and feeds them to a Xilinx signed to - -- float IP. We can currently only interpret 32 bit signeds to unsigned, so to - -- make sure we don't overflow any addition we force @n + m <= 32@. - n + m <= 32 - ) => - -- | Configuration parameters. - ControlConfig m -> - -- | Link availability mask. - Signal dom (BitVector n) -> - -- | Stability indicators for each of the elastic buffers. - Signal dom (Vec n StabilityIndication) -> - -- | Data counts from elastic buffers. - Signal dom (Vec n (RelDataCount m)) -> - -- | Current state. - Signal dom ControlSt -> - -- | Updated state. - Signal dom ControlSt -callisto ControlConfig{..} mask scs dataCounts state = - rfStateUpdate - <$> (all stable <$> scs) - <*> D.toSignal c_des - <*> updatedState - where - updatedState = - D.toSignal - $ ControlSt - <$> delayIU "[1]" z_kNext - <*> b_kNext - <*> delayIU "[2]" steadyStateTarget - <*> delayIU "[3]" (D.fromSignal (rfState <$> state)) - - -- See fields in 'ControlSt' for documentation of 'z_k', 'b_k', and css. - z_k :: DSignal dom 0 (Signed 32) - z_k = D.fromSignal (_z_k <$> state) - - b_k :: DSignal dom 0 SpeedChange - b_k = D.fromSignal (_b_k <$> state) - - steadyStateTarget :: DSignal dom 0 Float - steadyStateTarget = D.fromSignal (_steadyStateTarget <$> state) - - -- see clock control algorithm simulation here: - -- - -- https://github.com/bittide/Callisto.jl/blob/e47139fca128995e2e64b2be935ad588f6d4f9fb/demo/pulsecontrol.jl#L24 - -- - -- `k_p` (proportional gain) is copied from the Julia implementation. `fStep` should - -- match the step size of the clock boards. For all our HITL tests this is set by - -- `HwCcTopologies.commonStepSizeSelect`. - -- - k_p, fStep :: forall d. DSignal dom d Float - k_p = pure 2e-8 - fStep = pure 100e-9 - - r_k :: DSignal dom F.FromS32DefDelay Float - r_k = - F.fromS32 - $ D.fromSignal - $ let - nBuffers = case useLowerLimit @n @m @32 of - Dict -> safePopCountTo32 <$> mask - measuredSum = sumTo32 <$> dataCounts - targetCountSigned = case euclid3 @n @m @32 of - Dict -> case leTrans @1 @n @(32 - m) of - Sub Dict -> extend @_ @_ @(32 - m - 1) $ dataCountToSigned targetCount - in - measuredSum - (pure targetCountSigned * nBuffers) - - c_des :: DSignal dom (F.FromS32DefDelay + F.MulDefDelay + F.AddDefDelay) Float - c_des = delayIU "[4]" $ (k_p `F.mul` r_k) `F.add` delayIU "[5]" steadyStateTarget - - z_kNext :: DSignal dom 0 (Signed 32) - z_kNext = z_k + fmap sign b_k - - c_est :: DSignal dom (F.FromS32DefDelay + F.MulDefDelay + F.AddDefDelay) Float - c_est = delayIU "[6]" $ fStep `F.mul` F.fromS32 z_kNext - - b_kNext = - flip fmap (F.compare c_des c_est) $ \case - F.LT -> SlowDown - F.GT -> SpeedUp - F.EQ -> NoChange - -- TODO: Propagate errors upwards? - F.NaN -> NoChange - - rfStateUpdate stable target st@ControlSt{..} - | not reframingEnabled = st - | otherwise = case rfState of - Detect - | not stable -> - st - | otherwise -> - st - { rfState = - Wait - { curWaitTime = waitTime - , targetCorrection = target - } - } - Wait{..} - | curWaitTime > 0 -> - st - { rfState = - Wait - { curWaitTime = curWaitTime - 1 - , .. - } - } - | otherwise -> - st - { rfState = Done - , _steadyStateTarget = targetCorrection - } - Done -> st - - -- Uninitialized version of 'Clash.Signal.Delayed.delayI' - delayIU :: - forall d k a. - (HiddenClock dom, HiddenEnable dom, NFDataX a, KnownNat d) => - String -> - DSignal dom k a -> - DSignal dom (k + d) a - delayIU = - D.delayI . errorX . ("callisto: No start value " <>) diff --git a/bittide/src/Bittide/ClockControl/Callisto/Types.hs b/bittide/src/Bittide/ClockControl/Callisto/Types.hs deleted file mode 100644 index 1a81dee8c..000000000 --- a/bittide/src/Bittide/ClockControl/Callisto/Types.hs +++ /dev/null @@ -1,85 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE UndecidableInstances #-} - -module Bittide.ClockControl.Callisto.Types ( - CallistoResult (..), - ReframingState (..), - ControlConfig (..), - ControlSt (..), -) where - -import Clash.Prelude - -import Bittide.ClockControl -import Bittide.ClockControl.StabilityChecker (StabilityIndication) - --- | Result of the clock control algorithm. -data CallistoResult (n :: Nat) = CallistoResult - { maybeSpeedChange :: Maybe SpeedChange - -- ^ Speed change requested for clock multiplier. This is 'Just' for a single - -- cycle. - , stability :: Vec n StabilityIndication - -- ^ All stability indicators for all of the elastic buffers. - , allStable :: Bool - -- ^ Joint stability indicator signaling that all elastic buffers - -- are stable. - , allSettled :: Bool - -- ^ Joint "being-settled" indicator signaling that all elastic - -- buffers have been settled. - , reframingState :: ReframingState - -- ^ State of the Reframing detector - } - deriving (Generic, NFDataX) - --- | Callisto specific control configuration options. -data ControlConfig (m :: Nat) = ControlConfig - { reframingEnabled :: Bool - -- ^ Enable reframing. Reframing allows a system to resettle buffers around - -- their midpoints, without dropping any frames. For more information, see - -- [arXiv:2303.11467](https://arxiv.org/abs/2303.11467). - , waitTime :: Unsigned 32 - -- ^ Number of cycles to wait until reframing takes place after - -- stability has been detected. - , targetCount :: RelDataCount m - -- ^ Target data count. See 'targetDataCount'. - } - -{- | State of the state machine for realizing the "detect, store, and -wait" approach of [arXiv:2303.11467](https://arxiv.org/abs/2303.11467) --} -data ReframingState - = -- | The controller remains in this state until stability has been - -- detected. - Detect - | -- | The controller remains in this state for the predefined - -- number of cycles with the assumption that the elastic buffers - -- of all other nodes are sufficiently stable after that time. - Wait - { targetCorrection :: !Float - -- ^ Stored correction value to be applied at reframing time. - , curWaitTime :: !(Unsigned 32) - -- ^ Number of cycles to wait until reframing takes place. - } - | -- | Reframing has taken place. There is nothing more to do. - Done - deriving (Generic, NFDataX) - --- | Callisto's internal state used in 'callisto' -data ControlSt = ControlSt - { _z_k :: !(Signed 32) - -- ^ Accumulated speed change requests, where speedup ~ 1, slowdown ~ -1. - , _b_k :: !SpeedChange - -- ^ Previously submitted speed change request. Used to determine the estimated - -- clock frequency. - , _steadyStateTarget :: !Float - -- ^ Steady-state value (determined when stability is detected for - -- the first time). - , rfState :: !ReframingState - -- ^ finite state machine for reframing detection - } - deriving (Generic, NFDataX) diff --git a/bittide/src/Bittide/ClockControl/Callisto/Util.hs b/bittide/src/Bittide/ClockControl/Callisto/Util.hs deleted file mode 100644 index 5b269a04b..000000000 --- a/bittide/src/Bittide/ClockControl/Callisto/Util.hs +++ /dev/null @@ -1,111 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - --- | Collection of helpers used in 'Bittide.ClockControl.Callisto.callisto'. -module Bittide.ClockControl.Callisto.Util where - -import Clash.Prelude - -import Bittide.ClockControl (RelDataCount, SpeedChange (..)) - -{- | Safe 'RelDataCount' to 'Signed' conversion. -(works for both: 'RelDataCount' being 'Signed' or 'Unsigned') --} -dataCountToSigned :: - forall n. - (KnownNat n) => - RelDataCount n -> - Signed (n + 1) -dataCountToSigned = bitCoerce . extend - -{- | A counter that starts at a given value, counts down, and if it reaches -zero wraps around to the initial value. --} -wrappingCounter :: - (HiddenClockResetEnable dom, KnownNat n) => - Unsigned n -> - Signal dom (Unsigned n) -wrappingCounter upper = counter - where - counter = register upper (go <$> counter) - - go 0 = upper - go n = pred n - -{- | A version of 'sum' that is guaranteed not to overflow. -(works for both: 'RelDataCount' being 'Signed' or 'Unsigned') --} -safeSum :: - ( KnownNat n - , KnownNat m - , 1 <= n - ) => - Vec n (RelDataCount m) -> - RelDataCount (m + n - 1) -safeSum = sum . map extend - -{- | Sum a bunch of 'RelDataCount's to a @Signed 32@, without overflowing. -(works for both: 'RelDataCount' being 'Signed' or 'Unsigned') --} -sumTo32 :: - forall n m. - ( KnownNat m - , KnownNat n - , (m + n) <= 32 - , 1 <= n - ) => - Vec n (RelDataCount m) -> - Signed 32 -sumTo32 = - extend @_ @_ @(32 - (m + n)) - . dataCountToSigned - . safeSum - --- | Counts the number of 'high' bits in a bitvector. -safePopCountTo32 :: - forall n. - ( KnownNat n - , (1 + n) <= 32 - , 1 <= n - ) => - BitVector n -> - Signed 32 -safePopCountTo32 = - sumTo32 . unpack @(Vec n (RelDataCount 1)) - -type FINC = Bool -type FDEC = Bool - -speedChangeToPins :: SpeedChange -> (FINC, FDEC) -speedChangeToPins = \case - SpeedUp -> (True, False) - SlowDown -> (False, True) - NoChange -> (False, False) - -{- | Holds any @a@ which has any bits set for @stickyCycles@ clock cycles. -On receiving a new @a@ with non-zero bits, it sets the new incoming value as it output -and holds it for @stickyCycles@ clock cycles. --} -stickyBits :: - forall dom stickyCycles a. - ( HiddenClockResetEnable dom - , NFDataX a - , BitPack a - , 1 <= stickyCycles - ) => - SNat stickyCycles -> - Signal dom a -> - Signal dom a -stickyBits SNat = mealy go (0, unpack 0) - where - go :: (Index stickyCycles, a) -> a -> ((Index stickyCycles, a), a) - go (count, storedBits) incomingBits = ((nextCount, nextStored), storedBits) - where - newIncoming = pack incomingBits /= 0 - predCount = satPred SatZero count - holdingBits = count /= 0 - (nextStored, nextCount) - | newIncoming = (incomingBits, maxBound) - | holdingBits = (storedBits, predCount) - | otherwise = (unpack 0, predCount) diff --git a/bittide/src/Bittide/ClockControl/ParseRegisters.hs b/bittide/src/Bittide/ClockControl/ParseRegisters.hs deleted file mode 100644 index ac171b332..000000000 --- a/bittide/src/Bittide/ClockControl/ParseRegisters.hs +++ /dev/null @@ -1,91 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE PackageImports #-} - -module Bittide.ClockControl.ParseRegisters where - -import Prelude - -import Bittide.ClockControl.Si539xSpi (RegisterEntry) -import Clash.Sized.Vector (listToVecTH) -import Control.Monad.IO.Class (liftIO) -import Data.List (isPrefixOf) -import Data.List.Extra (trim) -import Language.Haskell.TH -import System.FilePath (()) - -import "bittide-extra" Numeric.Extra (parseHex) - -import Paths_bittide (getDataFileName) - -type Lines = [String] - -{- | Parse a triple of hex strings into a 'RegisterEntry'. - ->>> parseRegisterEntry "0x0850,0x00" -Right (0b0000_1000,0b0101_0000,0b0000_0000) ->>> parseRegisterEntry "0x0850,,," -Left "Could not parse as triple: 0x0850,,," --} -parseRegisterEntry :: String -> Either String RegisterEntry -parseRegisterEntry [_, _, h0, h1, h2, h3, _, _, _, h4, h5] = do - n0 <- parseHex [h0, h1] - n1 <- parseHex [h2, h3] - n2 <- parseHex [h4, h5] - pure (n0, n1, n2) -parseRegisterEntry s = Left ("Could not parse as triple: " <> s) - --- | Keep parsing register entries until a line starting with '#' is encountered. -parseRegisterEntries :: Lines -> Either String ([RegisterEntry], Lines) -parseRegisterEntries [] = Left "parseRegisterEntries: unexpected EOF" -parseRegisterEntries (l : ls0) - | "#" `isPrefixOf` l = pure ([], ls0) - | otherwise = do - entry <- parseRegisterEntry l - (entries, ls1) <- parseRegisterEntries ls0 - pure (entry : entries, ls1) - -{- | Consume lines until a line matching the given string is encountered, then -parse register entries until a line starting with '#' is encountered. --} -parseSection :: String -> Lines -> Either String ([RegisterEntry], Lines) -parseSection s [] = Left ("parseSection: unexpected EOF for '" <> s <> "'") -parseSection s (l : ls) - | l == s = parseRegisterEntries ls - | otherwise = parseSection s ls - -{- | Parse a preamble, configuration, and postamble from a \"CSV\" file produced -by ClockBuilder Pro. Note that this is not actually a CSV file, but multiple -CSV files concatenated together, with comments (lines starting with @#@) in -between. --} -parse :: Lines -> Either String ([RegisterEntry], [RegisterEntry], [RegisterEntry]) -parse ls0 = do - (preamble, ls1) <- parseSection "# Start configuration preamble" ls0 - (config, ls2) <- parseSection "# Start configuration registers" ls1 - (postamble, _) <- parseSection "# Start configuration postamble" ls2 - pure (preamble, config, postamble) - --- | Like 'parse', but reads from a file. -parseFromFile :: - FilePath -> - IO (Either String ([RegisterEntry], [RegisterEntry], [RegisterEntry])) -parseFromFile f = parse . map trim . lines <$> readFile f - -{- | Parse a CSV produced by ClockBuilder Pro into a 'Si539xRegisterMap' using -Template Haskell. --} -parseFromFileToRegisterMap :: FilePath -> Q Exp -parseFromFileToRegisterMap fileName = do - path <- liftIO $ getDataFileName ("data" "clock_configs" fileName <> ".csv") - entries <- liftIO $ parseFromFile path - case entries of - Left err -> fail err - Right (preamble, config, postamble) -> - [e| - Si539xRegisterMap - $(listToVecTH preamble) - $(listToVecTH config) - $(listToVecTH postamble) - |] diff --git a/bittide/src/Bittide/ClockControl/Registers.hs b/bittide/src/Bittide/ClockControl/Registers.hs deleted file mode 100644 index 6ee5a9c98..000000000 --- a/bittide/src/Bittide/ClockControl/Registers.hs +++ /dev/null @@ -1,84 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=6 #-} - -module Bittide.ClockControl.Registers where - -import Clash.Prelude - -import Protocols -import Protocols.Wishbone - -import Bittide.ClockControl -import Bittide.ClockControl.Callisto.Util (speedChangeToPins, stickyBits) -import Bittide.ClockControl.StabilityChecker -import Bittide.Wishbone -import Clash.Functor.Extra - -import Data.Maybe (fromMaybe) - -type StableBool = Bool -type SettledBool = Bool - -{- | A wishbone accessible clock control interface. -This interface receives the link mask and 'RelDataCount's from all links. -Furthermore it produces FINC/FDEC pulses for the clock control boards. - -The word-aligned address layout of the Wishbone interface is as follows: - -- Address 0: Number of links -- Address 1: Link mask -- Address 2: FINC/FDEC -- Address 3: Link stables -- Address 4: Link settles -- Addresses 5 to (5 + nLinks): Data counts --} -clockControlWb :: - forall dom addrW nLinks m margin framesize. - ( HiddenClockResetEnable dom - , KnownNat addrW - , 1 <= framesize - , 1 <= nLinks - , KnownNat nLinks - , KnownNat m - , m <= 32 - , nLinks <= 32 - ) => - -- | Maximum number of elements the incoming buffer occupancy is - -- allowed to deviate from the current @target@ for it to be - -- considered "stable". - SNat margin -> - -- | Minimum number of clock cycles the incoming buffer occupancy - -- must remain within the @margin@ for it to be considered "stable". - SNat framesize -> - -- | Link mask - Signal dom (BitVector nLinks) -> - -- | Counters - Vec nLinks (Signal dom (RelDataCount m)) -> - -- | Wishbone accessible clock control circuitry - Circuit - (Wishbone dom 'Standard addrW (BitVector 32)) - (CSignal dom ("FINC" ::: Bool, "FDEC" ::: Bool), CSignal dom ("ALL_STABLE" ::: Bool)) -clockControlWb margin framesize linkMask counters = Circuit go - where - go (wbM2S, _) = (wbS2M, (fIncDec3, all (== True) <$> (fmap stable <$> stabilityIndications))) - where - stabilityIndications = bundle $ stabilityChecker margin framesize <$> counters - readVec = - dflipflop - <$> ( pure (natToNum @nLinks) - :> (zeroExtend @_ @_ @(32 - nLinks) <$> linkMask) - :> (resize . pack <$> fIncDec1) - :> (resize . pack . fmap stable <$> stabilityIndications) - :> (resize . pack . fmap settled <$> stabilityIndications) - :> (pack . (extend @_ @_ @(32 - m)) <<$>> counters) - ) - fIncDec0 = (\v -> unpack . resize <$> v !! (2 :: Unsigned 2)) <$> writeVec - fIncDec1 = register Nothing fIncDec0 - fIncDec2 = fromMaybe NoChange <$> fIncDec1 - fIncDec3 = - delay minBound {- glitch filter -} - $ stickyBits d20 (speedChangeToPins <$> fIncDec2) - (writeVec, wbS2M) = unbundle $ wbToVec <$> bundle readVec <*> wbM2S diff --git a/bittide/src/Bittide/ClockControl/Si5391A.hs b/bittide/src/Bittide/ClockControl/Si5391A.hs deleted file mode 100644 index aae1c4901..000000000 --- a/bittide/src/Bittide/ClockControl/Si5391A.hs +++ /dev/null @@ -1,889 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} - -module Bittide.ClockControl.Si5391A where - -import Clash.Prelude - -import Bittide.ClockControl.Si539xSpi - -type Si5391ARegisterMap = Si539xRegisterMap 2 427 3 - --- TODO: Add convenience function to more easily create register maps with configurable --- clock frequencies at different outputs with certain divider configurations. - --- | Configuration for Si5391A with all output clocks disabled. -testConfigA :: Si5391ARegisterMap -testConfigA = Si539xRegisterMap{..} - where - configPreamble = (0x0B, 0x24, 0xC0) :> (0x0B, 0x25, 0x00) :> Nil - configPostamble = (0x00, 0x1C, 0x01) :> (0x0B, 0x24, 0xC3) :> (0x0B, 0x25, 0x02) :> Nil - config = - (0x00, 0x06, 0x00) - :> (0x00, 0x07, 0x00) - :> (0x00, 0x08, 0x00) - :> (0x00, 0x0B, 0x74) - :> (0x00, 0x17, 0xD0) - :> (0x00, 0x18, 0xFF) - :> (0x00, 0x21, 0x08) - :> (0x00, 0x22, 0x00) - :> (0x00, 0x2B, 0x02) - :> (0x00, 0x2C, 0x20) - :> (0x00, 0x2D, 0x00) - :> (0x00, 0x2E, 0x00) - :> (0x00, 0x2F, 0x00) - :> (0x00, 0x30, 0x00) - :> (0x00, 0x31, 0x00) - :> (0x00, 0x32, 0x00) - :> (0x00, 0x33, 0x00) - :> (0x00, 0x34, 0x00) - :> (0x00, 0x35, 0x00) - :> (0x00, 0x36, 0x00) - :> (0x00, 0x37, 0x00) - :> (0x00, 0x38, 0x00) - :> (0x00, 0x39, 0x00) - :> (0x00, 0x3A, 0x00) - :> (0x00, 0x3B, 0x00) - :> (0x00, 0x3C, 0x00) - :> (0x00, 0x3D, 0x00) - :> (0x00, 0x41, 0x00) - :> (0x00, 0x42, 0x00) - :> (0x00, 0x43, 0x00) - :> (0x00, 0x44, 0x00) - :> (0x00, 0x9E, 0x00) - :> (0x01, 0x02, 0x01) - :> (0x01, 0x03, 0x01) - :> (0x01, 0x04, 0x09) - :> (0x01, 0x05, 0x3E) - :> (0x01, 0x06, 0x18) - :> (0x01, 0x08, 0x01) - :> (0x01, 0x09, 0x09) - :> (0x01, 0x0A, 0x3B) - :> (0x01, 0x0B, 0x28) - :> (0x01, 0x0D, 0x01) - :> (0x01, 0x0E, 0xCC) - :> (0x01, 0x0F, 0x00) - :> (0x01, 0x10, 0x18) - :> (0x01, 0x12, 0x01) - :> (0x01, 0x13, 0x09) - :> (0x01, 0x14, 0x3B) - :> (0x01, 0x15, 0x28) - :> (0x01, 0x17, 0x01) - :> (0x01, 0x18, 0x09) - :> (0x01, 0x19, 0x3B) - :> (0x01, 0x1A, 0x28) - :> (0x01, 0x1C, 0x01) - :> (0x01, 0x1D, 0x09) - :> (0x01, 0x1E, 0x3B) - :> (0x01, 0x1F, 0x28) - :> (0x01, 0x21, 0x01) - :> (0x01, 0x22, 0x09) - :> (0x01, 0x23, 0x3B) - :> (0x01, 0x24, 0x28) - :> (0x01, 0x26, 0x01) - :> (0x01, 0x27, 0x09) - :> (0x01, 0x28, 0x3B) - :> (0x01, 0x29, 0x28) - :> (0x01, 0x2B, 0x01) - :> (0x01, 0x2C, 0x09) - :> (0x01, 0x2D, 0x3B) - :> (0x01, 0x2E, 0x28) - :> (0x01, 0x30, 0x01) - :> (0x01, 0x31, 0x09) - :> (0x01, 0x32, 0x3B) - :> (0x01, 0x33, 0x28) - :> (0x01, 0x35, 0x01) - :> (0x01, 0x36, 0x09) - :> (0x01, 0x37, 0x3B) - :> (0x01, 0x38, 0x28) - :> (0x01, 0x3A, 0x01) - :> (0x01, 0x3B, 0xCC) - :> (0x01, 0x3C, 0x00) - :> (0x01, 0x3D, 0x18) - :> (0x01, 0x3F, 0x00) - :> (0x01, 0x40, 0x00) - :> (0x01, 0x41, 0x40) - :> (0x02, 0x06, 0x00) - :> (0x02, 0x08, 0x00) - :> (0x02, 0x09, 0x00) - :> (0x02, 0x0A, 0x00) - :> (0x02, 0x0B, 0x00) - :> (0x02, 0x0C, 0x00) - :> (0x02, 0x0D, 0x00) - :> (0x02, 0x0E, 0x00) - :> (0x02, 0x0F, 0x00) - :> (0x02, 0x10, 0x00) - :> (0x02, 0x11, 0x00) - :> (0x02, 0x12, 0x00) - :> (0x02, 0x13, 0x00) - :> (0x02, 0x14, 0x00) - :> (0x02, 0x15, 0x00) - :> (0x02, 0x16, 0x00) - :> (0x02, 0x17, 0x00) - :> (0x02, 0x18, 0x00) - :> (0x02, 0x19, 0x00) - :> (0x02, 0x1A, 0x00) - :> (0x02, 0x1B, 0x00) - :> (0x02, 0x1C, 0x00) - :> (0x02, 0x1D, 0x00) - :> (0x02, 0x1E, 0x00) - :> (0x02, 0x1F, 0x00) - :> (0x02, 0x20, 0x00) - :> (0x02, 0x21, 0x00) - :> (0x02, 0x22, 0x00) - :> (0x02, 0x23, 0x00) - :> (0x02, 0x24, 0x00) - :> (0x02, 0x25, 0x00) - :> (0x02, 0x26, 0x00) - :> (0x02, 0x27, 0x00) - :> (0x02, 0x28, 0x00) - :> (0x02, 0x29, 0x00) - :> (0x02, 0x2A, 0x00) - :> (0x02, 0x2B, 0x00) - :> (0x02, 0x2C, 0x00) - :> (0x02, 0x2D, 0x00) - :> (0x02, 0x2E, 0x00) - :> (0x02, 0x2F, 0x00) - :> (0x02, 0x35, 0x00) - :> (0x02, 0x36, 0x00) - :> (0x02, 0x37, 0x00) - :> (0x02, 0x38, 0x00) - :> (0x02, 0x39, 0x00) - :> (0x02, 0x3A, 0x00) - :> (0x02, 0x3B, 0x00) - :> (0x02, 0x3C, 0x00) - :> (0x02, 0x3D, 0x00) - :> (0x02, 0x3E, 0x00) - :> (0x02, 0x47, 0x00) - :> (0x02, 0x48, 0x00) - :> (0x02, 0x49, 0x00) - :> (0x02, 0x4A, 0x00) - :> (0x02, 0x4B, 0x00) - :> (0x02, 0x4C, 0x00) - :> (0x02, 0x4D, 0x00) - :> (0x02, 0x4E, 0x00) - :> (0x02, 0x4F, 0x00) - :> (0x02, 0x50, 0x00) - :> (0x02, 0x51, 0x00) - :> (0x02, 0x52, 0x00) - :> (0x02, 0x53, 0x00) - :> (0x02, 0x54, 0x00) - :> (0x02, 0x55, 0x00) - :> (0x02, 0x56, 0x00) - :> (0x02, 0x57, 0x00) - :> (0x02, 0x58, 0x00) - :> (0x02, 0x59, 0x00) - :> (0x02, 0x5A, 0x00) - :> (0x02, 0x5B, 0x00) - :> (0x02, 0x5C, 0x00) - :> (0x02, 0x5D, 0x00) - :> (0x02, 0x5E, 0x00) - :> (0x02, 0x5F, 0x00) - :> (0x02, 0x60, 0x00) - :> (0x02, 0x61, 0x00) - :> (0x02, 0x62, 0x00) - :> (0x02, 0x63, 0x00) - :> (0x02, 0x64, 0x00) - :> (0x02, 0x65, 0x00) - :> (0x02, 0x66, 0x00) - :> (0x02, 0x67, 0x00) - :> (0x02, 0x68, 0x00) - :> (0x02, 0x69, 0x00) - :> (0x02, 0x6A, 0x00) - :> (0x02, 0x6B, 0x35) - :> (0x02, 0x6C, 0x33) - :> (0x02, 0x6D, 0x39) - :> (0x02, 0x6E, 0x31) - :> (0x02, 0x6F, 0x41) - :> (0x02, 0x70, 0x45) - :> (0x02, 0x71, 0x56) - :> (0x02, 0x72, 0x42) - :> (0x03, 0x02, 0x00) - :> (0x03, 0x03, 0x00) - :> (0x03, 0x04, 0x00) - :> (0x03, 0x05, 0x00) - :> (0x03, 0x06, 0x00) - :> (0x03, 0x07, 0x00) - :> (0x03, 0x08, 0x00) - :> (0x03, 0x09, 0x00) - :> (0x03, 0x0A, 0x00) - :> (0x03, 0x0B, 0x00) - :> (0x03, 0x0C, 0x00) - :> (0x03, 0x0D, 0x00) - :> (0x03, 0x0E, 0x00) - :> (0x03, 0x0F, 0x00) - :> (0x03, 0x10, 0x00) - :> (0x03, 0x11, 0x00) - :> (0x03, 0x12, 0x00) - :> (0x03, 0x13, 0x00) - :> (0x03, 0x14, 0x00) - :> (0x03, 0x15, 0x00) - :> (0x03, 0x16, 0x00) - :> (0x03, 0x17, 0x00) - :> (0x03, 0x18, 0x00) - :> (0x03, 0x19, 0x00) - :> (0x03, 0x1A, 0x00) - :> (0x03, 0x1B, 0x00) - :> (0x03, 0x1C, 0x00) - :> (0x03, 0x1D, 0x00) - :> (0x03, 0x1E, 0x00) - :> (0x03, 0x1F, 0x00) - :> (0x03, 0x20, 0x00) - :> (0x03, 0x21, 0x00) - :> (0x03, 0x22, 0x00) - :> (0x03, 0x23, 0x00) - :> (0x03, 0x24, 0x00) - :> (0x03, 0x25, 0x00) - :> (0x03, 0x26, 0x00) - :> (0x03, 0x27, 0x00) - :> (0x03, 0x28, 0x00) - :> (0x03, 0x29, 0x00) - :> (0x03, 0x2A, 0x00) - :> (0x03, 0x2B, 0x00) - :> (0x03, 0x2C, 0x00) - :> (0x03, 0x2D, 0x00) - :> (0x03, 0x2E, 0x00) - :> (0x03, 0x2F, 0x00) - :> (0x03, 0x30, 0x00) - :> (0x03, 0x31, 0x00) - :> (0x03, 0x32, 0x00) - :> (0x03, 0x33, 0x00) - :> (0x03, 0x34, 0x00) - :> (0x03, 0x35, 0x00) - :> (0x03, 0x36, 0x00) - :> (0x03, 0x37, 0x00) - :> (0x03, 0x38, 0x00) - :> (0x03, 0x39, 0x1F) - :> (0x03, 0x3B, 0x00) - :> (0x03, 0x3C, 0x00) - :> (0x03, 0x3D, 0x00) - :> (0x03, 0x3E, 0x00) - :> (0x03, 0x3F, 0x00) - :> (0x03, 0x40, 0x00) - :> (0x03, 0x41, 0x00) - :> (0x03, 0x42, 0x00) - :> (0x03, 0x43, 0x00) - :> (0x03, 0x44, 0x00) - :> (0x03, 0x45, 0x00) - :> (0x03, 0x46, 0x00) - :> (0x03, 0x47, 0x00) - :> (0x03, 0x48, 0x00) - :> (0x03, 0x49, 0x00) - :> (0x03, 0x4A, 0x00) - :> (0x03, 0x4B, 0x00) - :> (0x03, 0x4C, 0x00) - :> (0x03, 0x4D, 0x00) - :> (0x03, 0x4E, 0x00) - :> (0x03, 0x4F, 0x00) - :> (0x03, 0x50, 0x00) - :> (0x03, 0x51, 0x00) - :> (0x03, 0x52, 0x00) - :> (0x03, 0x53, 0x00) - :> (0x03, 0x54, 0x00) - :> (0x03, 0x55, 0x00) - :> (0x03, 0x56, 0x00) - :> (0x03, 0x57, 0x00) - :> (0x03, 0x58, 0x00) - :> (0x03, 0x59, 0x00) - :> (0x03, 0x5A, 0x00) - :> (0x03, 0x5B, 0x00) - :> (0x03, 0x5C, 0x00) - :> (0x03, 0x5D, 0x00) - :> (0x03, 0x5E, 0x00) - :> (0x03, 0x5F, 0x00) - :> (0x03, 0x60, 0x00) - :> (0x03, 0x61, 0x00) - :> (0x03, 0x62, 0x00) - :> (0x08, 0x02, 0x00) - :> (0x08, 0x03, 0x00) - :> (0x08, 0x04, 0x00) - :> (0x08, 0x05, 0x00) - :> (0x08, 0x06, 0x00) - :> (0x08, 0x07, 0x00) - :> (0x08, 0x08, 0x00) - :> (0x08, 0x09, 0x00) - :> (0x08, 0x0A, 0x00) - :> (0x08, 0x0B, 0x00) - :> (0x08, 0x0C, 0x00) - :> (0x08, 0x0D, 0x00) - :> (0x08, 0x0E, 0x00) - :> (0x08, 0x0F, 0x00) - :> (0x08, 0x10, 0x00) - :> (0x08, 0x11, 0x00) - :> (0x08, 0x12, 0x00) - :> (0x08, 0x13, 0x00) - :> (0x08, 0x14, 0x00) - :> (0x08, 0x15, 0x00) - :> (0x08, 0x16, 0x00) - :> (0x08, 0x17, 0x00) - :> (0x08, 0x18, 0x00) - :> (0x08, 0x19, 0x00) - :> (0x08, 0x1A, 0x00) - :> (0x08, 0x1B, 0x00) - :> (0x08, 0x1C, 0x00) - :> (0x08, 0x1D, 0x00) - :> (0x08, 0x1E, 0x00) - :> (0x08, 0x1F, 0x00) - :> (0x08, 0x20, 0x00) - :> (0x08, 0x21, 0x00) - :> (0x08, 0x22, 0x00) - :> (0x08, 0x23, 0x00) - :> (0x08, 0x24, 0x00) - :> (0x08, 0x25, 0x00) - :> (0x08, 0x26, 0x00) - :> (0x08, 0x27, 0x00) - :> (0x08, 0x28, 0x00) - :> (0x08, 0x29, 0x00) - :> (0x08, 0x2A, 0x00) - :> (0x08, 0x2B, 0x00) - :> (0x08, 0x2C, 0x00) - :> (0x08, 0x2D, 0x00) - :> (0x08, 0x2E, 0x00) - :> (0x08, 0x2F, 0x00) - :> (0x08, 0x30, 0x00) - :> (0x08, 0x31, 0x00) - :> (0x08, 0x32, 0x00) - :> (0x08, 0x33, 0x00) - :> (0x08, 0x34, 0x00) - :> (0x08, 0x35, 0x00) - :> (0x08, 0x36, 0x00) - :> (0x08, 0x37, 0x00) - :> (0x08, 0x38, 0x00) - :> (0x08, 0x39, 0x00) - :> (0x08, 0x3A, 0x00) - :> (0x08, 0x3B, 0x00) - :> (0x08, 0x3C, 0x00) - :> (0x08, 0x3D, 0x00) - :> (0x08, 0x3E, 0x00) - :> (0x08, 0x3F, 0x00) - :> (0x08, 0x40, 0x00) - :> (0x08, 0x41, 0x00) - :> (0x08, 0x42, 0x00) - :> (0x08, 0x43, 0x00) - :> (0x08, 0x44, 0x00) - :> (0x08, 0x45, 0x00) - :> (0x08, 0x46, 0x00) - :> (0x08, 0x47, 0x00) - :> (0x08, 0x48, 0x00) - :> (0x08, 0x49, 0x00) - :> (0x08, 0x4A, 0x00) - :> (0x08, 0x4B, 0x00) - :> (0x08, 0x4C, 0x00) - :> (0x08, 0x4D, 0x00) - :> (0x08, 0x4E, 0x00) - :> (0x08, 0x4F, 0x00) - :> (0x08, 0x50, 0x00) - :> (0x08, 0x51, 0x00) - :> (0x08, 0x52, 0x00) - :> (0x08, 0x53, 0x00) - :> (0x08, 0x54, 0x00) - :> (0x08, 0x55, 0x00) - :> (0x08, 0x56, 0x00) - :> (0x08, 0x57, 0x00) - :> (0x08, 0x58, 0x00) - :> (0x08, 0x59, 0x00) - :> (0x08, 0x5A, 0x00) - :> (0x08, 0x5B, 0x00) - :> (0x08, 0x5C, 0x00) - :> (0x08, 0x5D, 0x00) - :> (0x08, 0x5E, 0x00) - :> (0x08, 0x5F, 0x00) - :> (0x08, 0x60, 0x00) - :> (0x08, 0x61, 0x00) - :> (0x09, 0x0E, 0x02) - :> (0x09, 0x1C, 0x04) - :> (0x09, 0x43, 0x01) - :> (0x09, 0x49, 0x00) - :> (0x09, 0x4A, 0xF0) - :> (0x09, 0x4E, 0x49) - :> (0x09, 0x4F, 0xF2) - :> (0x09, 0x5E, 0x00) - :> (0x0A, 0x02, 0x00) - :> (0x0A, 0x03, 0x1F) - :> (0x0A, 0x04, 0x00) - :> (0x0A, 0x05, 0x1F) - :> (0x0A, 0x14, 0x00) - :> (0x0A, 0x1A, 0x00) - :> (0x0A, 0x20, 0x00) - :> (0x0A, 0x26, 0x00) - :> (0x0A, 0x2C, 0x00) - :> (0x0A, 0x38, 0x00) - :> (0x0A, 0x39, 0x00) - :> (0x0A, 0x3A, 0x00) - :> (0x0A, 0x3C, 0x00) - :> (0x0A, 0x3D, 0x00) - :> (0x0A, 0x3E, 0x00) - :> (0x0A, 0x40, 0x00) - :> (0x0A, 0x41, 0x00) - :> (0x0A, 0x42, 0x00) - :> (0x0A, 0x44, 0x00) - :> (0x0A, 0x45, 0x00) - :> (0x0A, 0x46, 0x00) - :> (0x0A, 0x48, 0x00) - :> (0x0A, 0x49, 0x00) - :> (0x0A, 0x4A, 0x00) - :> (0x0A, 0x4C, 0x00) - :> (0x0A, 0x4D, 0x00) - :> (0x0A, 0x4E, 0x00) - :> (0x0A, 0x4F, 0x00) - :> (0x0A, 0x50, 0x00) - :> (0x0A, 0x51, 0x00) - :> (0x0A, 0x52, 0x00) - :> (0x0A, 0x53, 0x00) - :> (0x0A, 0x54, 0x00) - :> (0x0A, 0x55, 0x00) - :> (0x0A, 0x56, 0x00) - :> (0x0A, 0x57, 0x00) - :> (0x0A, 0x58, 0x00) - :> (0x0A, 0x59, 0x00) - :> (0x0A, 0x5A, 0x00) - :> (0x0A, 0x5B, 0x00) - :> (0x0A, 0x5C, 0x00) - :> (0x0A, 0x5D, 0x00) - :> (0x0A, 0x5E, 0x00) - :> (0x0A, 0x5F, 0x00) - :> (0x0B, 0x44, 0x00) - :> (0x0B, 0x4A, 0x00) - :> (0x0B, 0x57, 0x0E) - :> (0x0B, 0x58, 0x01) - :> Nil - -{- | Configuration for Si5391A with all output clocks enabled at 200MHz and routed through -divider N0. --} -testConfigB :: Si5391ARegisterMap -testConfigB = Si539xRegisterMap{..} - where - configPreamble = (0x0B, 0x24, 0xC0) :> (0x0B, 0x25, 0x00) :> Nil - configPostamble = (0x00, 0x1C, 0x01) :> (0x0B, 0x24, 0xC3) :> (0x0B, 0x25, 0x02) :> Nil - config = - (0x00, 0x06, 0x00) - :> (0x00, 0x07, 0x00) - :> (0x00, 0x08, 0x00) - :> (0x00, 0x0B, 0x74) - :> (0x00, 0x17, 0xD0) - :> (0x00, 0x18, 0xFF) - :> (0x00, 0x21, 0x0F) - :> (0x00, 0x22, 0x00) - :> (0x00, 0x2B, 0x02) - :> (0x00, 0x2C, 0x20) - :> (0x00, 0x2D, 0x00) - :> (0x00, 0x2E, 0x00) - :> (0x00, 0x2F, 0x00) - :> (0x00, 0x30, 0x00) - :> (0x00, 0x31, 0x00) - :> (0x00, 0x32, 0x00) - :> (0x00, 0x33, 0x00) - :> (0x00, 0x34, 0x00) - :> (0x00, 0x35, 0x00) - :> (0x00, 0x36, 0x00) - :> (0x00, 0x37, 0x00) - :> (0x00, 0x38, 0x00) - :> (0x00, 0x39, 0x00) - :> (0x00, 0x3A, 0x00) - :> (0x00, 0x3B, 0x00) - :> (0x00, 0x3C, 0x00) - :> (0x00, 0x3D, 0x00) - :> (0x00, 0x41, 0x00) - :> (0x00, 0x42, 0x00) - :> (0x00, 0x43, 0x00) - :> (0x00, 0x44, 0x00) - :> (0x00, 0x9E, 0x00) - :> (0x01, 0x02, 0x01) - :> (0x01, 0x03, 0x06) - :> (0x01, 0x04, 0x09) - :> (0x01, 0x05, 0x3E) - :> (0x01, 0x06, 0x18) - :> (0x01, 0x08, 0x06) - :> (0x01, 0x09, 0x09) - :> (0x01, 0x0A, 0x3E) - :> (0x01, 0x0B, 0x18) - :> (0x01, 0x0D, 0x06) - :> (0x01, 0x0E, 0x09) - :> (0x01, 0x0F, 0x3E) - :> (0x01, 0x10, 0x18) - :> (0x01, 0x12, 0x06) - :> (0x01, 0x13, 0x09) - :> (0x01, 0x14, 0x3E) - :> (0x01, 0x15, 0x18) - :> (0x01, 0x17, 0x06) - :> (0x01, 0x18, 0x09) - :> (0x01, 0x19, 0x3E) - :> (0x01, 0x1A, 0x18) - :> (0x01, 0x1C, 0x06) - :> (0x01, 0x1D, 0x09) - :> (0x01, 0x1E, 0x3E) - :> (0x01, 0x1F, 0x18) - :> (0x01, 0x21, 0x06) - :> (0x01, 0x22, 0x09) - :> (0x01, 0x23, 0x3E) - :> (0x01, 0x24, 0x18) - :> (0x01, 0x26, 0x06) - :> (0x01, 0x27, 0x09) - :> (0x01, 0x28, 0x3E) - :> (0x01, 0x29, 0x18) - :> (0x01, 0x2B, 0x06) - :> (0x01, 0x2C, 0x09) - :> (0x01, 0x2D, 0x3E) - :> (0x01, 0x2E, 0x18) - :> (0x01, 0x30, 0x06) - :> (0x01, 0x31, 0x09) - :> (0x01, 0x32, 0x3E) - :> (0x01, 0x33, 0x18) - :> (0x01, 0x35, 0x06) - :> (0x01, 0x36, 0x09) - :> (0x01, 0x37, 0x3E) - :> (0x01, 0x38, 0x18) - :> (0x01, 0x3A, 0x06) - :> (0x01, 0x3B, 0x09) - :> (0x01, 0x3C, 0x3E) - :> (0x01, 0x3D, 0x18) - :> (0x01, 0x3F, 0x00) - :> (0x01, 0x40, 0x00) - :> (0x01, 0x41, 0x40) - :> (0x02, 0x06, 0x00) - :> (0x02, 0x08, 0x00) - :> (0x02, 0x09, 0x00) - :> (0x02, 0x0A, 0x00) - :> (0x02, 0x0B, 0x00) - :> (0x02, 0x0C, 0x00) - :> (0x02, 0x0D, 0x00) - :> (0x02, 0x0E, 0x00) - :> (0x02, 0x0F, 0x00) - :> (0x02, 0x10, 0x00) - :> (0x02, 0x11, 0x00) - :> (0x02, 0x12, 0x00) - :> (0x02, 0x13, 0x00) - :> (0x02, 0x14, 0x00) - :> (0x02, 0x15, 0x00) - :> (0x02, 0x16, 0x00) - :> (0x02, 0x17, 0x00) - :> (0x02, 0x18, 0x00) - :> (0x02, 0x19, 0x00) - :> (0x02, 0x1A, 0x00) - :> (0x02, 0x1B, 0x00) - :> (0x02, 0x1C, 0x00) - :> (0x02, 0x1D, 0x00) - :> (0x02, 0x1E, 0x00) - :> (0x02, 0x1F, 0x00) - :> (0x02, 0x20, 0x00) - :> (0x02, 0x21, 0x00) - :> (0x02, 0x22, 0x00) - :> (0x02, 0x23, 0x00) - :> (0x02, 0x24, 0x00) - :> (0x02, 0x25, 0x00) - :> (0x02, 0x26, 0x00) - :> (0x02, 0x27, 0x00) - :> (0x02, 0x28, 0x00) - :> (0x02, 0x29, 0x00) - :> (0x02, 0x2A, 0x00) - :> (0x02, 0x2B, 0x00) - :> (0x02, 0x2C, 0x00) - :> (0x02, 0x2D, 0x00) - :> (0x02, 0x2E, 0x00) - :> (0x02, 0x2F, 0x00) - :> (0x02, 0x35, 0x00) - :> (0x02, 0x36, 0x00) - :> (0x02, 0x37, 0x00) - :> (0x02, 0x38, 0xC0) - :> (0x02, 0x39, 0x89) - :> (0x02, 0x3A, 0x00) - :> (0x02, 0x3B, 0x00) - :> (0x02, 0x3C, 0x00) - :> (0x02, 0x3D, 0x00) - :> (0x02, 0x3E, 0x80) - :> (0x02, 0x47, 0x00) - :> (0x02, 0x48, 0x00) - :> (0x02, 0x49, 0x00) - :> (0x02, 0x4A, 0x00) - :> (0x02, 0x4B, 0x00) - :> (0x02, 0x4C, 0x00) - :> (0x02, 0x4D, 0x00) - :> (0x02, 0x4E, 0x00) - :> (0x02, 0x4F, 0x00) - :> (0x02, 0x50, 0x00) - :> (0x02, 0x51, 0x00) - :> (0x02, 0x52, 0x00) - :> (0x02, 0x53, 0x00) - :> (0x02, 0x54, 0x00) - :> (0x02, 0x55, 0x00) - :> (0x02, 0x56, 0x00) - :> (0x02, 0x57, 0x00) - :> (0x02, 0x58, 0x00) - :> (0x02, 0x59, 0x00) - :> (0x02, 0x5A, 0x00) - :> (0x02, 0x5B, 0x00) - :> (0x02, 0x5C, 0x00) - :> (0x02, 0x5D, 0x00) - :> (0x02, 0x5E, 0x00) - :> (0x02, 0x5F, 0x00) - :> (0x02, 0x60, 0x00) - :> (0x02, 0x61, 0x00) - :> (0x02, 0x62, 0x00) - :> (0x02, 0x63, 0x00) - :> (0x02, 0x64, 0x00) - :> (0x02, 0x65, 0x00) - :> (0x02, 0x66, 0x00) - :> (0x02, 0x67, 0x00) - :> (0x02, 0x68, 0x00) - :> (0x02, 0x69, 0x00) - :> (0x02, 0x6A, 0x00) - :> (0x02, 0x6B, 0x35) - :> (0x02, 0x6C, 0x33) - :> (0x02, 0x6D, 0x39) - :> (0x02, 0x6E, 0x31) - :> (0x02, 0x6F, 0x41) - :> (0x02, 0x70, 0x45) - :> (0x02, 0x71, 0x56) - :> (0x02, 0x72, 0x42) - :> (0x03, 0x02, 0x00) - :> (0x03, 0x03, 0x00) - :> (0x03, 0x04, 0x00) - :> (0x03, 0x05, 0xD4) - :> (0x03, 0x06, 0x19) - :> (0x03, 0x07, 0x00) - :> (0x03, 0x08, 0x00) - :> (0x03, 0x09, 0x00) - :> (0x03, 0x0A, 0x00) - :> (0x03, 0x0B, 0xC8) - :> (0x03, 0x0C, 0x00) - :> (0x03, 0x0D, 0x00) - :> (0x03, 0x0E, 0x00) - :> (0x03, 0x0F, 0x00) - :> (0x03, 0x10, 0x00) - :> (0x03, 0x11, 0x00) - :> (0x03, 0x12, 0x00) - :> (0x03, 0x13, 0x00) - :> (0x03, 0x14, 0x00) - :> (0x03, 0x15, 0x00) - :> (0x03, 0x16, 0x00) - :> (0x03, 0x17, 0x00) - :> (0x03, 0x18, 0x00) - :> (0x03, 0x19, 0x00) - :> (0x03, 0x1A, 0x00) - :> (0x03, 0x1B, 0x00) - :> (0x03, 0x1C, 0x00) - :> (0x03, 0x1D, 0x00) - :> (0x03, 0x1E, 0x00) - :> (0x03, 0x1F, 0x00) - :> (0x03, 0x20, 0x00) - :> (0x03, 0x21, 0x00) - :> (0x03, 0x22, 0x00) - :> (0x03, 0x23, 0x00) - :> (0x03, 0x24, 0x00) - :> (0x03, 0x25, 0x00) - :> (0x03, 0x26, 0x00) - :> (0x03, 0x27, 0x00) - :> (0x03, 0x28, 0x00) - :> (0x03, 0x29, 0x00) - :> (0x03, 0x2A, 0x00) - :> (0x03, 0x2B, 0x00) - :> (0x03, 0x2C, 0x00) - :> (0x03, 0x2D, 0x00) - :> (0x03, 0x2E, 0x00) - :> (0x03, 0x2F, 0x00) - :> (0x03, 0x30, 0x00) - :> (0x03, 0x31, 0x00) - :> (0x03, 0x32, 0x00) - :> (0x03, 0x33, 0x00) - :> (0x03, 0x34, 0x00) - :> (0x03, 0x35, 0x00) - :> (0x03, 0x36, 0x00) - :> (0x03, 0x37, 0x00) - :> (0x03, 0x38, 0x00) - :> (0x03, 0x39, 0x1E) - :> (0x03, 0x3B, 0x53) - :> (0x03, 0x3C, 0xB1) - :> (0x03, 0x3D, 0x01) - :> (0x03, 0x3E, 0x00) - :> (0x03, 0x3F, 0x00) - :> (0x03, 0x40, 0x00) - :> (0x03, 0x41, 0x00) - :> (0x03, 0x42, 0x00) - :> (0x03, 0x43, 0x00) - :> (0x03, 0x44, 0x00) - :> (0x03, 0x45, 0x00) - :> (0x03, 0x46, 0x00) - :> (0x03, 0x47, 0x00) - :> (0x03, 0x48, 0x00) - :> (0x03, 0x49, 0x00) - :> (0x03, 0x4A, 0x00) - :> (0x03, 0x4B, 0x00) - :> (0x03, 0x4C, 0x00) - :> (0x03, 0x4D, 0x00) - :> (0x03, 0x4E, 0x00) - :> (0x03, 0x4F, 0x00) - :> (0x03, 0x50, 0x00) - :> (0x03, 0x51, 0x00) - :> (0x03, 0x52, 0x00) - :> (0x03, 0x53, 0x00) - :> (0x03, 0x54, 0x00) - :> (0x03, 0x55, 0x00) - :> (0x03, 0x56, 0x00) - :> (0x03, 0x57, 0x00) - :> (0x03, 0x58, 0x00) - :> (0x03, 0x59, 0x00) - :> (0x03, 0x5A, 0x00) - :> (0x03, 0x5B, 0x00) - :> (0x03, 0x5C, 0x00) - :> (0x03, 0x5D, 0x00) - :> (0x03, 0x5E, 0x00) - :> (0x03, 0x5F, 0x00) - :> (0x03, 0x60, 0x00) - :> (0x03, 0x61, 0x00) - :> (0x03, 0x62, 0x00) - :> (0x08, 0x02, 0x00) - :> (0x08, 0x03, 0x00) - :> (0x08, 0x04, 0x00) - :> (0x08, 0x05, 0x00) - :> (0x08, 0x06, 0x00) - :> (0x08, 0x07, 0x00) - :> (0x08, 0x08, 0x00) - :> (0x08, 0x09, 0x00) - :> (0x08, 0x0A, 0x00) - :> (0x08, 0x0B, 0x00) - :> (0x08, 0x0C, 0x00) - :> (0x08, 0x0D, 0x00) - :> (0x08, 0x0E, 0x00) - :> (0x08, 0x0F, 0x00) - :> (0x08, 0x10, 0x00) - :> (0x08, 0x11, 0x00) - :> (0x08, 0x12, 0x00) - :> (0x08, 0x13, 0x00) - :> (0x08, 0x14, 0x00) - :> (0x08, 0x15, 0x00) - :> (0x08, 0x16, 0x00) - :> (0x08, 0x17, 0x00) - :> (0x08, 0x18, 0x00) - :> (0x08, 0x19, 0x00) - :> (0x08, 0x1A, 0x00) - :> (0x08, 0x1B, 0x00) - :> (0x08, 0x1C, 0x00) - :> (0x08, 0x1D, 0x00) - :> (0x08, 0x1E, 0x00) - :> (0x08, 0x1F, 0x00) - :> (0x08, 0x20, 0x00) - :> (0x08, 0x21, 0x00) - :> (0x08, 0x22, 0x00) - :> (0x08, 0x23, 0x00) - :> (0x08, 0x24, 0x00) - :> (0x08, 0x25, 0x00) - :> (0x08, 0x26, 0x00) - :> (0x08, 0x27, 0x00) - :> (0x08, 0x28, 0x00) - :> (0x08, 0x29, 0x00) - :> (0x08, 0x2A, 0x00) - :> (0x08, 0x2B, 0x00) - :> (0x08, 0x2C, 0x00) - :> (0x08, 0x2D, 0x00) - :> (0x08, 0x2E, 0x00) - :> (0x08, 0x2F, 0x00) - :> (0x08, 0x30, 0x00) - :> (0x08, 0x31, 0x00) - :> (0x08, 0x32, 0x00) - :> (0x08, 0x33, 0x00) - :> (0x08, 0x34, 0x00) - :> (0x08, 0x35, 0x00) - :> (0x08, 0x36, 0x00) - :> (0x08, 0x37, 0x00) - :> (0x08, 0x38, 0x00) - :> (0x08, 0x39, 0x00) - :> (0x08, 0x3A, 0x00) - :> (0x08, 0x3B, 0x00) - :> (0x08, 0x3C, 0x00) - :> (0x08, 0x3D, 0x00) - :> (0x08, 0x3E, 0x00) - :> (0x08, 0x3F, 0x00) - :> (0x08, 0x40, 0x00) - :> (0x08, 0x41, 0x00) - :> (0x08, 0x42, 0x00) - :> (0x08, 0x43, 0x00) - :> (0x08, 0x44, 0x00) - :> (0x08, 0x45, 0x00) - :> (0x08, 0x46, 0x00) - :> (0x08, 0x47, 0x00) - :> (0x08, 0x48, 0x00) - :> (0x08, 0x49, 0x00) - :> (0x08, 0x4A, 0x00) - :> (0x08, 0x4B, 0x00) - :> (0x08, 0x4C, 0x00) - :> (0x08, 0x4D, 0x00) - :> (0x08, 0x4E, 0x00) - :> (0x08, 0x4F, 0x00) - :> (0x08, 0x50, 0x00) - :> (0x08, 0x51, 0x00) - :> (0x08, 0x52, 0x00) - :> (0x08, 0x53, 0x00) - :> (0x08, 0x54, 0x00) - :> (0x08, 0x55, 0x00) - :> (0x08, 0x56, 0x00) - :> (0x08, 0x57, 0x00) - :> (0x08, 0x58, 0x00) - :> (0x08, 0x59, 0x00) - :> (0x08, 0x5A, 0x00) - :> (0x08, 0x5B, 0x00) - :> (0x08, 0x5C, 0x00) - :> (0x08, 0x5D, 0x00) - :> (0x08, 0x5E, 0x00) - :> (0x08, 0x5F, 0x00) - :> (0x08, 0x60, 0x00) - :> (0x08, 0x61, 0x00) - :> (0x09, 0x0E, 0x02) - :> (0x09, 0x1C, 0x04) - :> (0x09, 0x43, 0x01) - :> (0x09, 0x49, 0x00) - :> (0x09, 0x4A, 0x00) - :> (0x09, 0x4E, 0x49) - :> (0x09, 0x4F, 0xF2) - :> (0x09, 0x5E, 0x00) - :> (0x0A, 0x02, 0x00) - :> (0x0A, 0x03, 0x01) - :> (0x0A, 0x04, 0x00) - :> (0x0A, 0x05, 0x01) - :> (0x0A, 0x14, 0x00) - :> (0x0A, 0x1A, 0x00) - :> (0x0A, 0x20, 0x00) - :> (0x0A, 0x26, 0x00) - :> (0x0A, 0x2C, 0x00) - :> (0x0A, 0x38, 0x00) - :> (0x0A, 0x39, 0x00) - :> (0x0A, 0x3A, 0x00) - :> (0x0A, 0x3C, 0x00) - :> (0x0A, 0x3D, 0x00) - :> (0x0A, 0x3E, 0x00) - :> (0x0A, 0x40, 0x00) - :> (0x0A, 0x41, 0x00) - :> (0x0A, 0x42, 0x00) - :> (0x0A, 0x44, 0x00) - :> (0x0A, 0x45, 0x00) - :> (0x0A, 0x46, 0x00) - :> (0x0A, 0x48, 0x00) - :> (0x0A, 0x49, 0x00) - :> (0x0A, 0x4A, 0x00) - :> (0x0A, 0x4C, 0x00) - :> (0x0A, 0x4D, 0x00) - :> (0x0A, 0x4E, 0x00) - :> (0x0A, 0x4F, 0x00) - :> (0x0A, 0x50, 0x00) - :> (0x0A, 0x51, 0x00) - :> (0x0A, 0x52, 0x00) - :> (0x0A, 0x53, 0x00) - :> (0x0A, 0x54, 0x00) - :> (0x0A, 0x55, 0x00) - :> (0x0A, 0x56, 0x00) - :> (0x0A, 0x57, 0x00) - :> (0x0A, 0x58, 0x00) - :> (0x0A, 0x59, 0x00) - :> (0x0A, 0x5A, 0x00) - :> (0x0A, 0x5B, 0x00) - :> (0x0A, 0x5C, 0x00) - :> (0x0A, 0x5D, 0x00) - :> (0x0A, 0x5E, 0x00) - :> (0x0A, 0x5F, 0x00) - :> (0x0B, 0x44, 0x0F) - :> (0x0B, 0x4A, 0x1E) - :> (0x0B, 0x57, 0x0E) - :> (0x0B, 0x58, 0x01) - :> Nil diff --git a/bittide/src/Bittide/ClockControl/Si5395J.hs b/bittide/src/Bittide/ClockControl/Si5395J.hs deleted file mode 100644 index 6c90402ba..000000000 --- a/bittide/src/Bittide/ClockControl/Si5395J.hs +++ /dev/null @@ -1,52 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE RecordWildCards #-} - -module Bittide.ClockControl.Si5395J where - -import Bittide.ClockControl.ParseRegisters -import Bittide.ClockControl.Si539xSpi -import Clash.Prelude - -type Si5395RegisterMap = Si539xRegisterMap 3 584 5 - -type TestConfig6_200_on_0a_RegisterMap = Si539xRegisterMap 3 590 5 -type TestConfig6_200_on_0a_TotalRegs = 3 + 590 + 5 - -{- | Configuration for Si5395J with the following configuration: - - out0a: 200MHz LVDS 1.8V - out9: 12.5MHz LVDS 1.8V - out9a: 200MHz LVDS 1.8V - - all of them doing 1ppb steps on Finc/Fdec --} -testConfig6_200_on_0a_1ppb :: TestConfig6_200_on_0a_RegisterMap -testConfig6_200_on_0a_1ppb = $(parseFromFileToRegisterMap "Si5395J-200MHz-1ppb-Registers") - --- | Same as 'testConfig6_200_on_0a_1ppb', but with a 10 ppb step size -testConfig6_200_on_0a_10ppb :: TestConfig6_200_on_0a_RegisterMap -testConfig6_200_on_0a_10ppb = $(parseFromFileToRegisterMap "Si5395J-200MHz-10ppb-Registers") - --- | Same as 'testConfig6_200_on_0a_1ppb', but with a 100 ppb step size -testConfig6_200_on_0a_100ppb :: TestConfig6_200_on_0a_RegisterMap -testConfig6_200_on_0a_100ppb = $(parseFromFileToRegisterMap "Si5395J-200MHz-100ppb-Registers") - --- | Same as 'testConfig6_200_on_0a_1ppb', but with a 500 ppb step size -testConfig6_200_on_0a_500ppb :: TestConfig6_200_on_0a_RegisterMap -testConfig6_200_on_0a_500ppb = $(parseFromFileToRegisterMap "Si5395J-200MHz-500ppb-Registers") - --- | Same as 'testConfig6_200_on_0a_1ppb', but with a 1 ppm step size -testConfig6_200_on_0a_1ppm :: TestConfig6_200_on_0a_RegisterMap -testConfig6_200_on_0a_1ppm = $(parseFromFileToRegisterMap "Si5395J-200MHz-1ppm-Registers") - -{- | Configuration for Si5395J with - out0a: 200MHz LVDS 1.8V connected to GTH SMA clk input (clk0 on quad 226) - out1: 200MHz LVDS 1.8V connected to User SMA clk input on node 7 only - out9: 20MHZ LVDS 1.8V - out9a: 200MHz LVDS 1.8V - all of them doing 10ppb steps on Finc/Fdec --} -testConfig6_200_on_0a_10ppb_and_1 :: Si5395RegisterMap -testConfig6_200_on_0a_10ppb_and_1 = $(parseFromFileToRegisterMap "Si5395J-200MHz-10ppb-and-out1") diff --git a/bittide/src/Bittide/ClockControl/Si539xSpi.hs b/bittide/src/Bittide/ClockControl/Si539xSpi.hs deleted file mode 100644 index d831e21c9..000000000 --- a/bittide/src/Bittide/ClockControl/Si539xSpi.hs +++ /dev/null @@ -1,438 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=15 #-} - -module Bittide.ClockControl.Si539xSpi where - -import Clash.Cores.SPI -import Clash.Prelude hiding (PeriodToCycles) - -import Data.Maybe - -import Bittide.Arithmetic.Time -import Bittide.ClockControl -import Bittide.Extra.Maybe -import Bittide.SharedTypes - -import Clash.Cores.Xilinx.DcFifo - --- | The Si539X chips use "Page"s to increase their address space. -type Page = Byte - --- | Different memory location depending on the current 'Page'. -type Address = Byte - --- | Indicates that the interface producing this is currently Busy and will not respond to inputs. -type Busy = Bool - --- | Indicates tgat the interface producing this value has captured the input. -type Acknowledge = Bool - --- | A Si539X register entry consists of a 'Page', and 'Address' and a 'Byte' value. -type RegisterEntry = (Page, Address, Byte) - --- | Used to read from or write to a register on a Si539x chip via SPI. -data RegisterOperation = RegisterOperation - { regPage :: Page - -- ^ Page at which to perform the read or write. - , regAddress :: Address - -- ^ Address at which to perform the read or write - , regWrite :: Maybe Byte - -- ^ @Nothing@ for a read operation, @Just byte@ to write @byte@ to this 'Page' and 'Address'. - } - deriving (Show, Generic, NFDataX) - -{- | Contains the configuration for an Si539x chip, explicitly differentiates between -the configuration preamble, configuration and configuration postamble. --} -data Si539xRegisterMap preambleEntries configEntries postambleEntries = Si539xRegisterMap - { configPreamble :: Vec preambleEntries RegisterEntry - -- ^ Configuration preamble - , config :: Vec configEntries RegisterEntry - -- ^ Configuration - , configPostamble :: Vec postambleEntries RegisterEntry - -- ^ Configuration postamble - } - -{- | Operations supported by the Si539x chip, @BurstWrite@ is omitted because the -current SPI core does not support writing a variable number of bytes while slave select -is low. --} -data SpiCommand - = -- | Sets the selected register 'Address' on the Si539x chip. - SetAddress Address - | -- | Writes data to the selected 'Address' on the selected 'Page'. - WriteData Byte - | -- | Reads data from the selected 'Address' on the selected 'Page'. - ReadData - | -- | Writes data to the selected 'Address' on the selected 'Page' and increments the 'Address'. - WriteDataInc Byte - | -- | Reads data from the selected 'Address' on the selected 'Page' and increments the 'Address'. - ReadDataInc - deriving (Eq) - --- | Converts an 'SpiCommand' to the corresponding bytes to be sent over SPI. -spiCommandToBytes :: SpiCommand -> Bytes 2 -spiCommandToBytes = \case - SetAddress bv -> pack (0b0000_0000 :: Byte, bv) - WriteData bv -> pack (0b0100_0000 :: Byte, bv) - ReadData -> pack (0b1000_0000 :: Byte, 0 :: Byte) - WriteDataInc bv -> pack (0b0110_0000 :: Byte, bv) - ReadDataInc -> pack (0b1000_0000 :: Byte, 0 :: Byte) - --- BurstWrite bv -> pack (0b1110_0000 :: Byte, bv) BurstWrite is not supported by the current SPI core. - --- | State of the configuration circuit in 'si539xSpi'. -data ConfigState dom entries - = -- | Continuously read from 'Address' 0xFE at any 'Page', if this operations returns - -- 0x0F twice in a row, the device is considered to be ready for operation. - WaitForReady Bool - | -- | Always after a @WaitForReady False@ state, we reset the SPI driver to make sure - -- it first sets the page and address again. - ResetDriver Bool - | -- | Fetches the 'RegisterEntry' at the 'Index' to be written to the @Si539x@ chip. - FetchReg (Index entries) - | -- | Writes the 'RegisterEntry' at the 'Index' to the @Si539x@ chip. - WriteEntry (Index entries) - | -- | Checks if the 'RegisterEntry' at the 'Index' was correctly written to the @Si539x@ chip. - ReadEntry (Index entries) - | -- | The 'RegisterEntry' at the 'Index' was not correctly written to the @Si539x@ chip. - Error (Index entries) - | -- | Continuously read from 'Address' 0x0C at 'Page' 0x00 until it returns bit 3 is 0. - WaitForLock - | -- | All entries in the 'Si539xRegisterMap' were correctly written to the @Si539x@ chip. - Finished - | -- | Waits for the Si539X to be calibrated after writing the configuration preamble from 'Si539xRegisterMap'. - Wait (Index (PeriodToCycles dom (Milliseconds 300))) (Index entries) - deriving (Show, Generic, NFDataX, Eq) - -instance - ( 1 <= entries - , KnownNat (DomainPeriod dom) - , KnownNat entries - ) => - BitPack (ConfigState dom entries) - --- | Utility function to retrieve the entry 'Index' from the 'ConfigState'. -getStateAddress :: (KnownNat entries) => ConfigState dom entries -> Index entries -getStateAddress = \case - WaitForReady _ -> 0 - ResetDriver _ -> 0 - FetchReg i -> i - WriteEntry i -> i - ReadEntry i -> i - Error i -> i - WaitForLock -> maxBound - Finished -> maxBound - Wait _ i -> i - -{- | SPI interface for a @Si539x@ clock generator chip with an initial configuration. -This component will first write and verify the initial configuration before becoming -available for external circuitry. For an interface that does not initially configure the -chip, see 'si539xDriver'. --} -si539xSpi :: - forall dom preambleEntries configEntries postambleEntries minTargetPeriodPs. - ( HiddenClockResetEnable dom - , KnownNat preambleEntries - , 1 <= preambleEntries - , KnownNat configEntries - , KnownNat postambleEntries - , 1 <= (preambleEntries + configEntries + postambleEntries) - ) => - -- | Initial configuration for the @Si539x@ chip. - Si539xRegisterMap preambleEntries configEntries postambleEntries -> - -- | Minimum period of the SPI clock frequency for the SPI clock divider. - SNat minTargetPeriodPs -> - -- | Read or write operation for the @Si539X@ registers. - Signal dom (Maybe RegisterOperation) -> - -- | MISO - "MISO" ::: Signal dom Bit -> - -- | - -- 1. Byte returned by read / write operation. - -- 2. The SPI interface is 'Busy' and does not accept new operations. - -- 3. Outgoing SPI signals: (SCK, MOSI, SS) - ( Signal dom (Maybe Byte) - , Signal dom Busy - , Signal dom (ConfigState dom (preambleEntries + configEntries + postambleEntries)) - , ( "SCK" ::: Signal dom Bool - , "MOSI" ::: Signal dom Bit - , "SS" ::: Signal dom Bool - ) - ) -si539xSpi Si539xRegisterMap{..} minTargetPs@SNat externalOperation miso = - (configByte, configBusy, configState, spiOut) - where - (driverByte, driverBusy, spiOut) = withReset driverReset si539xSpiDriver minTargetPs spiOperation miso - driverReset = forceReset $ holdTrue d3 $ flip fmap configState $ \case - ResetDriver _ -> True - _ -> False - - romOut = rom (configPreamble ++ config ++ configPostamble) romAddress - romAddress = bitCoerce . getStateAddress <$> configState - - (configState, spiOperation, configBusy, configByte) = - mealyB go (WaitForReady False) (romOut, externalOperation, driverByte, driverBusy) - - go currentState ((regPage, regAddress, byte), extSpi, spiByte, spiBusy) = - (nextState, (currentState, spiOp, busy, returnedByte)) - where - isConfigEntry i = - (natToNum @preambleEntries) <= i && i < (natToNum @(preambleEntries + configEntries)) - nextState = case (currentState, spiByte) of - (WaitForReady False, Just 0x0F) -> ResetDriver True - (WaitForReady True, Just 0x0F) -> FetchReg 0 - (WaitForReady _, Just _) -> ResetDriver False - (ResetDriver b, _) -> WaitForReady b - (FetchReg i, _) -> WriteEntry i - (WriteEntry i, Just _) - | i == maxBound -> WaitForLock - | i == (natToNum @preambleEntries - 1) -> Wait @dom 0 i - | isConfigEntry i -> ReadEntry i - | otherwise -> FetchReg (succ i) - (ReadEntry i, Just b) - | b == byte -> FetchReg (succ i) - | otherwise -> Error i - (Wait ((== maxBound) -> True) i, _) -> FetchReg (succ i) - (Wait j i, _) -> Wait (succ j) i - (WaitForLock, Just 0) -> Finished - (WaitForReady _, _) -> currentState - (WaitForLock, _) -> currentState - (WriteEntry _, _) -> currentState - (ReadEntry _, _) -> currentState - (Finished, _) -> currentState - (Error _, _) -> currentState - - spiOp = case currentState of - WaitForReady _ -> Just RegisterOperation{regPage = 0x00, regAddress = 0xFE, regWrite = Nothing} - ResetDriver _ -> Nothing - FetchReg _ -> Nothing - WriteEntry _ -> Just RegisterOperation{regPage, regAddress, regWrite = Just byte} - ReadEntry _ -> Just RegisterOperation{regPage, regAddress, regWrite = Nothing} - Wait _ _ -> Nothing - WaitForLock -> Just RegisterOperation{regPage = 0, regAddress = 0xC0, regWrite = Nothing} - Finished -> extSpi - Error _ -> extSpi - - (busy, returnedByte) = case currentState of - Finished -> (spiBusy, spiByte) - Error _ -> (spiBusy, spiByte) - _ -> (True, Nothing) - -{- | Keeps track of the current 'Page' and 'Address' of the @Si539x@ chip as well as -the current communication cycle. --} -data DriverState dom = DriverState - { currentPage :: Maybe Page - -- ^ Current 'Page' of the @Si539x@ chip. - , currentAddress :: Maybe Address - -- ^ Current 'Address' of the @Si539x@ chip. - , currentOp :: Maybe RegisterOperation - -- ^ Current communication transaction. - , commandAcknowledged :: Acknowledge - -- ^ Whether or not the current transaction has already been acknowledged. - , storedByte :: Maybe Byte - -- ^ Data we have received from the SPI interface. - , idleCycles :: Index (PeriodToCycles dom (Nanoseconds 95)) - -- ^ After communication, slave select must be high for at least 95ns. - } - deriving (Generic, NFDataX) - -{- | Circuitry that controls an SPI core based on a state machine that ensures communication -transactions with an @Si539x@ chip are executed correctly. It makes sure communication -operations target the right register and communication operations are spaced correctly. --} -si539xSpiDriver :: - forall dom minTargetPeriodPs. - (HiddenClockResetEnable dom) => - -- | Minimum period of the SPI clock frequency for the SPI clock divider. - SNat minTargetPeriodPs -> - -- | Read or write operation for the @Si539X@ registers. - Signal dom (Maybe RegisterOperation) -> - -- | MISO - "MISO" ::: Signal dom Bit -> - -- | - -- 1. Byte returned by read / write operation. - -- 2. The SPI interface is 'Busy' and does not accept new operations. - -- 3. Outgoing SPI signals: (SCK, MOSI, SS) - ( Signal dom (Maybe Byte) - , Signal dom Busy - , ( "SCK" ::: Signal dom Bool - , "MOSI" ::: Signal dom Bit - , "SS" ::: Signal dom Bool - ) - ) -si539xSpiDriver SNat incomingOpS miso = (fromSlave, decoderBusy, spiOut) - where - spiOut = (sck, mosi, ss) - (sck, mosi, ss, spiBusyS, acknowledge, receivedData) = - spiMaster - SPIMode0 - (SNat @(Max 1 (DivRU (PeriodToCycles dom minTargetPeriodPs) 2))) - d1 - spiWrite - miso - (spiWrite, decoderBusy, fromSlave) = - mealyB go defDriverState (incomingOpS, spiBusyS, acknowledge, receivedData) - defDriverState = - DriverState - { currentPage = Nothing - , currentAddress = Nothing - , currentOp = Nothing - , commandAcknowledged = False - , storedByte = Nothing - , idleCycles = maxBound - } - - go :: - DriverState dom -> - (Maybe RegisterOperation, Busy, Acknowledge, Maybe (Bytes 2)) -> - (DriverState dom, (Maybe (Bytes 2), Busy, Maybe Byte)) - - go currentState@(currentOp -> Nothing) (incomingOp, _, _, _) = - (currentState{currentOp = incomingOp}, (Nothing, False, storedByte currentState)) - go currentState@DriverState{..} (_, spiBusy, spiAck, receivedBytes) = - (nextState, (output, True, storedByte)) - where - RegisterOperation{..} = fromJust currentOp - samePage = currentPage == Just regPage - sameAddr = currentAddress == Just regAddress - - (spiCommand, nextOp, outBytes) = case (samePage, sameAddr, regWrite) of - (True, True, Just byte) -> (WriteData byte, Nothing, receivedBytes) - (True, True, Nothing) -> (ReadData, Nothing, receivedBytes) - (True, False, _) -> (SetAddress regAddress, currentOp, Nothing) - (False, _, _) - | currentAddress == Just 1 -> (WriteData regPage, currentOp, Nothing) - | otherwise -> (SetAddress 1, currentOp, Nothing) - - (nextPage, nextAddress) = case (currentPage, currentAddress, spiCommand) of - (_, Just 1, WriteData newPage) -> (Just newPage, currentAddress) - (_, _, SetAddress newAddr) -> (currentPage, Just newAddr) - _ -> (currentPage, currentAddress) - - updateIdleCycles - | spiBusy = idleCycles - | otherwise = satPred SatZero idleCycles - - nextState - | commandAcknowledged && not spiBusy && isNothing storedByte = - DriverState nextPage nextAddress nextOp False (fmap resize outBytes) maxBound - | otherwise = - currentState - { commandAcknowledged = spiAck || commandAcknowledged - , idleCycles = updateIdleCycles - , storedByte = fmap resize outBytes - } - - spiBytes = spiCommandToBytes spiCommand - output = orNothing (not commandAcknowledged && idleCycles == 0) spiBytes -{-# NOINLINE si539xSpiDriver #-} - --- TODO: Look into replacing dcFifo with XPM_CDC_Handshake. - -{- | Consumes 'SpeedChange's produced by a clock control algorithm and produces a -'RegisterOperation' for the 'si539xSpi' core. Consumption rate of 'SpeedUp's and -'SlowDown' depends on the availability of the SPI core. Uses 'dcFifo' with a depth -of 16 elements for clock domain crossing. This is an alternative to controlling the -FINC / FDEC pins directly, the advantages are that we already have to use SPI -to configure the chips, so we require less wiring / IO, and we don´t have to concern -ourselves with the timing requirements for controlling FINC / FDEC directly. The only -downside is that it is not as instantaneous as controlling the pins. --} -spiFrequencyController :: - forall domCallisto domSpi freqIncrementRange freqDecrementRange. - (KnownDomain domCallisto, KnownDomain domSpi) => - -- | The number of times we can increment the frequency from its initial value. - SNat freqIncrementRange -> - -- | The number of times we can decrement the frequency from its initial value. - SNat freqDecrementRange -> - -- | Callisto domain's clock. - Clock domCallisto -> - -- | Callisto domain's reset. - Reset domCallisto -> - -- | Callisto domain's enable. - Enable domCallisto -> - -- | SPI domain's clock. - Clock domSpi -> - -- | SPI domain's reset. - Reset domSpi -> - -- | SPI domain's enable. - Enable domSpi -> - -- | Requested 'SpeedChange'. - Signal domCallisto SpeedChange -> - -- | Incoming 'Busy' signal from the 'si539xSpi' component. - Signal domSpi Busy -> - -- | Outgoing 'RegisterOperation'. - Signal domSpi (Maybe RegisterOperation) -spiFrequencyController - SNat - SNat - clkCallisto - rstCallisto - enCallisto - clkSpi - rstSpi - enSpi - speedChange - spiBusy = spiOp - where - fifoIn = - mux - (speedChange .==. pure NoChange .||. not <$> fromEnable enCallisto) - (pure Nothing) - (Just <$> speedChange) - - FifoOut{..} = - dcFifo (defConfig @4) clkCallisto rstCallisto clkSpi rstSpi fifoIn readEnable - - (spiOp, readEnable) = - withClockResetEnable - clkSpi - rstSpi - enSpi - mealyB - go - initState - (spiBusy, isEmpty, fifoData) - - initState :: (Bool, Index (1 + freqIncrementRange + freqDecrementRange)) - initState = (False, natToNum @freqIncrementRange) - - go (fifoValid, stepCount) (spiBusyGo, isEmptyGo, fifoDataGo) = - ((readEnableGo, stepCountNext), (spiOpGo, readEnableGo)) - where - readEnableGo = not (isEmptyGo || spiBusyGo) - stepCountNext = case (fifoValid, spiBusyGo, fifoDataGo) of - (True, False, SpeedUp) -> satSucc SatBound stepCount - (True, False, SlowDown) -> satPred SatBound stepCount - _ -> stepCount - - spiOpGo = case (fifoValid, fifoDataGo, stepCount == maxBound, stepCount == minBound) of - (True, SpeedUp, False, _) -> - Just RegisterOperation{regPage = 0x00, regAddress = 0x1D, regWrite = Just 1} - (True, SlowDown, _, False) -> - Just RegisterOperation{regPage = 0x00, regAddress = 0x1D, regWrite = Just 2} - _ -> Nothing -{-# NOINLINE spiFrequencyController #-} - -{- | When this component receives @True@, it will hold it for @holdCycles@ number of -clock cycles. This implementation does not scale well to large values for @holdCycles@ -because it uses 'Vec' internally. --} -holdTrue :: - forall dom holdCycles. - (HiddenClockResetEnable dom, 1 <= holdCycles) => - SNat holdCycles -> - Signal dom Bool -> - Signal dom Bool -holdTrue SNat = mealy go (repeat False) - where - go :: (1 <= holdCycles) => Vec holdCycles Bool -> Bool -> (Vec holdCycles Bool, Bool) - go state@(Cons _ _) input = (takeI $ input :> state, fold (||) state) diff --git a/bittide/src/Bittide/ClockControl/StabilityChecker.hs b/bittide/src/Bittide/ClockControl/StabilityChecker.hs deleted file mode 100644 index a99636f42..000000000 --- a/bittide/src/Bittide/ClockControl/StabilityChecker.hs +++ /dev/null @@ -1,61 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GADTs #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE UndecidableInstances #-} - -module Bittide.ClockControl.StabilityChecker where - -import Clash.Prelude - -import Bittide.ClockControl (RelDataCount, targetDataCount) -import Bittide.ClockControl.Callisto.Util (dataCountToSigned) - --- | Stability results to be returned by the 'stabilityChecker'. -data StabilityIndication = StabilityIndication - { stable :: Bool - -- ^ Indicates stability of the signal over time. - , settled :: Bool - -- ^ Indicates whether the signal is stable and close to - -- 'targetDataCount'. - } - deriving (Generic, NFDataX, BitPack) - -{- | Checks whether the @Signal@ of buffer occupancies from an elastic -buffer is stable and settled. The @Signal@ is considered to be -stable, if it stays within a @margin@ of the target buffer -occupancy for @framesize@ number of cycles. If the current buffer -occupancies exceed that margin, then the target is updated to the -current buffer occupancy. The @Signal@ is considered to be settled, -if it is stable and close (within @margin@) to the global target -data count. --} -stabilityChecker :: - forall dom margin framesize n. - (HiddenClockResetEnable dom, 1 <= framesize, KnownNat n) => - -- | Maximum number of elements the incoming buffer occupancy is - -- allowed to deviate from the current @target@ for it to be - -- considered "stable". - SNat margin -> - -- | Minimum number of clock cycles the incoming buffer occupancy - -- must remain within the @margin@ for it to be considered "stable". - SNat framesize -> - -- | Incoming buffer occupancy. - Signal dom (RelDataCount n) -> - -- | Stability indicators - Signal dom StabilityIndication -stabilityChecker SNat SNat = mealy go (0, targetDataCount) - where - go (!cnt, !target) input = (newState, StabilityIndication{..}) - where - withinMargin !x !y = - abs (dataCountToSigned x `sub` dataCountToSigned y) <= (natToNum @margin) - - newState :: (Index (framesize + 1), RelDataCount n) - newState - | withinMargin target input = (satSucc SatBound cnt, target) - | otherwise = (0, input) - - stable = withinMargin target input && cnt == maxBound - settled = stable && withinMargin targetDataCount input diff --git a/bittide/src/Bittide/Counter.hs b/bittide/src/Bittide/Counter.hs deleted file mode 100644 index 008a85a92..000000000 --- a/bittide/src/Bittide/Counter.hs +++ /dev/null @@ -1,194 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Bittide.Counter ( - Active, - domainDiffCounter, - domainDiffCounterExt, -) where - -import Clash.Explicit.Prelude - -import Clash.Cores.Xilinx.Xpm (xpmCdcGray) -import Clash.Explicit.Reset.Extra (Asserted (..), xpmResetSynchronizer) -import Clash.Sized.Extra (concatUnsigneds, unsignedToSigned) - --- | State of 'domainDiffCounter' -data DdcState - = -- | In reset, or waiting for the incoming counter to change - DdcInReset - | -- | Counting and comparing with incoming domain - DdcRunning (Unsigned 64) - deriving (Generic, NFDataX) - --- | Indicates whether 'domainDiffCounter' is actively counting or still in reset. -type Active = Bool - -{- | Determine speed differences between two domains. If the source domain is -faster than the destination domain, the result will become larger. Vice versa, -if the source domain is slower than the destination domain, the result will -become smaller. This is analogous to what would happen to a FIFO's data count -when continuously written to by the source domain and read from by the -destination domain. To ease integration in control algorithms, this component -makes sure it starts counting at zero. It also waits for the incoming counter -to become active, i.e. non-zero, before starting to count itself. - -If both domains support initial values, 'domainDiffCounter' does not need to -be reset. - -To reset this component, the reset should be asserted for at least one cycle in -the source domain _plus_ four cycles in the destination domain. The reset in the -destination domain should be deasserted at the same time or *after* the one in -the source domain for glitchless operation. - -__N.B.__: - This function will only work properly if the given domains are pretty close - to another for a number of reasons: - - 1. It uses an 8-bit Gray counter internally for CDC - - 2. It uses one 64-bit counter in each domain - - 3. Its output is constrained to @Signed 32@ - - These values have been chosen such that: - - * The 64-bit counter only overflows once every 3000 years at 200 MHz - - * The 32-bit output only overflows after running at maximum divergence - rate (100 ppm) at 200 MHz for 2 days. We expect systems to stabilize - after a few milliseconds and reframing should nudge counters back to - zero ever so often. --} -domainDiffCounter :: - forall src dst. - ( KnownDomain src - , KnownDomain dst - ) => - Clock src -> - Reset src -> - Clock dst -> - Reset dst -> - -- | Counter and boolean indicating whether the component is currently active - Signal dst (Signed 32, Active) -domainDiffCounter clkSrc rstSrc clkDst rstDst = - mealy clkDst rstDst enableGen go DdcInReset counter - where - -- 64 bits is enough for approximately 3 millenia @ 200 MHz - counter = synchronizedSuccCounter @64 clkSrc rstSrc clkDst rstDst - - go :: DdcState -> Unsigned 64 -> (DdcState, (Signed 32, Bool)) - go DdcInReset c1 - | c1 == 0 = (DdcInReset, (0, False)) - | otherwise = (DdcRunning (c1 + 1), (0, True)) - go (DdcRunning c0) c1 = (DdcRunning (c0 + 1), (c1 `subAndTruncate` c0, True)) - - subAndTruncate :: Unsigned 64 -> Unsigned 64 -> Signed 32 - subAndTruncate c0 c1 = truncateB (unsignedToSigned c0 - unsignedToSigned c1) - -{- | A variant of 'domainDiffCounter' for determination of speed differences -between two domains, but which are captured in another external domain. --} -domainDiffCounterExt :: - forall ext src dst. - ( KnownDomain ext - , KnownDomain src - , KnownDomain dst - , HasSynchronousReset ext - , HasDefinedInitialValues src - , HasDefinedInitialValues dst - ) => - Clock ext -> - Reset ext -> - Clock src -> - Clock dst -> - Signal ext (Signed 32) -domainDiffCounterExt clkExt rstExt clkSrc clkDst = - truncateB - <$> ((-) <$> extendedGrayCounter clkSrc <*> extendedGrayCounter clkDst) - where - -- 64 bits is enough for approximately 3 millenia @ 200 MHz - extendedGrayCounter :: (KnownDomain dom) => Clock dom -> Signal ext (Signed 65) - extendedGrayCounter clk = - fmap unsignedToSigned - $ extendSuccCounter clkExt rstExt - $ xpmCdcGray clk clkExt counter - where - counter = - register - clk - (xpmResetSynchronizer Deasserted clkExt clk rstExt) - enableGen - (minBound :: Unsigned 8) - (satSucc SatWrap <$> counter) - -{- | A counter that counts /up/, synchronized from the domain @src@ to domain @dst@. To -reset this component, the reset should be asserted for at least one cycle in the -source domain _plus_ four cycles in the destination domain. - -__N.B.__: This function uses an 8-bit Gray counter internally, and will therefore - only work properly if both clock speeds are pretty close to one another. --} -synchronizedSuccCounter :: - forall n src dst. - ( KnownDomain src - , KnownDomain dst - , KnownNat n - , 8 <= n - ) => - Clock src -> - Reset src -> - Clock dst -> - Reset dst -> - Signal dst (Unsigned n) -synchronizedSuccCounter clkSrc rstSrc clkDst rstDst = - extendSuccCounter @8 @(n - 8) clkDst rstDst - $ xpmCdcGray @8 clkSrc clkDst counter - where - counter :: Signal src (Unsigned 8) - counter = register clkSrc rstSrc enableGen 0 (counter + 1) - --- | State of 'extendSuccCounter' -data EscState m - = -- | In reset, or waiting for an overflow - EscInReset - | -- | Counting - whenever an overflow occurs, this constructors field is upped - EscRunning (Unsigned m) - deriving (Generic, NFDataX) - -{- | Given a counter that counts /up/, extend the size of the counter. After its -reset is deasserted, it will wait until it sees an overflow to ensure the -counter is glitchless and always starts at 0. - -This can be used to extend computationally complex counter components, such as -Gray counters. --} -extendSuccCounter :: - forall n m dom. - ( KnownDomain dom - , KnownNat n - , KnownNat m - ) => - Clock dom -> - Reset dom -> - Signal dom (Unsigned n) -> - Signal dom (Unsigned (m + n)) -extendSuccCounter clk rst counterLower = - mealyB - clk - rst - enableGen - go - EscInReset - ( isFalling clk rst enableGen 0 (msb <$> counterLower) - , counterLower - ) - where - go :: EscState m -> (Bool, Unsigned n) -> (EscState m, Unsigned (m + n)) - go EscInReset (False, _) = (EscInReset, 0) - go EscInReset (True, n0) = (EscRunning 0, extend n0) - go (EscRunning m0) (overflow, n0) = (EscRunning m1, n1) - where - m1 = if overflow then m0 + 1 else m0 - n1 = concatUnsigneds m1 n0 diff --git a/bittide/src/Bittide/DoubleBufferedRam.hs b/bittide/src/Bittide/DoubleBufferedRam.hs deleted file mode 100644 index 49e246f60..000000000 --- a/bittide/src/Bittide/DoubleBufferedRam.hs +++ /dev/null @@ -1,617 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=7 #-} - -module Bittide.DoubleBufferedRam where - -import Clash.Prelude - -import Data.Constraint -import Data.Maybe -import Protocols (Ack (Ack), CSignal, Circuit (Circuit), Df) -import Protocols.Df (dataToMaybe) -import Protocols.Wishbone - -import Bittide.Extra.Maybe -import Bittide.SharedTypes hiding (delayControls) -import Data.Constraint.Nat.Extra -import Data.Typeable - -data ContentType n a - = Vec (Vec n a) - | Blob (MemBlob n (BitSize a)) - | BlobVec (Vec (Regs a 8) (MemBlob n 8)) - | File FilePath - | FileVec (Vec (Regs a 8) FilePath) - -instance (Show a, KnownNat n, Typeable a) => Show (ContentType n a) where - show = \case - (Vec _) -> "Vec: " <> nAnda - (Blob _) -> "Blob: " <> nAnda - (BlobVec _) -> "BlobVec: " <> nAnda - (File fp) -> "File: " <> nAnda <> ", filepath = " <> fp - (FileVec fps) -> "File: " <> nAnda <> ", filepaths = " <> show fps - where - nAnda = "(" <> show (natToNatural @n) <> " of type (" <> show (typeRep $ Proxy @a) <> "))" -data InitialContent elements a where - NonReloadable :: ContentType elements a -> InitialContent elements a - Reloadable :: ContentType elements a -> InitialContent elements a - Undefined :: (1 <= elements, KnownNat elements) => InitialContent elements a - -deriving instance - (Show a, KnownNat elements, Typeable a) => Show (InitialContent elements a) - -{- | Accepts 'InitialContents' and returns a 'blockRam' implementations initialized with -the corresponding content. --} -initializedRam :: - forall dom n a. - ( HiddenClockResetEnable dom - , KnownNat n - , 1 <= n - , Paddable a - ) => - ContentType n a -> - ( Signal dom (Index n) -> - Signal dom (Maybe (Located n a)) -> - Signal dom a - ) -initializedRam content rd wr = case content of - Vec vec -> blockRam vec rd wr - Blob blob -> bitCoerce <$> blockRamBlob blob rd (bitCoerce <$> wr) - BlobVec blobVec -> - getDataBe @8 - . RegisterBank - <$> bundle - ((`blockRamBlob` rd) <$> blobVec <*> unbundle ((`splitWriteInBytes` maxBound) <$> wr)) - File fp -> bitCoerce <$> blockRamFile (SNat @n) fp rd (bitCoerce <$> wr) - FileVec fpVec -> - getDataBe @8 - . RegisterBank - <$> bundle - ( (\fp -> blockRamFile (SNat @n) fp rd) - <$> fpVec - <*> unbundle ((`splitWriteInBytes` maxBound) <$> wr) - ) - -contentGenerator :: - forall dom romSize targetSize a. - ( HiddenClockResetEnable dom - , KnownNat targetSize - , 1 <= targetSize - , KnownNat romSize - , romSize <= targetSize - , Paddable a - ) => - ContentType romSize a -> - (Signal dom (Maybe (Located targetSize a)), Signal dom Bool) -contentGenerator content = case compareSNat d1 (SNat @romSize) of - SNatLE -> (mux (running .&&. not <$> done) writeOp (pure Nothing), done) - where - running = register False $ pure True - writeOp = curry Just . resize <$> romAddr0 <*> element - done = register False (done .||. (running .&&. romAddr0 .==. pure maxBound)) - romAddr0 = register (maxBound :: Index romSize) romAddr1 - romAddr1 = satSucc SatWrap <$> romAddr0 - element = initializedRam content (bitCoerce <$> romAddr1) (pure Nothing) - _ -> (pure Nothing, pure True) - --- | Circuit wrapper around `wbStorageDP`. -wbStorageDPC :: - forall dom depth awA awB. - ( HiddenClockResetEnable dom - , KnownNat awA - , KnownNat awB - , KnownNat depth - , 1 <= depth - ) => - InitialContent depth (Bytes 4) -> - Circuit - (Wishbone dom 'Standard awA (Bytes 4), Wishbone dom 'Standard awB (Bytes 4)) - () -wbStorageDPC content = Circuit go - where - go :: - ( ( Signal dom (WishboneM2S awA 4 (BitVector 32)) - , Signal dom (WishboneM2S awB 4 (BitVector 32)) - ) - , () - ) -> - ( ( Signal dom (WishboneS2M (BitVector 32)) - , Signal dom (WishboneS2M (BitVector 32)) - ) - , () - ) - go ((m2sA, m2sB), ()) = ((s2mA, s2mB), ()) - where - (s2mA, s2mB) = wbStorageDP content m2sA m2sB - -{- | Dual-ported Wishbone storage element, essentially a wrapper for the single-ported version -which priorities port A over port B. Transactions are not aborted, but when two transactions -are initiated at the same time, port A will have priority. --} -wbStorageDP :: - forall dom depth awA awB. - ( HiddenClockResetEnable dom - , KnownNat awA - , KnownNat awB - , KnownNat depth - , 1 <= depth - ) => - InitialContent depth (Bytes 4) -> - Signal dom (WishboneM2S awA 4 (Bytes 4)) -> - Signal dom (WishboneM2S awB 4 (Bytes 4)) -> - (Signal dom (WishboneS2M (Bytes 4)), Signal dom (WishboneS2M (Bytes 4))) -wbStorageDP initial aM2S bM2S = (aS2M, bS2M) - where - storageOut = wbStorage' @_ @depth @(Max awA awB) initial storageIn - - storageIn :: Signal dom (WishboneM2S (Max awA awB) 4 (Bytes 4)) - storageIn = mux (nowActive .==. pure A) (resizeM2SAddr <$> aM2S) (resizeM2SAddr <$> bM2S) - - -- We keep track of ongoing transactions to respect Read-modify-write operations. - nowActive = register A nextActive - nextActive = selectNow <$> nowActive <*> aM2S <*> aS2M <*> bM2S <*> bS2M - active WishboneM2S{busCycle, strobe} = busCycle && strobe - terminated WishboneS2M{acknowledge, err} = acknowledge || err - selectNow aorb am2s as2m bm2s bs2m = - case (aorb, active am2s, terminated as2m, active bm2s, terminated bs2m) of - (_, True, _, False, _) -> A - (_, False, _, True, _) -> B - (A, True, True, True, _) -> B - (B, True, _, True, True) -> A - _ -> aorb - - (aS2M, bS2M) = - unbundle - $ mux - (nowActive .==. pure A) - (bundle (storageOut, noTerminate <$> storageOut)) - (bundle (noTerminate <$> storageOut, storageOut)) - - noTerminate wb = wb{acknowledge = False, err = False, retry = False, stall = False} - -{- | Wishbone storage element with 'Circuit' interface from "Protocols.Wishbone" that -allows for word aligned reads and writes. --} -wbStorage :: - forall dom depth aw. - ( HiddenClockResetEnable dom - , KnownNat aw - , KnownNat depth - , 1 <= depth - ) => - InitialContent depth (Bytes 4) -> - Circuit (Wishbone dom 'Standard aw (Bytes 4)) () -wbStorage initContent = Circuit $ \(m2s, ()) -> - (wbStorage' initContent m2s, ()) -{-# NOINLINE wbStorage #-} - --- | Storage element with a single wishbone port. Allows for word-aligned addresses. -wbStorage' :: - forall dom depth aw. - ( HiddenClockResetEnable dom - , KnownNat aw - , KnownNat depth - , 1 <= depth - ) => - InitialContent depth (Bytes 4) -> - Signal dom (WishboneM2S aw 4 (Bytes 4)) -> - Signal dom (WishboneS2M (Bytes 4)) -wbStorage' initContent wbIn = delayControls wbIn wbOut - where - romOut = case initContent of - Reloadable content -> bundle $ contentGenerator content - other -> deepErrorX $ "wbStorage': No content generator for " <> show other - - readData = ram readAddr writeEntry byteSelect - - (ram, isReloadable) = case initContent of - Reloadable _ -> - (blockRamByteAddressableU, True) - Undefined -> - (blockRamByteAddressableU, False) - NonReloadable content -> - (blockRamByteAddressable @_ @depth content, False) - - (readAddr, writeEntry, byteSelect, wbOut) = - unbundle (go <$> bundle (wbIn, readData, romOut)) - - go (WishboneM2S{..}, readDataGo, (romWrite, romDone)) = - ( wbAddr - , writeEntryGo - , byteSelectGo - , (emptyWishboneS2M @(Bytes 4)){acknowledge, readData = readDataGo, err} - ) - where - wbAddr = unpack $ resize addr :: Index depth - addrLegal = addr < (natToNum @depth) - - masterActive = strobe && busCycle - err = masterActive && not addrLegal - acknowledge = masterActive && (not isReloadable || romDone) && addrLegal - - masterWriting = acknowledge && writeEnable - - writeEntryGo - | isReloadable && not romDone = romWrite - | masterWriting = Just (wbAddr, writeData) - | otherwise = Nothing - - byteSelectGo - | isReloadable && not romDone = maxBound - | otherwise = busSelect - - -- \| Delays the output controls to align them with the actual read / write timing. - delayControls :: - (NFDataX a) => - Signal dom (WishboneM2S aw selWidth a) -> -- current M2S signal - Signal dom (WishboneS2M a) -> - Signal dom (WishboneS2M a) - delayControls m2s s2m0 = mux inCycle s2m1 (pure emptyWishboneS2M) - where - inCycle = (busCycle <$> m2s) .&&. (strobe <$> m2s) - - -- It takes a single cycle to lookup elements in a block ram. We can therfore - -- only process a request every other clock cycle. - ack = (acknowledge <$> s2m0) .&&. (not <$> err1) .&&. (not <$> delayedAck) .&&. inCycle - err1 = (err <$> s2m0) .&&. inCycle - delayedAck = register False ack - s2m1 = - (\wb newAck newErr -> wb{acknowledge = newAck, err = newErr}) - <$> s2m0 - <*> delayedAck - <*> err1 - -{- | The double buffered Ram component is a memory component that contains two buffers -and enables the user to write to one buffer and read from the other. 'AorB' -selects which buffer is written to, while read operations read from the other buffer. --} -doubleBufferedRam :: - forall dom memDepth a. - (HiddenClockResetEnable dom, KnownNat memDepth, 1 <= memDepth, Paddable a, ShowX a) => - -- | Initial content. - ContentType (2 * memDepth) a -> - -- | Controls which buffers is written to, while the other buffer is read from. - Signal dom AorB -> - -- | Read address. - Signal dom (Index memDepth) -> - -- | Write operation. - Signal dom (Maybe (Located memDepth a)) -> - -- | Data at read address (1 cycle delay). - Signal dom a -doubleBufferedRam initContent outputSelect rd0 wr0 = - initializedRam initContent rd1 wr1 - where - (rd1, wr1) = unbundle $ updateAddrs <$> rd0 <*> wr0 <*> outputSelect - -{- | Version of 'doubleBufferedRam' with undefined initial contents. This component -contains two buffers and enables the user to write to one buffer and read from the -other. 'AorB' selects which buffer is written to, while read operations -read from the other buffer. --} -doubleBufferedRamU :: - forall dom memDepth a. - (HiddenClockResetEnable dom, KnownNat memDepth, 1 <= memDepth, NFDataX a) => - -- | Controls which buffers is written to, while the other buffer is read from. - Signal dom AorB -> - -- | Read address. - Signal dom (Index memDepth) -> - -- | Incoming data frame. - Signal dom (Maybe (Index memDepth, a)) -> - -- | Outgoing data - Signal dom a -doubleBufferedRamU outputSelect readAddr0 writeFrame0 = - blockRamU NoClearOnReset (SNat @(2 * memDepth)) rstFunc readAddr1 writeFrame1 - where - (readAddr1, writeFrame1) = - unbundle $ updateAddrs <$> readAddr0 <*> writeFrame0 <*> outputSelect - rstFunc = clashCompileError "doubleBufferedRamU: reset function undefined" - -{- | The byte addressable double buffered Ram component is a memory component that -consists of two buffers and internally stores its elements as a multiple of 8 bits. -It contains a blockRam per byte and uses the one hot byte select signal to determine -which bytes will be overwritten during a write operation. This components writes to -one buffer and reads from the other. 'AorB' selects which buffer is -written to, while read operations read from the other buffer. --} -doubleBufferedRamByteAddressable :: - forall dom memDepth a. - (HiddenClockResetEnable dom, KnownNat memDepth, 1 <= memDepth, Paddable a, ShowX a) => - -- | Initial content - ContentType (2 * memDepth) a -> - -- | Controls which buffers is written to, while the other buffer is read from. - Signal dom AorB -> - -- | Read address. - Signal dom (Index memDepth) -> - -- | Write operation. - Signal dom (Maybe (Located memDepth a)) -> - -- | Byte enables that determine which nBytes get replaced. - Signal dom (ByteEnable a) -> - -- | Data at read address (1 cycle delay). - Signal dom a -doubleBufferedRamByteAddressable initContent outputSelect rd0 wr0 = - blockRamByteAddressable initContent rd1 wr1 - where - (rd1, wr1) = unbundle $ updateAddrs <$> rd0 <*> wr0 <*> outputSelect - -{- | Version of 'doubleBufferedRamByteAddressable' where the initial content is undefined. -This memory element consists of two buffers and internally stores its elements as a -multiple of 8 bits. It contains a blockRam per byte and uses the one hot byte select -signal to determine which nBytes will be overwritten during a write operation. -This components writes to one buffer and reads from the other. Which buffer is -used for reading while the other is used for writing is controlled by the 'AorB'. --} -doubleBufferedRamByteAddressableU :: - forall dom memDepth a. - (KnownNat memDepth, 1 <= memDepth, HiddenClockResetEnable dom, Paddable a, ShowX a) => - -- | Controls which buffers is written to, while the other buffer is read from. - Signal dom AorB -> - -- | Read address. - Signal dom (Index memDepth) -> - -- | Incoming data frame. - Signal dom (Maybe (Located memDepth a)) -> - -- | One hot byte select for writing only - Signal dom (ByteEnable a) -> - -- | Outgoing data - Signal dom a -doubleBufferedRamByteAddressableU outputSelect readAddr0 writeFrame0 = - blockRamByteAddressableU readAddr1 writeFrame1 - where - (readAddr1, writeFrame1) = - unbundle $ updateAddrs <$> readAddr0 <*> writeFrame0 <*> outputSelect - -{- | Blockram similar to 'blockRam' with the addition that it takes a byte select signal -that controls which nBytes at the write address are updated. --} -blockRamByteAddressable :: - forall dom memDepth a. - (HiddenClockResetEnable dom, KnownNat memDepth, 1 <= memDepth, Paddable a, ShowX a) => - -- | Initial content. - ContentType memDepth a -> - -- | Read address. - Signal dom (Index memDepth) -> - -- | Write operation. - Signal dom (Maybe (Located memDepth a)) -> - -- | Byte enables that determine which nBytes get replaced. - Signal dom (ByteEnable a) -> - -- | Data at read address (1 cycle delay). - Signal dom a -blockRamByteAddressable initContent readAddr newEntry byteSelect = - getDataBe @8 . RegisterBank <$> case initContent of - Blob _ -> clashCompileError "blockRamByteAddressable: Singular MemBlobs are not supported. " - Vec vecOfA -> go (byteRam . Vec <$> transpose (fmap getBytes vecOfA)) - BlobVec blobs -> go (fmap (byteRam . Blob) blobs) - File _ -> - clashCompileError - "blockRamByteAddressable: Singular source files for initial content are not supported. " - FileVec blobs -> go (fmap (byteRam . File) blobs) - where - go brams = readBytes - where - writeBytes = unbundle $ splitWriteInBytes <$> newEntry <*> byteSelect - readBytes = bundle $ brams <*> writeBytes - getBytes (getRegsBe -> RegisterBank (vec :: Vec (Regs a 8) Byte)) = vec - byteRam = (`initializedRam` readAddr) - -{- | Version of 'blockRamByteAddressable' with undefined initial contents. It is similar -to 'blockRam' with the addition that it takes a byte select signal that controls -which nBytes at the write address are updated. --} -blockRamByteAddressableU :: - forall dom memDepth a. - (HiddenClockResetEnable dom, KnownNat memDepth, 1 <= memDepth, Paddable a, ShowX a) => - -- | Read address. - Signal dom (Index memDepth) -> - -- | Write operation. - Signal dom (Maybe (Located memDepth a)) -> - -- | Byte enables that determine which nBytes get replaced. - Signal dom (ByteEnable a) -> - -- | Data at read address (1 cycle delay). - Signal dom a -blockRamByteAddressableU readAddr newEntry byteSelect = - getDataBe @8 . RegisterBank <$> readBytes - where - writeBytes = unbundle $ splitWriteInBytes <$> newEntry <*> byteSelect - readBytes = bundle $ ram readAddr <$> writeBytes - ram = blockRamU NoClearOnReset (SNat @memDepth) rstFunc - rstFunc = clashCompileError "blockRamByteAddressableU: reset function undefined" - -data RegisterWritePriority = CircuitPriority | WishbonePriority - deriving (Eq) - -{- | Register with additional wishbone interface, this component has a configurable -priority that determines which value gets stored in the register during a write conflict. -The `RegisterWritePriority` determines if the wishbone write gets accepted or if the -`Df` write gets accepted. The other value is discarded. --} -registerWbC :: - forall dom a nBytes aw. - ( HiddenClockResetEnable dom - , Paddable a - , KnownNat nBytes - , 1 <= nBytes - , KnownNat aw - ) => - -- | Determines the write priority on write collisions - RegisterWritePriority -> - -- | Initial value. - a -> - Circuit (Wishbone dom 'Standard aw (Bytes nBytes), Df dom a) (CSignal dom a) -registerWbC prio initVal = case cancelMulDiv @nBytes @8 of - Dict -> Circuit go - where - go ((wbM2S, fmap dataToMaybe -> dfM2S), _) = ((wbS2M, fmap Ack dfS2M), aOut) - where - (aOut, wbS2M) = registerWb prio initVal wbM2S dfM2S - dfS2M - | prio == WishbonePriority = (\WishboneM2S{..} -> not (strobe && busCycle)) <$> wbM2S - | otherwise = pure True - -{- | Register with additional wishbone interface, this component has a configurable -priority that determines which value gets stored in the register during a write conflict. -With 'CircuitPriority', the incoming value in the fourth argument gets stored on a -collision and the wishbone bus gets acknowledged, but the value is silently ignored. -With 'WishbonePriority', the incoming wishbone write gets accepted and the value in the -fourth argument gets ignored. --} -registerWb :: - forall dom a nBytes addrW. - ( HiddenClockResetEnable dom - , Paddable a - , KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - ) => - -- | Determines the write priority on write collisions - RegisterWritePriority -> - -- | Initial value. - a -> - -- | Wishbone bus (master to slave) - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | New circuit value. - Signal dom (Maybe a) -> - -- | - -- 1. Outgoing stored value - -- 2. Outgoing wishbone bus (slave to master) - (Signal dom a, Signal dom (WishboneS2M (Bytes nBytes))) -registerWb writePriority initVal wbIn sigIn = - registerWbE writePriority initVal wbIn sigIn (pure maxBound) - -{-# NOINLINE registerWbE #-} - -{- | Register with additional wishbone interface, this component has a configurable -priority that determines which value gets stored in the register during a write conflict. -With 'CircuitPriority', the incoming value in the fourth argument gets stored on a -collision and the wishbone bus gets acknowledged, but the value is silently ignored. -With 'WishbonePriority', the incoming wishbone write gets accepted and the value in the -fourth argument gets ignored. This version has an additional argument for circuit write -byte enables. --} -registerWbE :: - forall dom a nBytes addrW. - ( HiddenClockResetEnable dom - , Paddable a - , KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - ) => - -- | Determines the write priority on write collisions - RegisterWritePriority -> - -- | Initial value. - a -> - -- | Wishbone bus (master to slave) - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | New circuit value. - Signal dom (Maybe a) -> - -- | Explicit Byte enables for new circuit value - Signal dom (ByteEnable a) -> - -- | - -- 1. Outgoing stored value - -- 2. Outgoing wishbone bus (slave to master) - (Signal dom a, Signal dom (WishboneS2M (Bytes nBytes))) -registerWbE writePriority initVal wbIn sigIn sigByteEnables = (regOut, wbOut) - where - regOut = registerByteAddressable initVal regIn byteEnables - (byteEnables, wbOut, regIn) = unbundle (go <$> regOut <*> sigIn <*> sigByteEnables <*> wbIn) - go :: - a -> - Maybe a -> - BitVector (Regs a 8) -> - WishboneM2S addrW nBytes (Bytes nBytes) -> - (BitVector (Regs a 8), WishboneS2M (Bytes nBytes), a) - go regOut0 sigIn0 sigbyteEnables0 WishboneM2S{..} = - ( byteEnables0 - , (emptyWishboneS2M @(Bytes nBytes)){acknowledge, err, readData} - , regIn0 - ) - where - invalidAddress = addr > resize (pack (maxBound :: Index (Max 1 (Regs a (nBytes * 8))))) - masterActive = strobe && busCycle - err = masterActive && invalidAddress - acknowledge = masterActive && not err - wbWriting = writeEnable && acknowledge - wbAddr = unpack (resize addr) :: Index (Max 1 (Regs a (nBytes * 8))) - readData = case getRegsLe regOut0 of - RegisterBank vec -> vec !! wbAddr - - wbByteEnables = - resize . pack . reverse $ replace wbAddr busSelect (repeat @(Regs a (nBytes * 8)) 0) - sigRegIn = fromMaybe (errorX "registerWb: sigIn is Nothing when Just is expected.") sigIn0 - wbRegIn = getDataLe . RegisterBank $ repeat writeData - (byteEnables0, regIn0) = case (writePriority, isJust sigIn0, wbWriting) of - (CircuitPriority, True, _) -> (sigbyteEnables0, sigRegIn) - (CircuitPriority, False, True) -> (wbByteEnables, wbRegIn) - (WishbonePriority, _, True) -> (wbByteEnables, wbRegIn) - (WishbonePriority, True, False) -> (sigbyteEnables0, sigRegIn) - (_, False, False) -> (0, errorX "registerWb: register input not defined.") - -{- | Register similar to 'register' with the addition that it takes a byte select signal -that controls which nBytes are updated. --} -registerByteAddressable :: - forall dom a. - (HiddenClockResetEnable dom, Paddable a) => - -- | Initial value. - a -> - -- | New value. - Signal dom a -> - -- | Byte enables that determine which nBytes of the new value are stored. - Signal dom (ByteEnable a) -> - -- | Stored value. - Signal dom a -registerByteAddressable initVal newVal byteEnables = - getDataLe @8 . RegisterBank <$> bundle regsOut - where - initBytes = getBytes initVal - newBytes = unbundle $ getBytes <$> newVal - regsOut = - (`andEnable` register) - <$> unbundle (reverse . unpack <$> byteEnables) - <*> initBytes - <*> newBytes - getBytes (getRegsLe -> RegisterBank vec) = vec - -{- | Takes singular write operation (Maybe (Index maxIndex, writeData)) and splits it up -according to a supplied byteselect bitvector into a vector of byte sized write operations -(Maybe (Index maxIndex, Byte)). --} -splitWriteInBytes :: - forall maxIndex writeData. - (Paddable writeData) => - -- | Incoming write operation. - Maybe (Located maxIndex writeData) -> - -- | Incoming byte enables. - ByteEnable writeData -> - -- | Per byte write operation. - Vec (Regs writeData 8) (Maybe (LocatedByte maxIndex)) -splitWriteInBytes (Just (addr, writeData)) byteSelect = - case getRegsBe writeData of - RegisterBank vec -> orNothing <$> unpack byteSelect <*> fmap (addr,) vec -splitWriteInBytes Nothing _ = repeat Nothing - -{- | Takes an address and write operation and 'bitCoerce's the addresses as follows: -'bitCoerce' (address, bool) --} -updateAddrs :: - (KnownNat n, 1 <= n, KnownNat m, 1 <= m) => - -- | An address. - Index n -> - -- | A write operation. - Maybe (Index m, b) -> - -- | A boolean that will be used for the addresses LSBs. - AorB -> - -- | - -- 1. Updated address - -- 2. Write operation with updated address. - (Index (n * 2), Maybe (Index (m * 2), b)) -updateAddrs rdAddr (Just (i, a)) bufSelect = - (mul2Index rdAddr bufSelect, Just (mul2Index i (swapAorB bufSelect), a)) -updateAddrs rdAddr Nothing bufSelect = - (mul2Index rdAddr bufSelect, Nothing) diff --git a/bittide/src/Bittide/ElasticBuffer.hs b/bittide/src/Bittide/ElasticBuffer.hs deleted file mode 100644 index 5169b5d05..000000000 --- a/bittide/src/Bittide/ElasticBuffer.hs +++ /dev/null @@ -1,172 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NamedFieldPuns #-} - -module Bittide.ElasticBuffer where - -import Clash.Cores.Xilinx.DcFifo -import Clash.Prelude -import GHC.Stack - -import Bittide.ClockControl (RelDataCount, targetDataCount) - -import qualified Clash.Cores.Extra as CE -import qualified Clash.Explicit.Prelude as E - -data EbMode - = -- | Disable write, enable read - Drain - | -- | Enable write, disable read - Fill - | -- | Enable write, enable read - Pass - deriving (Generic, NFDataX, Eq, Show) - -type Underflow = Bool -type Overflow = Bool - -ebModeToReadWrite :: EbMode -> (Bool, Bool) -ebModeToReadWrite = \case - Fill -> (False, True) - Drain -> (True, False) - Pass -> (True, True) - -{-# NOINLINE sticky #-} - --- | Create a sticky version of a boolean signal. -sticky :: - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Signal dom Bool -> - Signal dom Bool -sticky clk rst a = stickyA - where - stickyA = E.register clk rst enableGen False (stickyA .||. a) - -{-# NOINLINE xilinxElasticBuffer #-} - -{- | An elastic buffer backed by a Xilinx FIFO. It exposes all its control and -monitor signals in its read domain. --} -xilinxElasticBuffer :: - forall n readDom writeDom a. - ( HasCallStack - , KnownDomain readDom - , KnownDomain writeDom - , NFDataX a - , KnownNat n - , 4 <= n - , n <= 17 - ) => - Clock readDom -> - Clock writeDom -> - -- | Resetting resets the 'Underflow' and 'Overflow' signals, but not the 'RelDataCount' - -- ones. Make sure to hold the reset at least 3 cycles in both clock domains. - Reset readDom -> - Signal readDom EbMode -> - Signal writeDom a -> - ( Signal readDom (RelDataCount n) - , -- Indicates whether the FIFO under or overflowed. This signal is sticky: it - -- will only deassert upon reset. - Signal readDom Underflow - , Signal writeDom Overflow - , Signal readDom a - ) -xilinxElasticBuffer clkRead clkWrite rstRead ebMode wdata = - ( -- Note that this is chosen to work for 'RelDataCount' either being - -- set to 'Signed' with 'targetDataCount' equals 0 or set to - -- 'Unsigned' with 'targetDataCount' equals 'shiftR maxBound 1 + 1'. - -- This way, the representation can be easily switched without - -- introducing major code changes. - (+ targetDataCount) - . bitCoerce - . (+ (-1 - shiftR maxBound 1)) - <$> readCount - , isUnderflowSticky - , isOverflowSticky - , fifoData - ) - where - rstWrite = unsafeFromActiveHigh rstWriteBool - rstWriteBool = - CE.safeDffSynchronizer clkRead clkWrite False (unsafeToActiveHigh rstRead) - - FifoOut{readCount, isUnderflow, isOverflow, fifoData} = - dcFifo - (defConfig @n){dcOverflow = True, dcUnderflow = True} - clkWrite - noResetWrite - clkRead - noResetRead - writeData - readEnable - - -- We make sure to "stickify" the signals in their original domain. The - -- synchronizer might lose samples depending on clock configurations. - isUnderflowSticky = sticky clkRead rstRead isUnderflow - isOverflowSticky = sticky clkWrite rstWrite isOverflow - - -- We don't reset the Xilix FIFO: its reset documentation is self-contradictory - -- and mentions situations where the FIFO can end up in an unrecoverable state. - noResetWrite = unsafeFromActiveHigh (pure False) - noResetRead = unsafeFromActiveHigh (pure False) - - (readEnable, writeEnable) = unbundle (ebModeToReadWrite <$> ebMode) - - writeEnableSynced = CE.safeDffSynchronizer clkRead clkWrite False writeEnable - - writeData = mux writeEnableSynced (Just <$> wdata) (pure Nothing) - -{-# NOINLINE resettableXilinxElasticBuffer #-} -resettableXilinxElasticBuffer :: - forall n readDom writeDom a. - ( KnownDomain writeDom - , KnownDomain readDom - , NFDataX a - , KnownNat n - , 4 <= n - , n <= 17 - ) => - Clock readDom -> - Clock writeDom -> - -- | Resetting resets the 'Underflow' and 'Overflow' signals, but not the 'RelDataCount' - -- ones. Make sure to hold the reset at least 3 cycles in both clock domains. - Reset readDom -> - Signal writeDom a -> - ( Signal readDom (RelDataCount n) - , Signal readDom Underflow - , Signal readDom Overflow - , Signal readDom EbMode - , Signal readDom a - ) -resettableXilinxElasticBuffer clkRead clkWrite rstRead wdata = - (dataCount, under, over1, ebMode, readData) - where - (dataCount, under, over, readData) = - xilinxElasticBuffer @n clkRead clkWrite fifoReset ebMode wdata - fifoReset = unsafeFromActiveHigh $ not <$> stable - over1 = CE.safeDffSynchronizer clkWrite clkRead False over - - controllerReset = unsafeFromActiveHigh (unsafeToActiveHigh rstRead .||. under .||. over1) - - (ebMode, stable) = - unbundle - $ withClockResetEnable clkRead controllerReset enableGen - $ mealy goControl Drain dataCount - - goControl :: EbMode -> RelDataCount n -> (EbMode, (EbMode, Bool)) - goControl state0 datacount = (state1, (state0, stable0)) - where - state1 = - case state0 of - Drain - | datacount == minBound -> Fill - | otherwise -> Drain - Fill - | datacount < targetDataCount -> Fill - | otherwise -> Pass - Pass -> state0 - - stable0 = state0 == Pass diff --git a/bittide/src/Bittide/Ethernet/Mac.hs b/bittide/src/Bittide/Ethernet/Mac.hs deleted file mode 100644 index b5b504816..000000000 --- a/bittide/src/Bittide/Ethernet/Mac.hs +++ /dev/null @@ -1,552 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE PostfixOperators #-} -{-# LANGUAGE RecordWildCards #-} - -module Bittide.Ethernet.Mac where - -import Clash.Explicit.Prelude hiding ((:<)) - -import Bittide.Extra.Maybe -import Bittide.SharedTypes -import Bittide.Wishbone -import Clash.Annotations.Primitive -import Clash.Cores.Xilinx.Ethernet.Gmii.Internal -import Data.Constraint.Nat.Extra -import Data.List.Infinite (Infinite ((:<)), (...)) -import Data.Maybe -import Data.String.Interpolate (__i) -import Protocols.Axi4.Stream -import Protocols.Internal -import Protocols.Wishbone - -import qualified Clash.Prelude as CP - -data EthMacStatus = EthMacStatus - { txFifoUnderflow :: "txFifoUnderflow" ::: Bool - , txFifoOverflow :: "txFifoOverflow" ::: Bool - , txFifoBadFrame :: "txFifoBadFrame" ::: Bool - , txFifoGoodFrame :: "txFifoGoodFrame" ::: Bool - , rxBadFrame :: "rxBadFrame" ::: Bool - , rxBadFcs :: "rxBadFcs" ::: Bool - , rxFifoOverflow :: "rxFifoOverflow" ::: Bool - , rxFifoBadFrame :: "rxFifoBadFrame" ::: Bool - , rxFifoGoodFrame :: "rxFifoGoodFrame" ::: Bool - } - deriving (Generic, NFDataX, BitPack) - -{- | Wishbone peripheral that keeps track of the status flags of the Ethernet MAC. -Every cycle that a flag is set, will be counted with a counter. The width of the counters -is configurable using the first `SNat counterWidth` argument. --} -macStatusInterfaceWb :: - forall dom aw nBytes counterWidth. - ( CP.HiddenClockResetEnable dom - , KnownNat nBytes - , KnownNat aw - , 2 <= aw - , 1 <= nBytes - , counterWidth <= nBytes * 8 - ) => - -- | Number of bits of the counters - SNat counterWidth -> - Circuit - (Wishbone dom 'Standard aw (Bytes nBytes), CSignal dom EthMacStatus) - () -macStatusInterfaceWb SNat = case (cancelMulDiv @nBytes @8) of - Dict -> Circuit circuitGo - where - circuitGo ((wbM2S, macStatus), _) = ((wbS2M, pure ()), ()) - where - (_, wbS2M) = unbundle $ wbToVec <$> counts <*> wbM2S - pulses = fmap bitCoerce macStatus :: Signal dom (Vec (BitSize EthMacStatus) Bool) - extendF = signExtend @_ @_ @(nBytes * 8 - counterWidth) - counts = bundle $ fmap (fmap (pack . extendF)) $ pulseCount <$> unbundle pulses - pulseCount :: Signal dom Bool -> Signal dom (Unsigned counterWidth) - pulseCount pulse = cnt - where - cnt = CP.regEn 0 (CP.isRising True pulse) (succ <$> cnt) - -ethMac1GFifoC :: - ( KnownDomain sys - , KnownDomain tx - , KnownDomain rx - ) => - -- Configuration - - -- | TX FIFO depth - SNat txFifoDepth -> - -- | RX FIFO depth - SNat rxFifoDepth -> - -- Clocks and resets - - -- | Logic clock - Clock sys -> - -- | Logic reset - Reset sys -> - -- | TX clock - Clock tx -> - -- | TX reset - Reset tx -> - -- | RX clock - Clock rx -> - -- | RX reset - Reset rx -> - -- | Mii select - Signal rx Bool -> - -- | TX Clock enable - Signal tx Bool -> - -- | RX Clock enable - Signal rx Bool -> - Circuit - (Axi4Stream sys ('Axi4StreamConfig 1 0 0) Bool, CSignal rx Gmii) - (Axi4Stream sys ('Axi4StreamConfig 1 0 0) Bool, CSignal tx Gmii, CSignal sys EthMacStatus) -ethMac1GFifoC - txFifoDepth - rxFifoDepth - sysClk - sysRst - txClk - txRst - rxClk - rxRst - miiSel - txClkEna - rxClkEna = Circuit go - where - go ((axiTxM2S, gmiiRx), (axiRxS2M, _, _)) = ((axiTxS2M, pure ()), (axiRxM2S, gmiiTx, ethStatus)) - where - (axiTxS2M, axiRxM2S, gmiiTx, ethStatus) = - ethMac1GFifo - txFifoDepth - rxFifoDepth - sysClk - sysRst - txClk - txRst - rxClk - rxRst - miiSel - txClkEna - rxClkEna - axiTxM2S - gmiiRx - axiRxS2M - -ethMac1GFifo :: - ( KnownDomain sys - , KnownDomain tx - , KnownDomain rx - ) => - -- Configuration - - -- | TX FIFO depth - SNat txFifoDepth -> - -- | RX FIFO depth - SNat rxFifoDepth -> - -- Clocks and resets - - -- | Logic clock - Clock sys -> - -- | Logic reset - Reset sys -> - -- | TX clock - Clock tx -> - -- | TX reset - Reset tx -> - -- | RX clock - Clock rx -> - -- | RX reset - Reset rx -> - -- | Mii select - Signal rx Bool -> - -- | TX Clock enable - Signal tx Bool -> - -- | RX Clock enable - Signal rx Bool -> - -- TX Axi inputs - Signal sys (Maybe (Axi4StreamM2S ('Axi4StreamConfig 1 0 0) Bool)) -> - -- RX Gmii inputs - Signal rx Gmii -> - -- RX Axi inputs - Signal sys Axi4StreamS2M -> - ( -- TX Axi outputs - Signal sys Axi4StreamS2M - , -- RX Axi outputs - Signal sys (Maybe (Axi4StreamM2S ('Axi4StreamConfig 1 0 0) Bool)) - , -- GMII outputs - Signal tx Gmii - , -- TX Status - Signal sys EthMacStatus - ) -ethMac1GFifo - txFifoDepth - rxFifoDepth - sysClk - sysRst - txClk - txRst - rxClk - rxRst - miiSel - txClkEna - rxClkEna - txAxiM2S - rxGmii - rxAxiS2M = - (txAxiS2M, rxAxiM2S, gmiiTx, ethStatus) - where - txAxiS2M = Axi4StreamS2M <$> txAxiReady - - txAxiData = _tdata . fromJust <$> txAxiM2S - txAxiKeep = _tkeep . fromJust <$> txAxiM2S - txAxiValid = isJust <$> txAxiM2S - txAxiLast = _tlast . fromJust <$> txAxiM2S - txAxiUser = _tuser . fromJust <$> txAxiM2S - - rxAxiReady = _tready <$> rxAxiS2M - gmiiRxData' = bitCoerce . gmiiData <$> rxGmii - gmiiRxValid' = bitCoerce . gmiiValid <$> rxGmii - gmiiRxError' = bitCoerce . gmiiError <$> rxGmii - - -- Instantiate the blackbox - gmiiTx = Gmii <$> gmiiTxData' <*> fmap bitCoerce gmiiTxEnable' <*> fmap bitCoerce gmiiTxError' - ( txAxiReady - , rxAxiData - , rxAxiKeep - , rxAxiValid - , rxAxiLast - , rxAxiUser - , gmiiTxData' - , gmiiTxEnable' - , gmiiTxError' - , txFifoUnderflow - , txFifoOverflow - , txFifoBadFrame - , txFifoGoodFrame - , rxBadFrame - , rxBadFcs - , rxFifoOverflow - , rxFifoBadFrame - , rxFifoGoodFrame - ) = - ethMac1GFifoBb - txFifoDepth - rxFifoDepth - sysClk - sysRst - txClk - txRst - rxClk - rxRst - miiSel - txClkEna - rxClkEna - txAxiData - txAxiKeep - txAxiValid - txAxiLast - txAxiUser - gmiiRxData' - gmiiRxValid' - gmiiRxError' - rxAxiReady - - ethStatus = - makeEthStatus - <$> txFifoUnderflow - <*> txFifoOverflow - <*> txFifoBadFrame - <*> txFifoGoodFrame - <*> rxBadFrame - <*> rxBadFcs - <*> rxFifoOverflow - <*> rxFifoBadFrame - <*> rxFifoGoodFrame - - rxAxiM2S = makeAxi <$> rxAxiValid <*> rxAxiData <*> rxAxiKeep <*> rxAxiUser <*> rxAxiLast - -{- | Utility function to create an Axi4StreamM2S from it's components. -Exists to explicitly show the order of the arguments closely to the code that uses it. --} -makeAxi :: - (KnownNat n) => - Bool -> - Vec n (Unsigned 8) -> - Vec n Bool -> - Bool -> - Bool -> - Maybe (Axi4StreamM2S ('Axi4StreamConfig n 0 0) Bool) -makeAxi _tvalid _tdata _tkeep _tuser _tlast = orNothing _tvalid Axi4StreamM2S{..} - where - _tid = 0 - _tdest = 0 - _tstrb = repeat True - -{- | Utility function to create the EthMacStatus from it's components. -Exists to explicitly show the order of the arguments closely to the code that uses it. --} -makeEthStatus :: - Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> EthMacStatus -makeEthStatus - txFifoUnderflow - txFifoOverflow - txFifoBadFrame - txFifoGoodFrame - rxBadFrame - rxBadFcs - rxFifoOverflow - rxFifoBadFrame - rxFifoGoodFrame = EthMacStatus{..} - --- | 1G Ethernet MAC with TX and RX FIFOs -ethMac1GFifoBb :: - ( KnownDomain sys - , KnownDomain tx - , KnownDomain rx - ) => - -- Configuration - - -- | TX FIFO depth - SNat txFifoDepth -> - -- | RX FIFO depth - SNat rxFifoDepth -> - -- Clocks and resets - - -- | Logic clock - Clock sys -> - -- | Logic reset - Reset sys -> - -- | TX clock - Clock tx -> - -- | TX reset - Reset tx -> - -- | RX clock - Clock rx -> - -- | RX reset - Reset rx -> - -- | Mii select - Signal rx Bool -> - -- | TX Clock enable - Signal tx Bool -> - -- | RX Clock enable - Signal rx Bool -> - -- TX Axi inputs - - -- | TX Axi data - Signal sys (Vec 1 (Unsigned 8)) -> - -- | TX Axi keep - Signal sys (Vec 1 Bool) -> - -- | TX Axi valid - Signal sys Bool -> - -- | TX Axi last - Signal sys Bool -> - -- | TX Axi user - Signal sys Bool -> - -- RX Gmii inputs - - -- | RX Gmii data - Signal rx (BitVector 8) -> - -- | RX Gmii valid - Signal rx Bool -> - -- | RX Gmii error - Signal rx Bool -> - -- RX Axi inputs - - -- | RX Axi ready - Signal sys Bool -> - ( -- TX Axi outputs - -- \| TX Axi ready - Signal sys Bool - , -- RX Axi outputs - -- \| RX Axi data - Signal sys (Vec 1 (Unsigned 8)) - , -- \| RX Axi keep - Signal sys (Vec 1 Bool) - , -- \| RX Axi valid - Signal sys Bool - , -- \| RX Axi last - Signal sys Bool - , -- \| RX Axi user - Signal sys Bool - , -- GMII outputs - -- \| GMII TX data - Signal tx (BitVector 8) - , -- \| GMII TX enable - Signal tx Bool - , -- \| GMII TX error - Signal tx Bool - , -- TX Status - -- \| TX FIFO underflow - Signal sys Bool - , -- \| TX FIFO overflow - Signal sys Bool - , -- \| TX FIFO bad frame - Signal sys Bool - , -- \| TX FIFO good frame - Signal sys Bool - , -- RX Status - -- \| RX error bad frame - Signal sys Bool - , -- \| RX error bad FCS - Signal sys Bool - , -- \| RX FIFO overflow - Signal sys Bool - , -- \| RX FIFO bad frame - Signal sys Bool - , -- \| RX FIFO good frame - Signal sys Bool - ) -ethMac1GFifoBb SNat SNat !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ !_ = - (err, err, err, err, err, err, err, err, err, err, err, err, err, err, err, err, err, err) - where - err :: forall dom a. (NFDataX a) => Signal dom a - err = pure $ deepErrorX "simulation model not implemented" -{-# NOINLINE ethMac1GFifoBb #-} -{-# ANN ethMac1GFifoBb hasBlackBox #-} -{-# ANN - ethMac1GFifoBb - ( let - ( _sys - :< _tx - :< _rx - :< txFifoDepth - :< rxFifoDepth - :< sysClk - :< sysRst - :< txClk - :< txRst - :< rxClk - :< rxRst - :< miiSel - :< txClkEna - :< rxClkEna - :< txAxiData - :< txAxiKeep - :< txAxiValid - :< txAxiLast - :< txAxiUser - :< gmiiRxData - :< gmiiRxValid - :< gmiiRxError - :< rxAxiReady - :< txAxiReady - :< rxAxiData - :< rxAxiKeep - :< rxAxiValid - :< rxAxiLast - :< rxAxiUser - :< gmiiTxData - :< gmiiTxEn - :< gmiiTxErr - :< txFifoUnderflow - :< txFifoOverflow - :< txFifoBadFrame - :< txFifoGoodFrame - :< rxBadFrame - :< rxBadFcs - :< rxFifoOverflow - :< rxFifoBadFrame - :< rxFifoGoodFrame - :< _ - ) = ((0 :: Int) ...) - funcName = 'ethMac1GFifoBb - in - InlineYamlPrimitive - [Verilog] - [__i| - BlackBox: - kind: Declaration - name: #{funcName} - template: |- - wire ~GENSYM[txAxiReady][#{txAxiReady}]; - wire [7:0] ~GENSYM[rxAxiData][#{rxAxiData}]; - wire ~GENSYM[rxAxiKeep][#{rxAxiKeep}]; - wire ~GENSYM[rxAxiValid][#{rxAxiValid}]; - wire ~GENSYM[rxAxiLast][#{rxAxiLast}]; - wire ~GENSYM[rxAxiUser][#{rxAxiUser}]; - wire [7:0] ~GENSYM[gmiiTxData][#{gmiiTxData}]; - wire ~GENSYM[gmiiTxEn][#{gmiiTxEn}]; - wire ~GENSYM[gmiiTxErr][#{gmiiTxErr}]; - wire ~GENSYM[txFifoUnderflow][#{txFifoUnderflow}]; - wire ~GENSYM[txFifoOverflow][#{txFifoOverflow}]; - wire ~GENSYM[txFifoBadFrame][#{txFifoBadFrame}]; - wire ~GENSYM[txFifoGoodFrame][#{txFifoGoodFrame}]; - wire ~GENSYM[rxBadFrame][#{rxBadFrame}]; - wire ~GENSYM[rxBadFcs][#{rxBadFcs}]; - wire ~GENSYM[rxFifoOverflow][#{rxFifoOverflow}]; - wire ~GENSYM[rxFifoBadFrame][#{rxFifoBadFrame}]; - wire ~GENSYM[rxFifoGoodFrame][#{rxFifoGoodFrame}]; - assign ~RESULT = - { ~SYM[#{txAxiReady}] - , ~SYM[#{rxAxiData}] - , ~SYM[#{rxAxiKeep}] - , ~SYM[#{rxAxiValid}] - , ~SYM[#{rxAxiLast}] - , ~SYM[#{rxAxiUser}] - , ~SYM[#{gmiiTxData}] - , ~SYM[#{gmiiTxEn}] - , ~SYM[#{gmiiTxErr}] - , ~SYM[#{txFifoUnderflow}] - , ~SYM[#{txFifoOverflow}] - , ~SYM[#{txFifoBadFrame}] - , ~SYM[#{txFifoGoodFrame}] - , ~SYM[#{rxBadFrame}] - , ~SYM[#{rxBadFcs}] - , ~SYM[#{rxFifoOverflow}] - , ~SYM[#{rxFifoBadFrame}] - , ~SYM[#{rxFifoGoodFrame}] - }; - eth_mac_1g_fifo \#( - .TX_FIFO_DEPTH(~ARG[#{txFifoDepth}]), - .RX_FIFO_DEPTH(~ARG[#{rxFifoDepth}]) - ) - eth_mac_1g_fifo_inst - ( - .rx_clk(~ARG[#{rxClk}]), - .rx_rst(~ARG[#{rxRst}]), - .tx_clk(~ARG[#{txClk}]), - .tx_rst(~ARG[#{txRst}]), - .logic_clk(~ARG[#{sysClk}]), - .logic_rst(~ARG[#{sysRst}]), - .tx_axis_tdata(~ARG[#{txAxiData}]), - .tx_axis_tkeep(~ARG[#{txAxiKeep}]), - .tx_axis_tvalid(~ARG[#{txAxiValid}]), - .tx_axis_tlast(~ARG[#{txAxiLast}]), - .tx_axis_tuser(~ARG[#{txAxiUser}]), - .rx_axis_tready(~ARG[#{rxAxiReady}]), - .tx_axis_tready(~SYM[#{txAxiReady}]), - .rx_axis_tdata(~SYM[#{rxAxiData}]), - .rx_axis_tkeep(~SYM[#{rxAxiKeep}]), - .rx_axis_tvalid(~SYM[#{rxAxiValid}]), - .rx_axis_tlast(~SYM[#{rxAxiLast}]), - .rx_axis_tuser(~SYM[#{rxAxiUser}]), - .gmii_rxd(~ARG[#{gmiiRxData}]), - .gmii_rx_dv(~ARG[#{gmiiRxValid}]), - .gmii_rx_er(~ARG[#{gmiiRxError}]), - .gmii_txd(~SYM[#{gmiiTxData}]), - .gmii_tx_en(~SYM[#{gmiiTxEn}]), - .gmii_tx_er(~SYM[#{gmiiTxErr}]), - .rx_clk_enable(~ARG[#{rxClkEna}]), - .tx_clk_enable(~ARG[#{txClkEna}]), - .rx_mii_select(~ARG[#{miiSel}]), - .tx_mii_select(~ARG[#{miiSel}]), - .tx_error_underflow(~SYM[#{txFifoUnderflow}]), - .tx_fifo_overflow(~SYM[#{txFifoOverflow}]), - .tx_fifo_bad_frame(~SYM[#{txFifoBadFrame}]), - .tx_fifo_good_frame(~SYM[#{txFifoGoodFrame}]), - .rx_error_bad_frame(~SYM[#{rxBadFrame}]), - .rx_error_bad_fcs(~SYM[#{rxBadFcs}]), - .rx_fifo_overflow(~SYM[#{rxFifoOverflow}]), - .rx_fifo_bad_frame(~SYM[#{rxFifoBadFrame}]), - .rx_fifo_good_frame(~SYM[#{rxFifoGoodFrame}]), - - .cfg_ifg(8'd12), - .cfg_tx_enable(1'b1), - .cfg_rx_enable(1'b1) - ); - |] - ) - #-} diff --git a/bittide/src/Bittide/Node.hs b/bittide/src/Bittide/Node.hs deleted file mode 100644 index c4b50aec8..000000000 --- a/bittide/src/Bittide/Node.hs +++ /dev/null @@ -1,193 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GADTs #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=6 #-} - -module Bittide.Node where - -import Clash.Prelude -import Clash.Sized.Vector.ToTuple (vecToTuple) - -import Protocols -import Protocols.Wishbone - -import VexRiscv - -import Bittide.Calendar -import Bittide.DoubleBufferedRam -import Bittide.ProcessingElement -import Bittide.ScatterGather -import Bittide.SharedTypes -import Bittide.Switch - -import Control.Arrow ((&&&)) - -{- | A simple node consisting of one external bidirectional link and two 'gppe's. -This node's 'switch' has a 'CalendarConfig' of for a 'calendar' with up to @1024@ entries, -however, the 'calendar' is initialized with a single entry of repeated zeroes. -The 'scatterUnitWb's and 'gatherUnitWb's are initialized with 'CalendarConfig's of all -zeroes. The 'gppe's initial memories are both undefined and the 'MemoryMap' is a -vector of ever increasing base addresses (increments of 0x1000). --} -simpleNodeConfig :: NodeConfig 1 2 -simpleNodeConfig = - NodeConfig - (ManagementConfig (ScatterConfig sgConfig) (GatherConfig sgConfig) nmuConfig) - switchCal - (repeat (GppeConfig (ScatterConfig sgConfig) (GatherConfig sgConfig) peConfig)) - where - switchCal = CalendarConfig (SNat @1024) (switchEntry :> Nil) (switchEntry :> Nil) - sgConfig = CalendarConfig (SNat @1024) (sgEntry :> Nil) (sgEntry :> Nil) - peConfig = PeConfig memMapPe (Undefined @8192) (Undefined @8192) - nmuConfig = PeConfig memMapNmu (Undefined @8192) (Undefined @8192) - memMapPe = iterateI (+ 0x1000) 0 - memMapNmu = iterateI (+ 0x1000) 0 - switchEntry = ValidEntry{veEntry = repeat 0, veRepeat = 0 :: Unsigned 0} - sgEntry = ValidEntry{veEntry = 0 :: Index 1024, veRepeat = 0 :: Unsigned 0} - -{- | Each 'gppe' results in 2 busses for the 'managementUnit', namely: -* The 'calendar' for the 'scatterUnitWB'. -* The 'calendar' for the 'gatherUnitWB'. --} -type BussesPerGppe = 2 - --- | Configuration of a 'node'. -data NodeConfig externalLinks gppes where - NodeConfig :: - ( KnownNat nmuBusses - , nmuBusses ~ ((BussesPerGppe * gppes) + 1 + NmuInternalBusses) - , KnownNat nmuRemBusWidth - , nmuRemBusWidth ~ (30 - CLog 2 nmuBusses) - , CLog 2 nmuBusses <= 30 - ) => - -- | Configuration for the 'node's 'managementUnit'. - ManagementConfig ((BussesPerGppe * gppes) + 1) -> - -- | Configuratoin for the 'node's 'switch'. - CalendarConfig 4 nmuRemBusWidth (CalendarEntry (externalLinks + gppes + 1)) -> - -- | Configuration for all the node's 'gppe's. - Vec gppes (GppeConfig nmuRemBusWidth) -> - NodeConfig externalLinks gppes - --- | A 'node' consists of a 'switch', 'managementUnit' and @0..n@ 'gppe's. -node :: - forall dom extLinks gppes. - (HiddenClockResetEnable dom, KnownNat extLinks, KnownNat gppes) => - NodeConfig extLinks gppes -> - Vec extLinks (Signal dom (DataLink 64)) -> - Vec extLinks (Signal dom (DataLink 64)) -node (NodeConfig nmuConfig switchConfig gppeConfigs) linksIn = linksOut - where - (switchOut, swS2M) = switch switchConfig swM2S switchIn - switchIn = nmuToSwitch :> pesToSwitch ++ linksIn - (splitAtI -> ((head &&& tail) -> (switchToNmu, switchToPes), linksOut)) = switchOut - (nmuToSwitch, nmuM2Ss) = managementUnit nmuConfig switchToNmu nmuS2Ms - (swM2S, peM2Ss) = (head &&& tail) nmuM2Ss - - nmuS2Ms = swS2M :> peS2Ms - - (pesToSwitch, concat -> peS2Ms) = - unzip $ gppe <$> zip3 gppeConfigs switchToPes (unconcatI peM2Ss) - -type NmuInternalBusses = 6 -type NmuRemBusWidth nodeBusses = 30 - CLog 2 (nodeBusses + NmuInternalBusses) - -{- | Configuration for the 'managementUnit' and its 'Bittide.Link'. -The management unit contains the 4 wishbone busses that each pe has -and also the management busses for itself and all other pe's in this node. -Furthermore it also has access to the 'calendar' for the 'switch'. --} -data ManagementConfig nodeBusses where - ManagementConfig :: - (KnownNat nodeBusses) => - ScatterConfig 4 (NmuRemBusWidth nodeBusses) -> - GatherConfig 4 (NmuRemBusWidth nodeBusses) -> - PeConfig (nodeBusses + NmuInternalBusses) -> - ManagementConfig nodeBusses - -{- | Configuration for a general purpose processing element together with its link to the -switch. --} -data GppeConfig nmuRemBusWidth where - GppeConfig :: - ScatterConfig 4 nmuRemBusWidth -> - GatherConfig 4 nmuRemBusWidth -> - -- | Configuration for a 'gppe's 'processingElement', which statically - -- has four external busses connected to the instruction memory, data memory - -- , 'scatterUnitWb' and 'gatherUnitWb'. - PeConfig 4 -> - GppeConfig nmuRemBusWidth - -{-# NOINLINE gppe #-} - -{- | A general purpose 'processingElement' to be part of a Bittide Node. It contains -a 'processingElement', 'linkToPe' and 'peToLink' which create the interface for the -Bittide Link. It takes a 'GppeConfig', incoming link and two incoming 'WishboneM2S' -signals and produces the outgoing link alongside two 'WishhboneS2M' signals. -The order of Wishbone busses is as follows: -('scatterUnitWb' :> 'gatherUnitWb' :> Nil). --} -gppe :: - ( HiddenClockResetEnable dom - , KnownNat nmuRemBusWidth - ) => - -- | - -- ( Configures all local parameters - -- , Incoming 'Bittide.Link' - -- , Incoming @Vector@ of master busses - -- ) - ( GppeConfig nmuRemBusWidth - , Signal dom (DataLink 64) - , Vec 2 (Signal dom (WishboneM2S nmuRemBusWidth 4 (Bytes 4))) - ) -> - -- | - -- ( Outgoing 'Bittide.Link' - -- , Outgoing @Vector@ of slave busses - -- ) - ( Signal dom (DataLink 64) - , Vec 2 (Signal dom (WishboneS2M (Bytes 4))) - ) -gppe (GppeConfig scatterConfig gatherConfig peConfig, linkIn, vecToTuple -> (nmuM2S0, nmuM2S1)) = - (linkOut, nmuS2M0 :> nmuS2M1 :> Nil) - where - (suS2M, nmuS2M0) = scatterUnitWb scatterConfig nmuM2S0 linkIn suM2S - (linkOut, guS2M, nmuS2M1) = gatherUnitWb gatherConfig nmuM2S1 guM2S - (_, wbM2Ss) = toSignals (processingElement peConfig) (pure $ JtagIn low low low, wbS2Ms) - (suM2S, guM2S) = vecToTuple wbM2Ss - wbS2Ms = suS2M :> guS2M :> Nil - -{-# NOINLINE managementUnit #-} - -{- | A special purpose 'processingElement' that manages a Bittide Node. It contains -a 'processingElement', 'linkToPe' and 'peToLink' which create the interface for the -Bittide Link. It takes a 'ManagementConfig', incoming link and a vector of incoming -'WishboneS2M' signals and produces the outgoing link alongside a vector of -'WishhboneM2S' signals. --} -managementUnit :: - forall dom nodeBusses. - ( HiddenClockResetEnable dom - , KnownNat nodeBusses - , CLog 2 (nodeBusses + NmuInternalBusses) <= 30 - ) => - -- | Configures all local parameters. - ManagementConfig nodeBusses -> - -- | Incoming 'Bittide.Link'. - Signal dom (DataLink 64) -> - -- | Incoming @Vector@ of slave busses. - Vec nodeBusses (Signal dom (WishboneS2M (Bytes 4))) -> - -- | - -- ( Outgoing 'Bittide.Link' - -- , Outgoing @Vector@ of master busses) - ( Signal dom (DataLink 64) - , Vec nodeBusses (Signal dom (WishboneM2S (NmuRemBusWidth nodeBusses) 4 (Bytes 4))) - ) -managementUnit (ManagementConfig scatterConfig gatherConfig peConfig) linkIn nodeS2Ms = - (linkOut, nodeM2Ss) - where - (suS2M, nmuS2M0) = scatterUnitWb scatterConfig nmuM2S0 linkIn suM2S - (linkOut, guS2M, nmuS2M1) = gatherUnitWb gatherConfig nmuM2S1 guM2S - (vecToTuple -> (suM2S, guM2S), rest) = splitAtI nmuM2Ss - (vecToTuple -> (nmuM2S0, nmuM2S1), nodeM2Ss) = splitAtI rest - (_, nmuM2Ss) = toSignals (processingElement peConfig) (pure $ JtagIn low low low, nmuS2Ms) - nmuS2Ms = suS2M :> guS2M :> nmuS2M0 :> nmuS2M1 :> nodeS2Ms diff --git a/bittide/src/Bittide/ProcessingElement.hs b/bittide/src/Bittide/ProcessingElement.hs deleted file mode 100644 index 2270d831a..000000000 --- a/bittide/src/Bittide/ProcessingElement.hs +++ /dev/null @@ -1,158 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} - -{-# OPTIONS -fplugin=Protocols.Plugin #-} - -module Bittide.ProcessingElement where - -import Clash.Explicit.Prelude (unsafeOrReset) -import Clash.Prelude - -import Protocols -import Protocols.Wishbone -import VexRiscv (CpuIn (..), CpuOut (..), Jtag, JtagOut (debugReset), vexRiscv) - -import Bittide.DoubleBufferedRam -import Bittide.Extra.Maybe -import Bittide.SharedTypes -import Bittide.Wishbone - -import Clash.Cores.Xilinx.Ila (Depth (D4096)) - -import qualified Data.ByteString as BS - --- | Configuration for a Bittide Processing Element. -data PeConfig nBusses where - PeConfig :: - ( KnownNat depthI - , 1 <= depthI - , KnownNat depthD - , 1 <= depthD - , KnownNat nBusses - , 2 <= nBusses - , CLog 2 nBusses <= 30 - ) => - -- | The 'MemoryMap' for the contained 'singleMasterInterconnect'. - MemoryMap nBusses -> - -- | Initial content of the instruction memory, can be smaller than its total depth. - InitialContent depthI (Bytes 4) -> - -- | Initial content of the data memory, can be smaller than its total depth. - InitialContent depthD (Bytes 4) -> - PeConfig nBusses - -{- | VexRiscV based RV32IMC core together with instruction memory, data memory and -'singleMasterInterconnect'. --} -processingElement :: - forall dom nBusses. - (HiddenClockResetEnable dom) => - PeConfig nBusses -> - Circuit - (Jtag dom) - (Vec (nBusses - 2) (Wishbone dom 'Standard (MappedBusAddrWidth 30 nBusses) (Bytes 4))) -processingElement (PeConfig memMapConfig initI initD) = circuit $ \jtagIn -> do - (iBus0, dBus0) <- rvCircuit (pure low) (pure low) (pure low) -< jtagIn - iBus1 <- ilaWb (SSymbol @"instructionBus") 2 D4096 -< iBus0 - dBus1 <- ilaWb (SSymbol @"dataBus") 2 D4096 -< dBus0 - ([iMemBus, dMemBus], extBusses) <- - (splitAtC d2 <| singleMasterInterconnect memMapConfig) -< dBus1 - wbStorage initD -< dMemBus - iBus2 <- removeMsb -< iBus1 -- XXX: <= This should be handled by an interconnect - wbStorageDPC initI -< (iBus2, iMemBus) - idC -< extBusses - where - removeMsb :: - forall aw a. - (KnownNat aw) => - Circuit - (Wishbone dom 'Standard (aw + 4) a) - (Wishbone dom 'Standard aw a) - removeMsb = wbMap (mapAddr (truncateB :: BitVector (aw + 4) -> BitVector aw)) id - - wbMap fwd bwd = Circuit $ \(m2s, s2m) -> (fmap bwd s2m, fmap fwd m2s) - --- | Conceptually the same as 'splitAt', but for 'Circuit's -splitAtC :: - SNat left -> - Circuit (Vec (left + right) a) (Vec left a, Vec right a) -splitAtC SNat = Circuit go - where - go (fwd, (bwdLeft, bwdRight)) = (bwd, (fwdLeft, fwdRight)) - where - (fwdLeft, fwdRight) = splitAtI fwd - bwd = bwdLeft ++ bwdRight - -rvCircuit :: - (HiddenClockResetEnable dom) => - Signal dom Bit -> - Signal dom Bit -> - Signal dom Bit -> - Circuit - (Jtag dom) - ( Wishbone dom 'Standard 30 (Bytes 4) - , Wishbone dom 'Standard 30 (Bytes 4) - ) -rvCircuit tInterrupt sInterrupt eInterrupt = Circuit go - where - go (jtagIn, (iBusIn, dBusIn)) = (jtagOut, (iBusWbM2S <$> cpuOut, dBusWbM2S <$> cpuOut)) - where - tupToCoreIn (timerInterrupt, softwareInterrupt, externalInterrupt, iBusWbS2M, dBusWbS2M) = CpuIn{..} - rvIn = tupToCoreIn <$> bundle (tInterrupt, sInterrupt, eInterrupt, iBusIn, dBusIn) - (cpuOut, jtagOut) = vexRiscv hasClock (hasReset `unsafeOrReset` jtagReset) rvIn jtagIn - jtagReset = unsafeFromActiveHigh (delay False (bitToBool . debugReset <$> jtagOut)) - --- | Map a function over the address field of 'WishboneM2S' -mapAddr :: - (BitVector aw1 -> BitVector aw2) -> - WishboneM2S aw1 selWidth a -> - WishboneM2S aw2 selWidth a -mapAddr f wb = wb{addr = f (addr wb)} - -{- | Stateless wishbone device that only acknowledges writes to address 0. -Successful writes return the 'writeData' and 'busSelect'. --} -wishboneSink :: - (KnownNat addressWidth, Paddable dat) => - -- | Incoming wishbone bus. - Signal dom (WishboneM2S addressWidth bs dat) -> - -- | - -- 1. Outgoing wishbone bus. - -- 2. Result of successful write attempt. - Signal dom (WishboneS2M dat, Maybe (BitVector bs, dat)) -wishboneSink = fmap go - where - go WishboneM2S{..} = (wbOut, output) - where - masterActive = busCycle && strobe - addrLegal = addr == 0 - acknowledge = masterActive && writeEnable && addrLegal - err = masterActive && (not writeEnable || not addrLegal) - - output = orNothing acknowledge (busSelect, writeData) - wbOut = emptyWishboneS2M{acknowledge, err} - -{- | Provide a vector of filepaths, and a write operations containing a byteSelect and -a vector of characters and, for each filepath write the corresponding byte to that file -if the corresponding byteSelect is @1@. --} -printCharacters :: - (KnownNat paths, KnownNat chars, (paths + n) ~ chars) => - -- | Destination files for received bytes. - Vec paths FilePath -> - -- | Write attempt, bytes will only be written if the corresponding byteSelect is @1@. - Maybe (BitVector chars, Vec chars Byte) -> - IO () -printCharacters Nil _ = pure () -printCharacters paths@(Cons _ _) inps = case inps of - Just (byteSelect, chars) -> - sequence_ $ printToFiles <*> take SNat (unpack byteSelect) <*> take SNat chars - Nothing -> pure () - where - printToFiles = printToFile <$> paths - printToFile path byteSelect char - | byteSelect = BS.appendFile path $ BS.singleton $ bitCoerce char - | otherwise = pure () diff --git a/bittide/src/Bittide/ProcessingElement/DeviceTreeCompiler.hs b/bittide/src/Bittide/ProcessingElement/DeviceTreeCompiler.hs deleted file mode 100644 index 186265c74..000000000 --- a/bittide/src/Bittide/ProcessingElement/DeviceTreeCompiler.hs +++ /dev/null @@ -1,59 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Bittide.ProcessingElement.DeviceTreeCompiler ( - compileDeviceTreeSource, -) where - -import Prelude - -import Data.Char (isSpace) -import Data.List (dropWhileEnd) -import System.Exit -import System.IO (hPutStrLn) -import System.Process - -import System.IO.Temp.Extra - -import qualified Data.ByteString as BS -import qualified System.IO as IO - -findDtc :: IO (Maybe FilePath) -findDtc = do - let process = shell "which dtc" - (exitcode, stdout, _) <- readCreateProcessWithExitCode process "" - - case exitcode of - ExitSuccess -> pure . Just $ dropWhileEnd isSpace stdout - ExitFailure _ -> pure Nothing - -compileDeviceTreeSource :: FilePath -> IO (Maybe BS.ByteString) -compileDeviceTreeSource src = withTempBinaryFile "tmp" "fdt.dtb" $ \path _ -> do - dtcPathRes <- findDtc - case dtcPathRes of - Nothing -> do - hPutStrLn - IO.stderr - "Unable to find device tree compiler on the system. Are you in a Nix shell?" - pure Nothing - Just dtc -> do - (exitCode, stdout, stderr) <- - readProcessWithExitCode - dtc - ["-O", "dtb", "-b", "0", src, "-o", path] -- args - "" -- stdin - case exitCode of - ExitSuccess -> do - content <- BS.readFile path - pure $ Just content - ExitFailure n -> do - hPutStrLn IO.stderr $ "devicetree compilation failed with exit code: " <> show n - hPutStrLn IO.stderr $ "devicetree-source file: " <> src - hPutStrLn IO.stderr "" - hPutStrLn IO.stderr "stdout:" - hPutStrLn IO.stderr stdout - hPutStrLn IO.stderr "" - hPutStrLn IO.stderr "stderr:" - hPutStrLn IO.stderr stderr - pure Nothing diff --git a/bittide/src/Bittide/ProcessingElement/ProgramStream.hs b/bittide/src/Bittide/ProcessingElement/ProgramStream.hs deleted file mode 100644 index cef66b4eb..000000000 --- a/bittide/src/Bittide/ProcessingElement/ProgramStream.hs +++ /dev/null @@ -1,93 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Bittide.ProcessingElement.ProgramStream ( - elfStreamStructure, - elfStream, - ZeroPaddingLength, - IsExecutable, - Segment, - Address, -) where - -import Clash.Prelude - -import Data.Elf - -import qualified Data.ByteString as BS -import qualified Data.List as L - -type ZeroPaddingLength = BitVector 32 -type IsExecutable = Bool -type Segment = (IsExecutable, Address, [BitVector 8], ZeroPaddingLength) - -type Address = BitVector 32 - -{- | Parse the contents of an ELF binary and yield the structures -needed for the streaming format. --} -elfStreamStructure :: BS.ByteString -> (Address, [Segment]) -elfStreamStructure contents = - let elf = parseElf contents - in readElf elf - -{- | Generate a streamable format of the contents of an ELF binary. - -The generated stream has the following structure (little endian where needed): - -- entry address (32bit) -- number of segments @s@ (32bit) -- @s@ segment streams - - - bool to indicate if segment is executable or not - - the starting address of the segment in memory (32bit) - - the length of the data @d@ to be transmitted (32bit) - - the amount of zero-padding to add at the end (32bit) - - the segment contents (@d@ bytes) --} -elfStream :: BS.ByteString -> [BitVector 8] -elfStream contents = - let (addr, segs) = elfStreamStructure contents - in bvToLE addr - <> bvToLE (fromIntegral $ L.length segs) - <> L.concatMap segmentStream segs - where - segmentStream :: Segment -> [BitVector 8] - segmentStream (isExec, addr, dat, padding) = - (if isExec then 1 else 0) - : bvToLE addr - <> bvToLE (fromIntegral $ L.length dat) - <> bvToLE padding - <> dat - - bvToLE :: BitVector 32 -> [BitVector 8] - bvToLE (bitCoerce -> (a, b, c, d)) = [d, c, b, a] - -readElf :: Elf -> (Address, [Segment]) -readElf elf = - let segs = L.foldr go [] (elfSegments elf) - in (fromIntegral (elfEntry elf), segs) - where - go seg acc - -- skip segments that don't need loading - | elfSegmentType seg /= PT_LOAD = - acc - | PF_X `elem` elfSegmentFlags seg = - streamSegment True seg : acc - | PF_R `elem` elfSegmentFlags seg = - streamSegment False seg : acc - | otherwise = acc - - bytes str = pack <$> BS.unpack str - - -- stream for a segment - streamSegment isExec seg = - let - segData = elfSegmentData seg - fileSz = fromIntegral $ BS.length segData - memSz = fromIntegral $ elfSegmentMemSize seg - zeroPadding = memSz - fileSz - addr = fromIntegral $ elfSegmentPhysAddr seg - in - (isExec, addr, bytes segData, zeroPadding) diff --git a/bittide/src/Bittide/ProcessingElement/ReadElf.hs b/bittide/src/Bittide/ProcessingElement/ReadElf.hs deleted file mode 100644 index 0c9d9313d..000000000 --- a/bittide/src/Bittide/ProcessingElement/ReadElf.hs +++ /dev/null @@ -1,57 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE PatternGuards #-} - -module Bittide.ProcessingElement.ReadElf (readElf, readElfFromMemory, Address, BinaryData) where - -import Clash.Prelude - -import Data.Elf - -import qualified Data.ByteString as BS -import qualified Data.IntMap.Strict as I -import qualified Data.List as L - -type BinaryData = I.IntMap (BitVector 8) -type Address = BitVector 32 - -readElfFromMemory :: BS.ByteString -> (Address, BinaryData, BinaryData) -readElfFromMemory contents = - let elf = parseElf contents - in readElf elf - -{- | readElf :: elf file -> (initial PC, instructions, data) - -TODO Check the ELF header is valid: is this RISCV? Is it RV32IMC? -TODO Binaries output now are SYS V ABI, are others compatible? --} -readElf :: Elf -> (Address, BinaryData, BinaryData) -readElf elf = - let (iMem, dMem) = L.foldr go (mempty, mempty) (elfSegments elf) - in (fromIntegral (elfEntry elf), iMem, dMem) - where - go seg acc@(is, ds) - -- skip segments that don't need loading - | elfSegmentType seg /= PT_LOAD = - acc - | PF_X `elem` elfSegmentFlags seg = - ( addData - (elfSegmentPhysAddr seg) - (bytes $ elfSegmentData seg `BS.append` BS.pack [0, 0]) - is - , ds - ) - | otherwise = - let - segData = elfSegmentData seg - fileSz = fromIntegral $ BS.length segData - memSz = fromIntegral $ elfSegmentMemSize seg - data' = bytes segData <> L.replicate (memSz - fileSz) 0 - in - (is, addData (elfSegmentPhysAddr seg) data' ds) - - bytes str = pack <$> BS.unpack str - - addData (fromIntegral -> startAddr) dat mem = - I.fromList (L.zip [startAddr ..] dat) <> mem diff --git a/bittide/src/Bittide/ProcessingElement/Util.hs b/bittide/src/Bittide/ProcessingElement/Util.hs deleted file mode 100644 index 41269edf4..000000000 --- a/bittide/src/Bittide/ProcessingElement/Util.hs +++ /dev/null @@ -1,163 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} - -module Bittide.ProcessingElement.Util where - -import Clash.Prelude hiding (Exp) - -import Bittide.ProcessingElement.DeviceTreeCompiler -import Bittide.ProcessingElement.ReadElf -import Bittide.SharedTypes - -import Control.Monad (when) -import Data.Maybe -import GHC.Stack -import Language.Haskell.TH -import Numeric (showHex) -import System.Exit -import System.IO (hPutStrLn, stderr) - -import qualified Data.ByteString as BS -import qualified Data.IntMap as I -import qualified Data.List as L - -{- | Given a `Maybe Int` and a list, if the `Maybe Int` is `Just s`, pad the list to -size `s` with the given element. If the length of the list is greater than `s`, -throw an error. If the `Maybe Int` is `Nothing`, return the list as is. --} -padToSize :: (HasCallStack) => String -> Maybe Int -> a -> [a] -> [a] -padToSize _ Nothing _ l = l -padToSize name (Just s) a xs - | s >= l = xs <> L.replicate (s - l) a - | otherwise = - error - $ "Bittide.ProcessingElement.Util: " - <> name - <> " with length " - <> show l - <> " is longer than the specified size " - <> show s - where - l = L.length xs - -{- | Given the path to an elf file, the path to a device tree and a starting address - for the device tree. Return a 3 tuple containing: - (initial program counter, instruction memory blob, data memory blob) --} -memBlobsFromElf :: - (HasCallStack) => - -- | How the words should be ordered in the memBlob - ByteOrder -> - -- | Optional size in bytes to which we should pad the instruction memBlob and data memBlob. - -- Rounds up to the nearest word size. - (Maybe Int, Maybe Int) -> - -- | Source file, assumed to be Little Endian. - FilePath -> - -- | Optional tuple of starting address and filepath to a device tree. - Maybe (I.Key, FilePath) -> - -- | (instruction memBlob, data memBlob) - Q Exp -memBlobsFromElf byteOrder (iSize, dSize) elfPath maybeDeviceTree = do - (iMemIntMap, dMemIntMap) <- runIO (getBytesMems elfPath maybeDeviceTree) - let - (_iStartAddr, _, iList) = extractIntMapData byteOrder iMemIntMap - (_dStartAddr, _, dList) = extractIntMapData byteOrder dMemIntMap - iListPadded = padToSize "Instruction memory" (fmap ((`div` 4) . (+ 3)) iSize) 0 iList - dListPadded = padToSize "Data memory" (fmap ((`div` 4) . (+ 3)) dSize) 0 dList - iBlob = memBlobTH Nothing iListPadded - dBlob = memBlobTH Nothing dListPadded - - [|($iBlob, $dBlob)|] - -{- | Given the path to an elf file, the path to a device tree and a starting address - for the device tree. Return a 3 tuple containing: -Return a 3 tuple containing (initial program counter, instruction memory blob, data memory blob) --} -getBytesMems :: FilePath -> Maybe (I.Key, FilePath) -> IO (I.IntMap Byte, I.IntMap Byte) -getBytesMems elfPath maybeDeviceTree = do - elfBytes <- BS.readFile elfPath - let (entry, iMem, dMem0) = readElfFromMemory elfBytes - - when (entry /= 0x8000_0000) $ do - hPutStrLn stderr - $ "Entry point of ELF file at " - <> show elfPath - <> " must be 0x80000000. Found 0x" - <> showHex entry "" - <> " instead" - exitFailure - - -- add device tree as a memory mapped component - deviceTree <- maybe (pure []) (readDeviceTree . snd) maybeDeviceTree - let - fdtAddr = maybe 0 fst maybeDeviceTree - deviceTreeMap = I.fromList (L.zip [fdtAddr ..] deviceTree) - dMem1 = - I.unionWithKey - ( \k _ _ -> - error - $ "Bittide.ProcessingElement.Util: Overlapping element in data memory and device tree at address 0x" - <> showHex k "" - ) - dMem0 - deviceTreeMap - - pure (iMem, if isJust maybeDeviceTree then dMem1 else dMem0) - --- | Given an IntMap, return the starting address, size and content as @[Bytes 4]@ -extractIntMapData :: - ByteOrder -> - -- | IntMap - I.IntMap (BitVector 8) -> - -- | - -- 1. Starting address - -- 2. Size - -- 3. List of words - (BitVector 32, Int, [Bytes 4]) -extractIntMapData byteOrder dataMap = (resize . bitCoerce $ startAddr, size, combineFunction content) - where - combineFunction - | LittleEndian <- byteOrder = toWordsLinear - | BigEndian <- byteOrder = toWordsSwapped - - ordList = I.toAscList dataMap - startAddr = fst $ L.head ordList - size = I.size dataMap - content = - snd (L.head ordList) : flattenContent startAddr (L.tail ordList) - - flattenContent _ [] = [] - flattenContent prevAddr ((nextAddr, val) : vals) = - let - n = nextAddr - prevAddr - 1 - padding = L.replicate n 0 - in - padding L.++ (val : flattenContent nextAddr vals) - - toWordsLinear :: [Bytes 1] -> [Bytes 4] - toWordsLinear [] = [] - toWordsLinear [!a] = [bitCoerce (a, 0 :: Bytes 3)] - toWordsLinear [!a, !b] = [bitCoerce (a, b, 0 :: Bytes 2)] - toWordsLinear [!a, !b, !c] = [bitCoerce (a, b, c, 0 :: Bytes 1)] - toWordsLinear ((!a) : (!b) : (!c) : (!d) : rest) = bitCoerce (a, b, c, d) : toWordsLinear rest - - toWordsSwapped :: [Bytes 1] -> [Bytes 4] - toWordsSwapped [] = [] - toWordsSwapped [!a] = [bitCoerce (a, 0 :: Bytes 3)] - toWordsSwapped [!a, !b] = [bitCoerce (b, a, 0 :: Bytes 2)] - toWordsSwapped [!a, !b, !c] = [bitCoerce (c, b, a, 0 :: Bytes 1)] - toWordsSwapped ((!a) : (!b) : (!c) : (!d) : rest) = bitCoerce (d, c, b, a) : toWordsSwapped rest - --- | Given the filepath to a device tree, return the divce tree as list of `Byte`. -readDeviceTree :: FilePath -> IO [Byte] -readDeviceTree deviceTreePath = do - compileRes <- compileDeviceTreeSource deviceTreePath - - deviceTreeRaw <- maybe exitFailure pure compileRes - - let - padding = L.replicate (4 - (BS.length deviceTreeRaw `mod` 4)) 0 - - pure (fmap pack . BS.unpack $ deviceTreeRaw <> BS.pack padding) diff --git a/bittide/src/Bittide/ScatterGather.hs b/bittide/src/Bittide/ScatterGather.hs deleted file mode 100644 index 9b785221d..000000000 --- a/bittide/src/Bittide/ScatterGather.hs +++ /dev/null @@ -1,275 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} - -module Bittide.ScatterGather ( - scatterUnitWb, - ScatterConfig (..), - gatherUnitWb, - GatherConfig (..), -) where - -import Clash.Prelude - -import Protocols.Wishbone - -import Bittide.Calendar -import Bittide.DoubleBufferedRam -import Bittide.Extra.Maybe -import Bittide.SharedTypes - -{- | Existential type to explicitly differentiate between a configuration for -the 'scatterUnitWb' and 'gatherUnitWb' at type level and hide the memory depth from -higher level APIs. --} -data ScatterConfig nBytes addrW where - ScatterConfig :: - (KnownNat memDepth, 1 <= memDepth) => - (CalendarConfig nBytes addrW (Index memDepth)) -> - ScatterConfig nBytes addrW - -{- | Existential type to explicitly differentiate between a configuration for -the 'scatterUnitWb' and 'gatherUnitWb' at type level and hide the memory depth from -higher level APIs. --} -data GatherConfig nBytes addrW where - GatherConfig :: - (KnownNat memDepth, 1 <= memDepth) => - (CalendarConfig nBytes addrW (Index memDepth)) -> - GatherConfig nBytes addrW - -{- | Double buffered memory component that can be written to by a Bittide link. The write -address of the incoming frame is determined by the incorporated 'calendar'. The buffers -are swapped at the beginning of each metacycle. Reading the buffer is done by supplying -a read address. Furthermore this component offers ports to control the incorporated 'calendar'. --} -scatterUnit :: - ( HiddenClockResetEnable dom - , KnownNat memDepth - , 1 <= memDepth - , KnownNat frameWidth - , KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - ) => - -- | Configuration for the 'calendar'. - CalendarConfig nBytes addrW (Index memDepth) -> - -- | Wishbone (master -> slave) port for the 'calendar'. - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | Incoming frame from Bittide link. - Signal dom (DataLink frameWidth) -> - -- | Read address. - Signal dom (Index memDepth) -> - -- | 1. Data at read address delayed 1 cycle - -- 2. Wishbone (slave -> master) from 'calendar') - -- 3. End of metacycle. - ( Signal dom (BitVector frameWidth) - , Signal dom (WishboneS2M (Bytes nBytes)) - , Signal dom Bool - ) -scatterUnit calConfig wbIn linkIn readAddr = (readOut, wbOut, endOfMetacycle) - where - (writeAddr, endOfMetacycle, wbOut) = mkCalendar calConfig wbIn - writeOp = (\a b -> (a,) <$> b) <$> writeAddr <*> linkIn - readOut = doubleBufferedRamU bufSelect0 readAddr writeOp - bufSelect0 = register A bufSelect1 - bufSelect1 = mux endOfMetacycle (swapAorB <$> bufSelect0) bufSelect0 - -{- | Double buffered memory component that can be written to by a generic write operation. The -write address of the incoming frame is determined by the incorporated 'calendar'. The -buffers are swapped at the beginning of each metacycle. Reading the buffer is done by -supplying a read address. Furthermore this component offers ports to control the -incorporated 'calendar'. --} -gatherUnit :: - ( HiddenClockResetEnable dom - , KnownNat memDepth - , 1 <= memDepth - , KnownNat frameWidth - , 1 <= frameWidth - , KnownNat (DivRU frameWidth 8) - , 1 <= (DivRU frameWidth 8) - , KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - ) => - -- | Configuration for the 'calendar'. - CalendarConfig nBytes addrW (Index memDepth) -> - -- | Wishbone (master -> slave) port for the 'calendar'. - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | Write operation writing a frame. - Signal dom (Maybe (LocatedBits memDepth frameWidth)) -> - -- | Byte enable for write operation. - Signal dom (ByteEnable (BitVector frameWidth)) -> - -- | 1. Frame to Bittide Link. - -- 2. Wishbone (slave -> master) from 'calendar') - -- 3. End of metacycle. - ( Signal dom (DataLink frameWidth) - , Signal dom (WishboneS2M (Bytes nBytes)) - , Signal dom Bool - ) -gatherUnit calConfig wbIn writeOp byteEnables = (linkOut, wbOut, endOfMetacycle) - where - (readAddr, endOfMetacycle, wbOut) = mkCalendar calConfig wbIn - linkOut = mux (register True ((== 0) <$> readAddr)) (pure Nothing) (Just <$> bramOut) - bramOut = doubleBufferedRamByteAddressableU bufSelect0 readAddr writeOp byteEnables - bufSelect0 = register A bufSelect1 - bufSelect1 = mux endOfMetacycle (swapAorB <$> bufSelect0) bufSelect0 - -{- | Wishbone interface for the 'scatterUnit' and 'gatherUnit'. It makes the scatter and gather -unit, which operate on 64 bit frames, addressable via a 32 bit wishbone bus. --} -wbInterface :: - forall nBytes addrW addresses. - ( KnownNat nBytes - , KnownNat addresses - , 1 <= addresses - , KnownNat addrW - ) => - -- | Wishbone (master -> slave) data. - WishboneM2S addrW nBytes (Bytes nBytes) -> - -- | Read data to be send to over the (slave -> master) port. - Bytes nBytes -> - -- | (slave - master data, read address memory element, write data memory element) - (WishboneS2M (Bytes nBytes), Index addresses, Maybe (Bytes nBytes)) -wbInterface WishboneM2S{..} readData = - ( (emptyWishboneS2M @(Bytes nBytes)){readData, acknowledge, err} - , wbAddr - , writeOp - ) - where - masterActive = strobe && busCycle - maxAddress = resize $ pack (maxBound :: Index addresses) - err = masterActive && (addr > maxAddress) - acknowledge = masterActive && not err - wbAddr = unpack . resize $ pack addr - writeOp = orNothing (strobe && writeEnable && not err) writeData - -{- | Adds a stalling address to the 'wbInterface' by demanding an extra address on type level. -When this address is accessed, the outgoing 'WishboneS2M' bus' acknowledge is replaced -with the @endOfMetacycle@ signal to stall the wishbone master until the end of the metacycle. --} -addStalling :: - (KnownNat memAddresses, 1 <= memAddresses) => - -- | Controls the 'acknowledge' of the returned 'WishboneS2M' when the incoming address - -- is 'maxBound'. - Bool -> - -- | - -- 1. Incoming 'WishboneS2M' bus. - -- 2. Incoming wishbone address (stalling address in range). - -- 3. Incoming write operation. - ( WishboneS2M wbData - , Index (memAddresses + 1) - , Maybe a - ) -> - -- | - -- 1. Outgoing 'WishboneS2M' bus (@acknowledge@ replaced with @endOfMetacycle@ when @wbAddr == maxBound@). - -- 2. Outgoing wishbone address (stalling address not in range). - -- 3. Outgoing write operation (set to @Nothing@ when @wbAddr == maxBound@). - ( WishboneS2M wbData - , Index memAddresses - , Maybe a - ) -addStalling endOfMetacycle (incomingBus@WishboneS2M{..}, wbAddr, writeOp0) = - (slaveToMaster1, memAddr, writeOp1) - where - stalledBus = incomingBus{acknowledge = endOfMetacycle} - (slaveToMaster1, writeOp1) - | acknowledge && (wbAddr == maxBound) = (stalledBus, Nothing) - | otherwise = (incomingBus, writeOp0) - memAddr = bitCoerce $ resize wbAddr - -{-# NOINLINE scatterUnitWb #-} - -{- | Wishbone addressable 'scatterUnit', the wishbone port can read the data from this -memory element as if it has a 32 bit port by selecting the upper 32 or lower 32 bits -of the read data. --} -scatterUnitWb :: - forall dom addrWidthSu nBytesCal addrWidthCal. - ( HiddenClockResetEnable dom - , KnownNat addrWidthSu - , KnownNat nBytesCal - , 1 <= nBytesCal - , KnownNat addrWidthCal - ) => - -- | Configuration for the 'calendar'. - ScatterConfig nBytesCal addrWidthCal -> - -- | Wishbone (master -> slave) port 'calendar'. - Signal dom (WishboneM2S addrWidthCal nBytesCal (Bytes nBytesCal)) -> - -- | Incoming frame from Bittide link. - Signal dom (DataLink 64) -> - -- | Wishbone (master -> slave) port scatter memory. - Signal dom (WishboneM2S addrWidthSu 4 (Bytes 4)) -> - -- | - -- 1. Wishbone (slave -> master) port scatter memory - -- 2. Wishbone (slave -> master) port 'calendar' - (Signal dom (WishboneS2M (Bytes 4)), Signal dom (WishboneS2M (Bytes nBytesCal))) -scatterUnitWb (ScatterConfig calConfig) wbInCal linkIn wbInSu = - (delayControls wbOutSu, wbOutCal) - where - (wbOutSu, memAddr, _) = - unbundle - $ addStalling - <$> endOfMetacycle - <*> (wbInterface <$> wbInSu <*> scatteredData) - (readAddr, upperSelected) = unbundle $ div2Index <$> memAddr - (scatterUnitRead, wbOutCal, endOfMetacycle) = - scatterUnit calConfig wbInCal linkIn readAddr - (lower, upper) = unbundle $ split <$> scatterUnitRead - selected = register (errorX "scatterUnitWb: Initial selection undefined") upperSelected - scatteredData = mux selected upper lower - -{-# NOINLINE gatherUnitWb #-} - -{- | Wishbone addressable 'gatherUnit', the wishbone port can write data to this -memory element as if it has a 32 bit port by controlling the byte enables of the -'gatherUnit' based on the third bit. --} -gatherUnitWb :: - forall dom addrWidthGu nBytesCal addrWidthCal. - ( HiddenClockResetEnable dom - , KnownNat addrWidthGu - , KnownNat nBytesCal - , 1 <= nBytesCal - , KnownNat addrWidthCal - ) => - -- | Configuration for the 'calendar'. - GatherConfig nBytesCal addrWidthCal -> - -- | Wishbone (master -> slave) data 'calendar'. - Signal dom (WishboneM2S addrWidthCal nBytesCal (Bytes nBytesCal)) -> - -- | Wishbone (master -> slave) port gather memory. - Signal dom (WishboneM2S addrWidthGu 4 (Bytes 4)) -> - -- | - -- 1. Wishbone (slave -> master) port gather memory - -- 2. Wishbone (slave -> master) port 'calendar' - ( Signal dom (DataLink 64) - , Signal dom (WishboneS2M (Bytes 4)) - , Signal dom (WishboneS2M (Bytes nBytesCal)) - ) -gatherUnitWb (GatherConfig calConfig) wbInCal wbInGu = - (linkOut, delayControls wbOutGu, wbOutCal) - where - (wbOutGu, memAddr, writeOp) = - unbundle - $ addStalling - <$> endOfMetacycle - <*> (wbInterface <$> wbInGu <*> pure 0b0) - (writeAddr, upperSelected) = unbundle $ div2Index <$> memAddr - (linkOut, wbOutCal, endOfMetacycle) = - gatherUnit calConfig wbInCal gatherWrite gatherByteEnables - gatherWrite = mkWrite <$> writeAddr <*> writeOp - gatherByteEnables = mkEnables <$> upperSelected <*> (busSelect <$> wbInGu) - - -- We update the 64 bit entry of the 'gatherUnit' in chunks of 32 bits. Thus we repeat - -- the writeData of the wishbone bus twice in the write operation to the 'gatherUnit' and - -- use the byte enables to either update the upper or lower - mkWrite address (Just write) = Just (address, write ++# write) - mkWrite _ _ = Nothing - mkEnables selected byteEnables - | selected = 0 ++# byteEnables - | otherwise = byteEnables ++# 0 diff --git a/bittide/src/Bittide/SharedTypes.hs b/bittide/src/Bittide/SharedTypes.hs deleted file mode 100644 index 50f631a67..000000000 --- a/bittide/src/Bittide/SharedTypes.hs +++ /dev/null @@ -1,202 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=8 #-} - -module Bittide.SharedTypes where - -import Clash.Prelude - -import Data.Constraint -import Data.Constraint.Nat.Extra -import Data.Type.Equality ((:~:) (Refl)) -import Protocols.Wishbone - --- | To be used when there are two options. -data AorB = A | B deriving (Eq, Generic, BitPack, Show, NFDataX) - --- | If we receive 'A', return 'B'. If we receive 'B', return 'A' -swapAorB :: AorB -> AorB -swapAorB A = B -swapAorB B = A - --- | Polymorphic record update of 'addr'. -updateM2SAddr :: - BitVector addressWidthNew -> - WishboneM2S addressWidthOld selWidth dat -> - WishboneM2S addressWidthNew selWidth dat -updateM2SAddr newAddr WishboneM2S{..} = WishboneM2S{addr = newAddr, ..} - --- | Resize 'WishboneM2S's 'addr' field. -resizeM2SAddr :: - (KnownNat addressWidthOld, KnownNat addressWidthNew) => - WishboneM2S addressWidthOld selWidth dat -> - WishboneM2S addressWidthNew selWidth dat -resizeM2SAddr WishboneM2S{..} = WishboneM2S{addr = resize addr, ..} - --- | A single byte. -type Byte = BitVector 8 - --- | BitVector of _n_ bytes. -type Bytes n = BitVector (n * 8) - --- | A BitVector that contains one bit per byte in the BitSize of a. -type ByteEnable a = BitVector (Regs a 8) - --- | Either contains a @Just (BitVector frameWidth)@ or @Nothing@. -type DataLink frameWidth = Maybe (BitVector frameWidth) - --- | Type synonym that constrains @a@ and @b@ to both be @KnownNat@ and that @a <= b@. -type LessThan a b = (KnownNat a, KnownNat b, a <= b) - --- Synonym that returns the number of bits required to represent natural number n. -type NatRequiredBits n = CLog 2 (n + 1) - --- Constraint that requires the number of bits required to represent natural number n --- to be lesser than or equal to bits. -type NatFitsInBits n bits = NatRequiredBits n <= bits - --- | Constraints required to add padding to @a@. -type Paddable a = (BitPack a, NFDataX a) - --- Located i x is a datatype that indicates that data x has a relation with Index i, --- example usage: write operation of type D to a blockRam with 'i' addresses can --- be described as: Located i D - --- | @writeData@ has a relation with @Index maxIndex@. -type Located maxIndex writeData = (Index maxIndex, writeData) - --- | @BitVector bits@ has a relation with @Index maxIndex@. -type LocatedBits maxIndex bits = Located maxIndex (BitVector bits) - --- | 'Byte' has a relation with @Index maxIndex@. -type LocatedByte maxIndex = Located maxIndex Byte - --- | 'Bytes' has a relation with @Index maxIndex@. -type LocatedBytes maxIndex nBytes = Located maxIndex (Bytes nBytes) - --- Padding bits added when a is stored in multiples of bw bits. -type Pad a bw = (Regs a bw * bw) - BitSize a - --- Amount of bw sized registers required to store a. -type Regs a bw = DivRU (BitSize a) bw - -data ByteOrder = LittleEndian | BigEndian - deriving (Eq) - --- | Stores any arbitrary datatype as a vector of registers. -newtype RegisterBank regSize content (byteOrder :: ByteOrder) - = RegisterBank (Vec (Regs content regSize) (BitVector regSize)) - deriving (Generic) - -instance - (KnownNat regSize, 1 <= regSize, BitPack content) => - BitPack (RegisterBank regSize content byteOrder) - where - type - BitSize (RegisterBank regSize content byteOrder) = - Regs content regSize * regSize - pack (RegisterBank vec) = pack vec - unpack bv = RegisterBank (unpack bv) - -deriving newtype instance - ( KnownNat regSize - , 1 <= regSize - , Paddable content - , NFDataX (RegisterBank regSize content byteOrder) - ) => - NFDataX (RegisterBank regSize content byteOrder) - -deriving newtype instance - (KnownNat regSize, ShowX (RegisterBank regSize content byteOrder)) => - ShowX (RegisterBank regSize content byteOrder) - -convertBe :: - (Paddable a, KnownNat bw, 1 <= bw) => - (RegisterBank bw a 'BigEndian, a) -> - (a, RegisterBank bw a 'BigEndian) -convertBe (regBank, a) = (getDataBe regBank, getRegsBe a) - --- | Transforms a to _RegisterBank_. -getRegsLe :: - forall bw a. - (Paddable a, KnownNat bw, 1 <= bw) => - a -> - RegisterBank bw a 'LittleEndian -getRegsLe a = case timesDivRU @bw @(BitSize a) of - Dict -> RegisterBank (reverse $ bitCoerce (0 :: BitVector (Pad a bw), a)) - --- | Transforms a to _RegisterBank_. -getRegsBe :: - forall bw a. (Paddable a, KnownNat bw, 1 <= bw) => a -> RegisterBank bw a 'BigEndian -getRegsBe a = case timesDivRU @bw @(BitSize a) of - Dict -> RegisterBank (bitCoerce (0 :: BitVector (Pad a bw), a)) - --- | Transforms _RegisterBank_ to a. -getDataBe :: - forall bw a. (Paddable a, KnownNat bw, 1 <= bw) => RegisterBank bw a 'BigEndian -> a -getDataBe (RegisterBank vec) = - case timesDivRU @bw @(BitSize a) of - Dict -> unpack . snd $ split @_ @(Pad a bw) @(BitSize a) (pack vec) - --- | Transforms _RegisterBank_ to a. -getDataLe :: - forall bw a. (Paddable a, KnownNat bw, 1 <= bw) => RegisterBank bw a 'LittleEndian -> a -getDataLe (RegisterBank (reverse -> vec)) = - case timesDivRU @bw @(BitSize a) of - Dict -> unpack . snd $ split @_ @(Pad a bw) @(BitSize a) (pack vec) - -{- | Coerces a tuple of index n and a boolean to index (n*2) where the LSB of the result -is determined by the boolean. --} -mul2Index :: - forall n b. - (KnownNat n, 1 <= n, BitPack b, BitSize b ~ 1) => - Index n -> - b -> - Index (n * 2) -mul2Index n b = case clogProductRule @n of Refl -> bitCoerce (n, b) - --- | Coerces an index of size (n*2) to index n with the LSB as separate boolean. -div2Index :: - forall n b. - (KnownNat n, 1 <= n, BitPack b, BitSize b ~ 1) => - Index (n * 2) -> - (Index n, b) -div2Index = case clogProductRule @n of Refl -> bitCoerce - --- | Delays the output controls to align them with the actual read / write timing. -delayControls :: - (HiddenClockResetEnable dom) => - Signal dom (WishboneS2M bytes) -> - Signal dom (WishboneS2M bytes) -delayControls wbIn = wbOut - where - delayedAck = register False (acknowledge <$> wbIn) - delayedErr = register False (err <$> wbIn) - wbOut = - (\wb newAck newErr -> wb{acknowledge = newAck, err = newErr}) - <$> wbIn - <*> delayedAck - <*> delayedErr - --- | Takes an implicit reset and a Signal dom Bool that can force a reset when True. -forceReset :: - (HiddenReset dom) => - -- | Forces a reset when True. - Signal dom Bool -> - -- | Active when the implicit reset is active or the first argument is True. - Reset dom -forceReset force = unsafeFromActiveHigh (unsafeToActiveHigh hasReset .||. force) - --- | Divide and round up. -divRU :: (Integral a) => a -> a -> a -divRU b a = (b + a - 1) `div` a diff --git a/bittide/src/Bittide/Switch.hs b/bittide/src/Bittide/Switch.hs deleted file mode 100644 index e6dda2dea..000000000 --- a/bittide/src/Bittide/Switch.hs +++ /dev/null @@ -1,103 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GADTs #-} -{-# LANGUAGE RecordWildCards #-} - -module Bittide.Switch where - -import Clash.Prelude - -import Bittide.Calendar -import Bittide.SharedTypes -import Data.Constraint.Nat.Extra -import Protocols -import Protocols.Wishbone - --- | An index which source is selected by the crossbar, 0 selects Nothing, k selects k - 1. -type CrossbarIndex links = Index (links + 1) - -{- | Stores for each link, an index where the incoming frame is written to in the scatter -memory and a crossbar index to select the outgoing frame. --} -type CalendarEntry links = Vec links (CrossbarIndex links) - -switchC :: - forall dom nBytes addrW links frameWidth. - ( HiddenClockResetEnable dom - , KnownNat links - , KnownNat frameWidth - , 1 <= frameWidth - , KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - ) => - CalendarConfig nBytes addrW (CalendarEntry links) -> - Circuit - ( CSignal dom (Vec links (DataLink frameWidth)) - , Wishbone dom 'Standard addrW (Bytes nBytes) - ) - (CSignal dom (Vec links (DataLink frameWidth))) -switchC conf = case (cancelMulDiv @nBytes @8) of - Dict -> Circuit go - where - go ((unbundle -> streamsIn, calM2S), _) = ((pure (), calS2M), bundle streamsOut) - where - (streamsOut, calS2M) = switch conf calM2S streamsIn - -{-# NOINLINE switch #-} - --- TODO: The switch is currently hardcoded to be bidirectional, we intend to change this when --- the need arises. - -{- | The Bittide Switch routes data from incoming links to outgoing links based on a 'Calendar'. -The switch consists of a 'crossbar', a 'calendar' and receiver and transmitter logic per link. -The receive logic consists of a 'rxUnit' and a receive register (single depth -'Bittide.ScatterGather.scatterUnit'). The transmit logic consists of a transmit register -(single depth 'Bittide.ScatterGather.gatherUnit') and a 'txUnit'. The 'crossbar' selects -one of the receive register's output for each transmit register. Index @0@ selects a -null frame @Nothing@ and @k@ selects receive register @(k - 1)@. --} -switch :: - forall dom nBytes addrW links frameWidth. - ( HiddenClockResetEnable dom - , KnownNat addrW - , KnownNat frameWidth - , 1 <= frameWidth - , KnownNat links - , KnownNat nBytes - , 1 <= nBytes - ) => - CalendarConfig nBytes addrW (CalendarEntry links) -> - -- | Wishbone interface wired to the calendar. - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | All incoming datalinks - Vec links (Signal dom (DataLink frameWidth)) -> - -- | All outgoing datalinks - ( Vec links (Signal dom (DataLink frameWidth)) - , Signal dom (WishboneS2M (Bytes nBytes)) - ) -switch calConfig calM2S streamsIn = (streamsOut, calS2M) - where - (cal, _, calS2M) = mkCalendar @dom @nBytes @addrW calConfig calM2S - scatterFrames = register Nothing <$> streamsIn - gatherFrames = unbundle $ crossBar <$> cal <*> bundle scatterFrames - streamsOut = register Nothing <$> gatherFrames - -{-# NOINLINE crossBar #-} - -{- | The 'crossbar' receives a vector of indices and a vector of incoming frames. -For each outgoing link it will select a data source. @0@ selects a null frame @Nothing@, -therefore indexing of incoming links starts at 1 (index 1 selects incoming frame 0). -Source: bittide hardware, switch logic. --} -crossBar :: - (KnownNat links) => - Vec links (CrossbarIndex links) -> - -- | Source selection for each outgoing link, 0 is a null frame, links start at index 1. - Vec links (Maybe a) -> - -- | Vector of incoming links. - Vec links (Maybe a) -crossBar calendarEntry inputStreams = fmap selectChannel calendarEntry - where - selectChannel i = (Nothing :> inputStreams) !! i diff --git a/bittide/src/Bittide/Transceiver.hs b/bittide/src/Bittide/Transceiver.hs deleted file mode 100644 index afb817628..000000000 --- a/bittide/src/Bittide/Transceiver.hs +++ /dev/null @@ -1,627 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE NoFieldSelectors #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} - -{- | Transceiver module for the Bittide project. This module is a wrapper around -the 'Clash.Cores.Xilinx.GTH.gthCore' function, adding additional functionality -such as PRBS generation and checking, comma insertion, word alignment, and -user data handshaking. - -__CAUTION__: When instantiating multiple transceivers you might want to use -'transceiverPrbsN'. Make sure to read its documentation before proceeding. - -= Internals -This section will cover the internals of the transceiver module. Feel free to -skip reading this if you just want to use the transceiver. - -__Commas__ -We've configured the Xilinx transceiver IP to use 8b/10b encoding. In order -for the decoding to work properly, the transceivers need to byte-align. This -is done by detecting comma symbols in the incoming data stream. We start out -by sending them for a number of cycles (see @Comma.@'Comma.defCycles'). - -__Word alignment__ -After sending commas, we assume the receiver receives our words in a byte-aligned -fashion. We can use this fact by reserving the MSB of each byte for an alignment -symbol - see "Bittide.Transceiver.WordAlign". - -__Meta data__ -We send along meta data with each word. This meta data is used to signal to the -neighbor that we're ready to receive user data, or that the next word will be -user data. The meta data also contains the FPGA and transceiver index, which -can be used for debugging. - -__Reset manager__ -A reset manager is used as a sort of \"watchdog\" while booting the -transceivers. It will reset the receive side of the transceiver if it doesn't -receive sensible (PRBS) data for a certain amount of time. After resetting the -receive side for a number of times, it will reset the transmit side as well if -the received data is still considered gibberish. Note that this means that if -you receive *good* data for @'Bittide.Transceiver.ResetManager.rxRetries' * -'Bittide.Transceiver.ResetManager.rxTimeoutMs'@ milliseconds, you can be sure -the neighbor won't reset its transceiver anymore. - -__Word format__ -An (aligned) 64 bit word is formatted as follows: - -> +----------+----------+----------+----------+----------+----------+----------+----------+ -> | 1mpppppp | 0mpppppp | 0mpppppp | 0mpppppp | 0mpppppp | 0mpppppp | 0mpppppp | 0mpppppp | -> +----------+----------+----------+----------+----------+----------+----------+----------+ - - * 1/0: alignment symbol - * m: meta data - * p: PRBS data - -__Protocol__ -The protocol is as follows: - -Transmit: - - 1. Send commas for a number of cycles - 2. Send PRBS data with meta data - 3. Wait for receiver to signal it has successfully decoded PRBS data for a long time - 4. Send meta data with 'ready' set to 'True' - 5. Wait for 'Input.txReady' - 6. Send meta data with 'lastPrbsWord' set to 'True' - 7. Send user data - -Note that the reset manager might decide to reset in steps (2) and (3). - -Receive: - - 1. Detect alignment symbol and shift data accordingly - 2. Check PRBS data - 3. Signal that PRBS data is OK after observing it for some time (see section - __Reset manager__). - 4. Wait for 'Meta.lastPrbsWord' - 5. Freeze alignment logic --} -module Bittide.Transceiver where - -import Clash.Explicit.Prelude - -import Bittide.Arithmetic.Time (trueForSteps) -import Bittide.ElasticBuffer (sticky) -import Clash.Cores.Xilinx.GTH (GthCore) -import Clash.Cores.Xilinx.Ila ( - Depth (D1024), - IlaConfig (advancedTriggers, depth, stages), - ila, - ilaConfig, - ) -import Clash.Cores.Xilinx.Xpm.Cdc.ArraySingle (xpmCdcArraySingle) -import Clash.Cores.Xilinx.Xpm.Cdc.Single (xpmCdcSingle) -import Clash.Explicit.Reset.Extra (Asserted (Asserted), delayReset, xpmResetSynchronizer) -import Clash.Prelude (withClock) -import Clash.Sized.Vector.Extra (zipWith8) -import Control.Monad (when) -import Data.Maybe (fromMaybe, isNothing) -import Data.Proxy (Proxy (Proxy)) - -import qualified Bittide.Transceiver.Cdc as Cdc -import qualified Bittide.Transceiver.Comma as Comma -import qualified Bittide.Transceiver.Prbs as Prbs -import qualified Bittide.Transceiver.ResetManager as ResetManager -import qualified Bittide.Transceiver.WordAlign as WordAlign -import qualified Clash.Cores.Xilinx.GTH as Gth - -{- | Meta information send along with the PRBS and alignment symbols. See module -documentation for more information. --} -data Meta = Meta - { ready :: Bool - -- ^ Ready to receive user data - , lastPrbsWord :: Bool - -- ^ Next word will be user data - , fpgaIndex :: Unsigned 3 - -- ^ FPGA index to use (debug only, logic does not rely on this) - , transceiverIndex :: Unsigned 3 - -- ^ Transceiver index to use (debug only, logic does not rely on this) - } - deriving (Generic, NFDataX, BitPack) - -{- | Insert zeroes such that each of the following are encoded in 4 bits, making -them easier to read when formatted as a hex value: - - * prbsOk, lastPrbsWord - * fpgaIndex - * transceiverIndex - -This is useful for when we don't control formatting (such as when looking at -ILA traces). --} -prettifyMetaBits :: BitVector 8 -> BitVector 12 -prettifyMetaBits bv = - pack - $ let meta = unpack @Meta bv - in ( low - , low - , meta.ready - , meta.lastPrbsWord - , low - , meta.fpgaIndex - , low - , meta.transceiverIndex - ) - -data Config dom = Config - { debugIla :: Bool - -- ^ Instantiate a debug ILAs - , debugFpgaIndex :: Signal dom (Unsigned 3) - -- ^ FPGA index to use for debug signals - , resetManagerConfig :: ResetManager.Config - -- ^ Configuration for 'ResetManager.resetManager' - } - -defConfig :: Config dom -defConfig = - Config - { debugIla = False - , debugFpgaIndex = pure 0 - , resetManagerConfig = ResetManager.defConfig - } - -{- | Careful: the domains for each transceiver are different, even if their -types say otherwise. --} -data Outputs n tx rx txS free = Outputs - { txClocks :: Vec n (Clock tx) - -- ^ See 'Output.txClock' - , txResets :: Vec n (Reset tx) - -- ^ See 'Output.txReset' - , txReadys :: Vec n (Signal tx Bool) - -- ^ See 'Output.txReady' - , txSamplings :: Vec n (Signal tx Bool) - -- ^ See 'Output.txSampling' - , txPs :: Signal txS (BitVector n) - -- ^ See 'Output.txP' - , txNs :: Signal txS (BitVector n) - -- ^ See 'Output.txN' - , rxClocks :: Vec n (Clock rx) - -- ^ See 'Output.rxClock' - , rxResets :: Vec n (Reset rx) - -- ^ See 'Output.rxReset' - , rxDatas :: Vec n (Signal rx (Maybe (BitVector 64))) - -- ^ See 'Output.rxData' - , linkUps :: Vec n (Signal free Bool) - -- ^ See 'Output.linkUp' - , linkReadys :: Vec n (Signal free Bool) - -- ^ See 'Output.linkReady' - , stats :: Vec n (Signal free ResetManager.Statistics) - -- ^ See 'Output.stats' - } - -data Output tx rx txS free serializedData = Output - { txClock :: Clock tx - -- ^ Transmit clock. See 'txReset'. - , txReset :: Reset tx - -- ^ Reset signal for the transmit side. Clock can be unstable until this reset - -- is deasserted. - , txReady :: Signal tx Bool - -- ^ Ready to signal to neigbor that next word will be user data. Waiting for - -- 'Input.txReady' to be asserted before starting to send 'txData'. - , txSampling :: Signal tx Bool - -- ^ Data is sampled from 'Input.txSampling' - , txP :: Signal txS serializedData - -- ^ Transmit data (and implicitly a clock), positive - , txN :: Signal txS serializedData - -- ^ Transmit data (and implicitly a clock), negative - , rxClock :: Clock rx - -- ^ Receive clock, recovered from the incoming data stream. See 'rxReset'. - , rxReset :: Reset rx - -- ^ Reset signal for the receive side. Clock can be unstable until this reset - -- is deasserted. - , rxData :: Signal rx (Maybe (BitVector 64)) - -- ^ User data received from the neighbor - , linkUp :: Signal free Bool - -- ^ True if both the transmit and receive side are either handling user data - , linkReady :: Signal free Bool - -- ^ True if both the transmit and receive side ready to handle user data or - -- doing so. I.e., 'linkUp' implies 'linkReady'. Note that this - , stats :: Signal free ResetManager.Statistics - -- ^ Statistics exported by 'ResetManager.resetManager'. Useful for debugging. - } - -data Input tx rx ref free rxS serializedData = Input - { clock :: Clock free - -- ^ Any "always on" clock - , reset :: Reset free - -- ^ Reset signal for the entire transceiver - , refClock :: Clock ref - -- ^ Reference clock. Used to synthesize transmit clock. - , transceiverIndex :: Unsigned 3 - -- ^ Index of this transceiver, used for debugging. Can be set to 0 if not used. - , channelName :: String - -- ^ Channel name, example \"X0Y18\" - , clockPath :: String - -- ^ Clock path, example \"clk0-2\" - , rxN :: Signal rxS serializedData - , rxP :: Signal rxS serializedData - , txData :: Signal tx (BitVector 64) - -- ^ Data to transmit to the neighbor. Is sampled on sample after - -- 'Output.txSamplingOnNext' is asserted. Is sampled when - -- 'Output.txData' is asserted. - , txReady :: Signal tx Bool - -- ^ When asserted, signal to neighbor that next word will be user data. This - -- signal is ignored until 'Output.txReady' is asserted. Can be tied - -- to 'True'. - , rxReady :: Signal rx Bool - -- ^ When asserted, allow signalling to the neighbor that we are ready to - -- receive user data. Once asserted, it should stay asserted. Note that the - -- neighbor might decide to not send user data for a long time, even if this - -- is asserted. - } - -data Inputs tx rx ref free rxS n = Inputs - { clock :: Clock free - -- ^ See 'Input.clock' - , reset :: Reset free - -- ^ See 'Input.reset' - , refClock :: Clock ref - -- ^ See 'Input.refClock' - , channelNames :: Vec n String - -- ^ See 'Input.channel' - , clockPaths :: Vec n String - -- ^ See 'Input.clockPath' - , rxNs :: Signal rxS (BitVector n) - -- ^ See 'Input.rxN' - , rxPs :: Signal rxS (BitVector n) - -- ^ See 'Input.rxP' - , txDatas :: Vec n (Signal tx (BitVector 64)) - -- ^ See 'Input.txData' - , txReadys :: Vec n (Signal tx Bool) - -- ^ See 'Input.txReady' - , rxReadys :: Vec n (Signal rx Bool) - -- ^ See 'Input.rxReady' - } - -transceiverPrbsN :: - forall tx rx ref free txS rxS n. - ( KnownNat n - , HasSynchronousReset tx - , HasDefinedInitialValues tx - , HasSynchronousReset rx - , HasDefinedInitialValues rx - , HasSynchronousReset free - , HasDefinedInitialValues free - , KnownDomain rxS - , KnownDomain txS - , KnownDomain ref - , KnownDomain free - ) => - Config free -> - Inputs tx rx ref free rxS n -> - Outputs n tx rx txS free -transceiverPrbsN opts inputs@Inputs{clock, reset, refClock} = - Outputs - { -- tx - txClocks = map (.txClock) outputs - , txResets = map (.txReset) outputs - , txReadys = map (.txReady) outputs - , txSamplings = map (.txSampling) outputs - , -- rx - rxClocks = map (.rxClock) outputs - , rxResets = map (.rxReset) outputs - , rxDatas = map (.rxData) outputs - , -- transceiver - txPs = pack <$> bundle (map (.txP) outputs) - , txNs = pack <$> bundle (map (.txN) outputs) - , -- free - linkUps = map (.linkUp) outputs - , linkReadys = map (.linkReady) outputs - , stats = map (.stats) outputs - } - where - -- XXX: Replacing 'zipWithN' with '<$>' and '<*>' triggers a combination of: - -- - -- * https://github.com/clash-lang/clash-compiler/issues/2723 - -- * https://github.com/clash-lang/clash-compiler/issues/2722 - -- - -- Note that these bugs break the instantiation of multiple ILAs. - outputs = - zipWith8 - go - (iterateI (+ 1) 0) -- Note that the target type is only 3 bits, so this will - -- wrap around after 8 transceivers. This is fine, as we - -- only use this for debugging. - inputs.channelNames - inputs.clockPaths - (unbundle (unpack <$> inputs.rxNs)) - (unbundle (unpack <$> inputs.rxPs)) - inputs.txDatas - inputs.txReadys - inputs.rxReadys - - go transceiverIndex channelName clockPath rxN rxP txData txReady rxReady = - transceiverPrbs - opts - Input - { channelName - , clockPath - , rxN - , rxP - , txData - , txReady - , rxReady - , transceiverIndex - , clock - , reset - , refClock - } - -transceiverPrbs :: - forall tx rx ref free txS rxS. - ( HasSynchronousReset tx - , HasDefinedInitialValues tx - , HasSynchronousReset rx - , HasDefinedInitialValues rx - , HasSynchronousReset free - , HasDefinedInitialValues free - , KnownDomain rxS - , KnownDomain txS - , KnownDomain ref - , KnownDomain free - ) => - Config free -> - Input tx rx ref free rxS (BitVector 1) -> - Output tx rx txS free (BitVector 1) -transceiverPrbs = transceiverPrbsWith Gth.gthCore - -transceiverPrbsWith :: - forall tx rx ref free txS rxS serializedData. - ( HasSynchronousReset tx - , HasDefinedInitialValues tx - , HasSynchronousReset rx - , HasDefinedInitialValues rx - , HasSynchronousReset free - , HasDefinedInitialValues free - , KnownDomain rxS - , KnownDomain txS - , KnownDomain ref - , KnownDomain free - ) => - GthCore tx rx ref free txS rxS serializedData -> - Config free -> - Input tx rx ref free rxS serializedData -> - Output tx rx txS free serializedData -transceiverPrbsWith gthCore opts args@Input{clock, reset} = - when opts.debugIla debugIla `hwSeqX` result - where - debugIla :: Signal free () - debugIla = - ila - ( ( ilaConfig - $ "ila_probe_fpgaIndex" - :> "ila_probe_transIndex" - :> "ila_probe_txRetries" - :> "ila_probe_rxRetries" - :> "ila_probe_rxFullRetries" - :> "ila_probe_failAfterUps" - :> "ila_probe_rx_data0" - :> "ila_probe_alignedRxData0" - :> "ila_probe_gtwiz_userdata_tx_in" - :> "ila_probe_reset_rx_done" - :> "ila_probe_reset_tx_done" - :> "ila_probe_reset" - :> "ila_probe_alignError" - :> "ila_probe_prbsErrors" - :> "ila_probe_alignedAlignBits" - :> "ila_probe_alignedMetaBits" - :> "ila_probe_rxCtrl0" - :> "ila_probe_rxCtrl1" - :> "ila_probe_rxCtrl2" - :> "ila_probe_rxCtrl3" - :> "ila_probe_prbsOk" - :> "ila_probe_prbsOkDelayed" - :> "ila_probe_rst_all" - :> "ila_probe_rst_rx" - :> "ila_probe_rxReset" - :> "ila_probe_txReset" - :> "ila_probe_metaTx" - :> "ila_probe_linkUp" - :> "ila_probe_txLastFree" - :> "capture" - :> "trigger" - :> Nil - ) - { advancedTriggers = True - , stages = 1 - , depth = D1024 - } - ) - clock - opts.debugFpgaIndex - (pure args.transceiverIndex :: Signal free (Unsigned 3)) - ((.txRetries) <$> stats) - ((.rxRetries) <$> stats) - ((.rxFullRetries) <$> stats) - ((.failAfterUps) <$> stats) - (xpmCdcArraySingle rxClock clock rx_data0) - (xpmCdcArraySingle rxClock clock alignedRxData0) - (xpmCdcArraySingle txClock clock gtwiz_userdata_tx_in) - (xpmCdcArraySingle rxClock clock reset_rx_done) - (xpmCdcArraySingle txClock clock reset_tx_done) - (unsafeToActiveHigh reset) - (xpmCdcArraySingle rxClock clock alignError) - (xpmCdcArraySingle rxClock clock prbsErrors) - (xpmCdcArraySingle rxClock clock alignedAlignBits) - (xpmCdcArraySingle rxClock clock (prettifyMetaBits <$> alignedMetaBits)) - (xpmCdcArraySingle rxClock clock rxCtrl0) - (xpmCdcArraySingle rxClock clock rxCtrl1) - (xpmCdcArraySingle rxClock clock rxCtrl2) - (xpmCdcArraySingle rxClock clock rxCtrl3) - (xpmCdcSingle rxClock clock prbsOk) - (xpmCdcSingle rxClock clock prbsOkDelayed) - (unsafeToActiveHigh rst_all) - (unsafeToActiveHigh rst_rx) - (xpmCdcSingle rxClock clock $ unsafeToActiveHigh rxReset) - (xpmCdcSingle txClock clock $ unsafeToActiveHigh txReset) - (xpmCdcArraySingle txClock clock (prettifyMetaBits . pack <$> metaTx)) - linkUp - txLastFree - (pure True :: Signal free Bool) -- capture - txLastFree -- trigger - result = - Output - { txSampling = txUserData - , rxData = mux rxUserData (Just <$> alignedRxData0) (pure Nothing) - , txReady - , txN - , txP - , txClock - , txReset - , rxClock - , rxReset - , linkUp - , linkReady - , stats - } - - linkUp = - withLockTxFree txUserData - .&&. withLockRxFree rxUserData - - linkReady = linkUp .||. withLockRxFree rxReadySticky - - ( txN - , txP - , txClock - , rxClock - , rx_data0 - , reset_tx_done - , reset_rx_done - , tx_active - , rxCtrl0 - , rxCtrl1 - , rxCtrl2 - , rxCtrl3 - ) = - gthCore - args.channelName - args.clockPath - args.rxN - args.rxP - clock -- gtwiz_reset_clk_freerun_in - (delayReset Asserted clock rst_all) - -- \* filter glitches * - (delayReset Asserted clock rst_rx) - -- \* filter glitches * - -- gtwiz_reset_rx_datapath_in - gtwiz_userdata_tx_in - txctrl - args.refClock -- gtrefclk0_in - prbsConfig = Prbs.conf31 @48 - - (commas, txctrl) = Comma.generator d1 txClock txReset - commasDone = isNothing <$> commas - prbs = Prbs.generator txClock (unsafeFromActiveLow commasDone) enableGen prbsConfig - prbsWithMeta = WordAlign.joinMsbs @8 <$> fmap pack metaTx <*> prbs - prbsWithMetaAndAlign = WordAlign.joinMsbs @8 WordAlign.alignSymbol <$> prbsWithMeta - gtwiz_userdata_tx_in = - mux - txUserData - args.txData - (fromMaybe <$> prbsWithMetaAndAlign <*> commas) - - rxReset = - xpmResetSynchronizer Asserted rxClock rxClock - $ unsafeFromActiveLow (bitCoerce <$> reset_rx_done) - `orReset` xpmResetSynchronizer Asserted clock rxClock reset - - alignedRxData0 :: Signal rx (BitVector 64) - alignedRxData0 = - withClock rxClock - $ WordAlign.alignBytesFromMsbs @8 WordAlign.alignLsbFirst (rxUserData .||. rxLast) rx_data0 - - (alignedAlignBits, alignedRxData1) = - unbundle - $ WordAlign.splitMsbs @8 @8 - <$> alignedRxData0 - - (alignedMetaBits, alignedRxData2) = - unbundle - $ WordAlign.splitMsbs @8 @7 - <$> alignedRxData1 - - prbsErrors = Prbs.checker rxClock rxReset enableGen prbsConfig alignedRxData2 - anyPrbsErrors = prbsErrors ./=. pure 0 - alignError = alignedAlignBits ./=. pure WordAlign.alignSymbol - - -- We consider the control symbols as errors, as they should not be present in - -- the user data stream. 8b/10b encoding errors naturally count as errors too. - -- Note that the upper bits of rxCtrl0 and rxCtrl1 are unused. - -- - -- TODO: Truncate rxCtrl0 and rxCtrl1 in GTH primitive. - rxCtrlOrError = - fmap (truncateB @_ @8) rxCtrl0 - ./=. pure 0 - .||. fmap (truncateB @_ @8) rxCtrl1 - ./=. pure 0 - .||. rxCtrl2 - ./=. pure 0 - .||. rxCtrl3 - ./=. pure 0 - - prbsOk = - rxUserData - .||. Prbs.tracker rxClock rxReset (anyPrbsErrors .||. alignError .||. rxCtrlOrError) - - -- 'prbsWaitMs' is the number of milliseconds representing the worst case time - -- it takes for the PRBS to stabilize. I.e., after this time we can be sure the - -- neighbor doesn't reset its transceiver anymore. We add a single retry to - -- account for clock speed variations. - prbsWaitMs = - ((1 :: Index 2) `add` opts.resetManagerConfig.rxRetries) - `mul` opts.resetManagerConfig.rxTimeoutMs - prbsOkDelayed = trueForSteps (Proxy @(Milliseconds 1)) prbsWaitMs rxClock rxReset prbsOk - validMeta = mux rxUserData (pure False) prbsOkDelayed - - rxMeta = mux validMeta (Just . unpack @Meta <$> alignedMetaBits) (pure Nothing) - rxLast = maybe False (.lastPrbsWord) <$> rxMeta - rxReady = maybe False (.ready) <$> rxMeta - - rxUserData = sticky rxClock rxReset rxLast - txUserData = sticky txClock txReset txLast - - txReady = txLast .||. withLockRxTx (prbsOkDelayed .&&. sticky rxClock rxReset args.rxReady) - - rxReadySticky = sticky rxClock rxReset rxReady - txLast = args.txReady .&&. withLockRxTx rxReadySticky - txLastFree = xpmCdcSingle txClock clock txLast - - metaTx :: Signal tx Meta - metaTx = - Meta - <$> txReady - <*> txLast - -- We shouldn't sync with 'xpmCdcArraySingle' here, as the individual bits in - -- 'fpgaIndex' are related to each other. Still, we know fpgaIndex is basically - -- a constant so :shrug:. - <*> xpmCdcArraySingle clock txClock opts.debugFpgaIndex - <*> pure args.transceiverIndex - - (rst_all, rst_rx, stats) = - ResetManager.resetManager - opts.resetManagerConfig - clock - reset - (withLockTxFree (pure True)) - (withLockRxFree (pure True)) - (withLockRxFree (prbsOk .||. rxUserData)) - - txReset = - xpmResetSynchronizer Asserted txClock txClock - $ unsafeFromActiveLow (bitCoerce <$> tx_active) - `orReset` unsafeFromActiveLow (bitCoerce <$> reset_tx_done) - `orReset` xpmResetSynchronizer Asserted clock txClock reset - - withLockTxFree = Cdc.withLock txClock (unpack <$> reset_tx_done) clock reset - withLockRxFree = Cdc.withLock rxClock (unpack <$> reset_rx_done) clock reset - withLockRxTx = Cdc.withLock rxClock (unpack <$> reset_rx_done) txClock txReset diff --git a/bittide/src/Bittide/Transceiver/Cdc.hs b/bittide/src/Bittide/Transceiver/Cdc.hs deleted file mode 100644 index 2d7a91c32..000000000 --- a/bittide/src/Bittide/Transceiver/Cdc.hs +++ /dev/null @@ -1,64 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -{- | Utilities to safely synchronize signals between different clock domains, -where either the source or the destination clock is inactive. --} -module Bittide.Transceiver.Cdc (withLock) where - -import Clash.Explicit.Prelude - -import Clash.Cores.Xilinx.Xpm.Cdc.Single (XpmCdcSingleConfig (..), xpmCdcSingleWith) - -{- | Synchronize an (asynchronous) lock signal between two clock domains. This is -different from using 'xpmCdcSingle' directly, as it ensures that the registers -in the destination domain are properly reset. --} -withLock :: - ( KnownDomain src - , KnownDomain dst - ) => - Clock src -> - Signal src Bool -> - Clock dst -> - Reset dst -> - Signal src Bool -> - Signal dst Bool -withLock = withLockN (SNat @4) - --- | Worker function for 'cdcLock'. -withLockN :: - forall src dst nRegs. - ( KnownDomain src - , KnownDomain dst - , 2 <= nRegs - , nRegs <= 10 - ) => - SNat nRegs -> - Clock src -> - Signal src Bool -> - Clock dst -> - Reset dst -> - Signal src Bool -> - Signal dst Bool -withLockN nRegs@SNat srcClk asyncSrcRst dstClk dstRst s = - -- Ignore the first /nRegs/ cycles of 'lock' - the pipeline might still contain - -- old data. Ideally 'xpmCdcSingle' would have a reset port so we could avoid - -- this. - mux - (counter .==. pure (maxBound @(Index (nRegs + 1)))) - lockSynced - (pure False) - where - counter = register dstClk dstRst enableGen 0 (satSucc SatBound <$> counter) - - cdcConfig = - XpmCdcSingleConfig - { stages = nRegs - , initialValues = True -- default - , registerInput = False - } - - sGlitchFree = delay srcClk enableGen False s - lockSynced = xpmCdcSingleWith cdcConfig srcClk dstClk (asyncSrcRst .&&. sGlitchFree) diff --git a/bittide/src/Bittide/Transceiver/Comma.hs b/bittide/src/Bittide/Transceiver/Comma.hs deleted file mode 100644 index 77ff97918..000000000 --- a/bittide/src/Bittide/Transceiver/Comma.hs +++ /dev/null @@ -1,49 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -module Bittide.Transceiver.Comma where - -import Clash.Explicit.Prelude - -import Bittide.Arithmetic.Time (IndexMs) -import Bittide.SharedTypes (Byte, Bytes) -import Clash.Class.Counter (Counter (countSuccOverflow)) - --- | Generate commas (transceiver alignment symbols) for a number of milliseconds -generator :: - forall ms nBytes dom. - ( KnownDomain dom - , KnownNat nBytes - , 1 <= ms - ) => - SNat ms -> - Clock dom -> - Reset dom -> - -- | (comma, tx control) - ( Signal dom (Maybe (Bytes nBytes)) - , Signal dom (BitVector nBytes) - ) -generator _nCycles@SNat clk rst = - unbundle - $ mux - (counter .==. pure maxBound) - (pure (Nothing, 0)) - (pure (Just commas, maxBound)) - where - comma :: Byte - comma = 0xbc - - commas :: Bytes nBytes - commas = pack (repeat comma) - - counter = - register - clk - rst - enableGen - (0 :: Index ms, 0 :: IndexMs dom 1) - (next <$> counter) - - next n = case countSuccOverflow n of - (True, _) -> n - (_, nextN) -> nextN diff --git a/bittide/src/Bittide/Transceiver/Prbs.hs b/bittide/src/Bittide/Transceiver/Prbs.hs deleted file mode 100644 index f09bbe565..000000000 --- a/bittide/src/Bittide/Transceiver/Prbs.hs +++ /dev/null @@ -1,149 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GADTs #-} - -{- | A pseudo-random bit sequence (PRBS) generator and checker. These functions -are used to test the signal integrity of transceivers. The generator generates -a PRBS stream, while the checker checks whether the received stream is the -same as the generated stream. Note that the checker is "self synchronizing", -meaning that it will synchronize with the generator after /polyLength/ cycles. --} -module Bittide.Transceiver.Prbs where - -import Clash.Explicit.Prelude - -{- | Configuration for a PRBS generator or checker. Note that this can only -specify PRBS streams with two taps: the first tap is always the MSB -(@polyLength@), and the second tap is the @polyTap@-th bit. - -See https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence for more information. --} -data Config polyLength polyTap nBits where - Config :: - ( KnownNat polyLength - , KnownNat polyTap - , KnownNat nBits - , 1 <= nBits - , 1 <= polyTap - , (polyTap + 1) <= polyLength - , -- Same constraints, but written differently for type checking purposes: - (_n0 + 1) ~ nBits - , (polyTap + _n1) ~ polyLength - , polyTap ~ (_n2 + 1) - , _n1 ~ (_n3 + 1) - ) => - Config polyLength polyTap nBits - --- | PRBS31: @x^31 + x^28 + 1@ -conf31 :: forall n. (KnownNat n, 1 <= n) => Config 31 28 n -conf31 = leToPlus @1 @n Config - --- | PRBS generator, see module documentation. -generator :: - forall dom polyLength polyTap nBits. - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Enable dom -> - Config polyLength polyTap nBits -> - Signal dom (BitVector nBits) -generator clk rst ena Config = - moore clk rst ena go snd (maxBound, maxBound) (pure ()) - where - go :: - (BitVector polyLength, BitVector nBits) -> - () -> - (BitVector polyLength, BitVector nBits) - go (prbsReg, _) _ = - ( last prbs - , pack (reverse $ map msb prbs) - ) - where - prbs :: Vec nBits (BitVector polyLength) - prbs = unfoldrI goPrbs prbsReg - - goPrbs :: BitVector polyLength -> (BitVector polyLength, BitVector polyLength) - goPrbs bv = (o, o) - where - o = newBit +>>. bv - tap = SNat @(polyLength - polyTap) - newBit = bitStep bv tap - --- | PRBS checker, see module documentation. -checker :: - forall dom polyLength polyTap nBits. - (KnownDomain dom) => - Clock dom -> - Reset dom -> - Enable dom -> - Config polyLength polyTap nBits -> - Signal dom (BitVector nBits) -> - Signal dom (BitVector nBits) -checker clk rst ena Config = mealy clk rst ena go (maxBound, maxBound) - where - go :: - (BitVector polyLength, BitVector nBits) -> - BitVector nBits -> - ((BitVector polyLength, BitVector nBits), BitVector nBits) - go (prbsReg, prbsOutPrev) prbsIn = - ( (prbsState, pack $ reverse prbsOut) - , prbsOutPrev - ) - where - prbsOut :: Vec nBits Bit - prbsState :: BitVector polyLength - (prbsState, prbsOut) = mapAccumL goPrbs prbsReg (reverse $ unpack prbsIn) - - goPrbs :: BitVector polyLength -> Bit -> (BitVector polyLength, Bit) - goPrbs bv newBit = (o, bitErr) - where - o = newBit +>>. bv - tap = SNat @(polyLength - polyTap) - bitErr = xor newBit (bitStep bv tap) - -bitStep :: - ( BitSize a ~ ((1 + n) + i) - , BitPack a - , KnownNat n - , KnownNat i - ) => - a -> - SNat n -> - Bit -bitStep bv tap = - -- XXX: Replacing 'slice' by 'testBit' makes tests fail? - xor (lsb bv) (unpack $ slice tap tap bv) - -data TrackerState - = -- | Link is considered down. Needs 127 cycles of \"good\" input to transition - -- to 'Up'. - Down (Index 127) - | -- | Link has not seen errors in at least 127 cycles. - Up - deriving (Eq, Show, Generic, NFDataX) - -{- | Small state machine tracking whether a link is stable. A link is considered -stable, if no errors were detected for a number of cycles (see "PrbsTrackerState"). -Whenever a bit error is detected, it immediately deasserts its output. --} -tracker :: - (KnownDomain dom) => - Clock dom -> - Reset dom -> - -- | PRBS error detected - Signal dom Bool -> - -- | Link OK - Signal dom Bool -tracker clk rst = - mealy clk rst enableGen go initSt - where - initSt = Down maxBound - - go :: TrackerState -> Bool -> (TrackerState, Bool) - go _ True = (initSt, False) - go st False = - case st of - Down 0 -> (Up, False) - Down n -> (Down (n - 1), False) - Up -> (Up, True) diff --git a/bittide/src/Bittide/Transceiver/ResetManager.hs b/bittide/src/Bittide/Transceiver/ResetManager.hs deleted file mode 100644 index a4eb934ce..000000000 --- a/bittide/src/Bittide/Transceiver/ResetManager.hs +++ /dev/null @@ -1,183 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE NoFieldSelectors #-} -{-# OPTIONS_GHC -Wno-ambiguous-fields #-} - --- | Functions to reset the transceiver subsystems during bringup -module Bittide.Transceiver.ResetManager where - -import Clash.Explicit.Prelude - -import Bittide.Arithmetic.Time (IndexMs) -import Clash.Class.Counter (countSucc) - --- | See 'Config.txTimeoutMs' -type MaxTxTimeoutMs = Index 128 - --- | See 'Config.rxTimeoutMs' -type MaxRxTimeoutMs = Index 128 - --- | See 'Config.rxRetries' -type MaxRxRetries = Index 128 - --- | Statistics exported by 'resetManager' -data Statistics = Statistics - { txRetries :: Unsigned 32 - -- ^ How many times the transmit side was reset - , rxRetries :: MaxRxRetries - -- ^ How many times the receive side was reset. Note that this value in itself - -- will reset if the transmit side resets - see 'rxFullRetries'. - , rxFullRetries :: Unsigned 32 - -- ^ How many times 'rxRetries' overflowed. I.e., how many times 'RxWait' moved - -- the state machine back to 'StartTx'. - , failAfterUps :: Unsigned 32 - -- ^ How many times the link failed when in the 'Monitor' state - i.e., after - -- detecting it fully worked. This usually happens if the other side drops its - -- link because it tried resetting its receive side too many times - see - -- 'rxFullRetries'. - } - deriving (Generic, NFDataX) - -{- | Configuration for 'resetManager' - -Develop notes: the current API is a balance between the tightest possible -hardware and the most flexible API. We could make the API more flexible by -exposing 'MaxTxTimeoutMs' and friends as type parameters, but that severely -impacts verbosity and readability. --} -data Config = Config - { txTimeoutMs :: MaxTxTimeoutMs - -- ^ Number of milliseconds to wait for the transmit side to be ready, before - -- resetting the transmit subsystem. - , rxTimeoutMs :: MaxRxTimeoutMs - -- ^ Number of milliseconds to wait for the receive side to be ready, before - -- resetting the receiver subsystem. - , rxRetries :: MaxRxRetries - -- ^ Number of times to retry the receive side before resetting the transmit - -- side as well. - } - deriving (Generic, NFDataX, Show) - -{- | Default configuration for 'resetManager' - -XXX: Current timeout values for 'TxWait' and 'RxWait' are chosen arbitrarily. - We should investigate what these values should be for quick bring-up. --} -defConfig :: Config -defConfig = - Config - { txTimeoutMs = 1 - , rxTimeoutMs = 5 - , rxRetries = 8 - } - -{- | Bringing up the transceivers is a stochastic process - at least, that is -what Xilinx reference designs make us believe. We therefore retry a number of -times if we don't see sensible data coming in. See the individual constructors -and 'resetManager' for more information. --} -data State dom - = -- | Reset everything - transmit and receive side - StartTx - | -- | Reset just the receive side - StartRx - | -- | Wait for the transmit side to report it is done. After /n/ milliseconds - -- (see type) it times out, moving to 'StartTx'. - TxWait (MaxTxTimeoutMs, IndexMs dom 1) - | -- | Wait for the receive side to report it is done _and_ that it can predict - -- the data coming from the other side. After /n/ milliseconds (see type) it - -- times out. Depending on the value of 'ResetStat's 'rxRetries' it will - -- either reset both the receive and the transmit side, or just the receive - -- side. If all is well though, move on to 'Monitor'. - RxWait (MaxRxTimeoutMs, IndexMs dom 1) - | -- | Wait till the end of the universe, or until a link goes down - whichever - -- comes first. In case of the latter, the state machine moves to 'StartTx'. - Monitor - deriving (Generic, NFDataX, Eq) - -{- | Reset manager for transceivers: see 'State' for more information on -this state machine. See 'Statistics' for information on what debug values -are exported. --} -resetManager :: - forall dom. - (KnownDomain dom) => - Config -> - Clock dom -> - Reset dom -> - "tx_init_done" ::: Signal dom Bool -> - "rx_init_done" ::: Signal dom Bool -> - "rx_data_good" ::: Signal dom Bool -> - ( "reset_all_out" ::: Reset dom - , "reset_rx" ::: Reset dom - , "stats" ::: Signal dom Statistics - ) -resetManager config clk rst tx_init_done rx_init_done rx_data_good = - ( unsafeFromActiveHigh reset_all_out_sig - , unsafeFromActiveHigh reset_rx_sig - , statistics - ) - where - (reset_all_out_sig, reset_rx_sig, statistics) = - mooreB - clk - rst - enableGen - update - extractOutput - (initState, initStats) - ( tx_init_done - , rx_init_done - , rx_data_good - ) - - initStats :: Statistics - initStats = - Statistics - { txRetries = 0 - , rxRetries = 0 - , rxFullRetries = 0 - , failAfterUps = 0 - } - - initState :: State dom - initState = StartTx - - update :: (State dom, Statistics) -> (Bool, Bool, Bool) -> (State dom, Statistics) - update st (tx_done, rx_done, rx_good) = - case st of - -- Reset everything: - (StartTx, stats) -> (TxWait minBound, stats) - -- Wait for transceiver to indicate it is done - (TxWait cntr@(ms, _), stats@Statistics{txRetries}) - | tx_done -> (RxWait minBound, stats) - | ms == config.txTimeoutMs -> (StartTx, stats{txRetries = satSucc SatBound txRetries}) - | otherwise -> (TxWait (countSucc cntr), stats) - -- Reset receive side logic - (StartRx, stats) -> (RxWait minBound, stats) - -- Wait for a reliable incoming link. This can fail in multiple ways, see - -- 'RxWait'. - (RxWait cntr@(ms, _), stats@Statistics{rxRetries, rxFullRetries}) - | rx_done && rx_good -> - (Monitor, stats) - | ms == config.rxTimeoutMs && rxRetries >= config.rxRetries -> - (StartTx, stats{rxFullRetries = satSucc SatBound rxFullRetries}) - | ms == config.rxTimeoutMs -> - (StartRx, stats{rxRetries = satSucc SatBound rxRetries}) - | otherwise -> - (RxWait (countSucc cntr), stats) - -- Monitor link. Move all the way back to 'StartTx' if the link goes down - -- for some reason. - (Monitor, stats@Statistics{failAfterUps}) - | rx_done && rx_good -> (Monitor, stats) - | otherwise -> (StartTx, stats{failAfterUps = satSucc SatBound failAfterUps}) - - extractOutput (st, stats) = - ( st == StartTx - , st == StartRx - , stats - ) diff --git a/bittide/src/Bittide/Transceiver/WordAlign.hs b/bittide/src/Bittide/Transceiver/WordAlign.hs deleted file mode 100644 index 0fb8c9a75..000000000 --- a/bittide/src/Bittide/Transceiver/WordAlign.hs +++ /dev/null @@ -1,196 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NamedFieldPuns #-} - --- or by any other shift (or none at all!). This module provides utilities to end --- up with a word aligned stream. The basic idea is that, while \"booting\" the --- connection the MSB of each byte is reserved, where an asserted MSB indicates --- the start of a word. The other bits can be used to detect a valid connection --- through PRBS streams. --- --- TODO: Remove this module in favor of retry logic. That is, we can keep --- resetting the transceivers until they're aligned. Its unclear how exactly --- this should work though. - -{- | Fundamentally, our transceivers are sending over single bits. Through transceiver -IP we can send words (e.g., 32 bits) on one end and receive words on the other -end. The IP makes sure that any received are byte aligned. That means that a -stream: - -> --------------------------------------------------- -> ... | A3 A2 A1 A0 | B3 B2 B1 B0 | C3 C2 C1 C0 | ... -> --------------------------------------------------- - -..might be, assuming LSB first transmission, received on the other end as: - -> ----------------------------------------------------------------- -> ... | A1 A0 .. .. | A3 A2 B1 B0 | C1 C0 B3 B2 | .. .. C3 C2 | ... -> ----------------------------------------------------------------- --} -module Bittide.Transceiver.WordAlign ( - alignBytesFromMsbs, - - -- * Convenience functions - alignSymbol, - splitMsbs, - joinMsbs, - - -- * Core functions and utilities - AlignmentFn, - aligner, - alignLsbFirst, - alignMsbFirst, - - -- * Dealigning (for testing purposes) - dealignLsbFirst, - dealignMsbFirst, -) where - -import Clash.Prelude - -import Bittide.SharedTypes (Bytes) -import Clash.Explicit.Prelude (noReset) -import Data.Bifunctor (Bifunctor (bimap)) -import Data.Maybe (fromMaybe) -import Data.Tuple.Extra (curry3) - -{- | Split the MSBs of a 'BitVector's \"bytes\" into a 'BitVector' of MSBs and a -'BitVector' of the remaining bits. --} -splitMsbs :: - forall nBytes byteWidth. - ( KnownNat nBytes - , KnownNat byteWidth - , 1 <= byteWidth - ) => - BitVector (nBytes * byteWidth) -> - ( BitVector nBytes - , BitVector (nBytes * (byteWidth - 1)) - ) -splitMsbs = - bimap pack pack - . unzip - . unpack @(Vec nBytes (Bit, BitVector (byteWidth - 1))) - --- | Opposite of 'splitMsbs'. -joinMsbs :: - forall nBytes byteWidth. - ( KnownNat nBytes - , KnownNat byteWidth - , 1 <= byteWidth - ) => - BitVector nBytes -> - BitVector (nBytes * (byteWidth - 1)) -> - BitVector (nBytes * byteWidth) -joinMsbs msbs bvs = - pack - $ zip - (unpack @(Vec nBytes Bool) msbs) - (unpack @(Vec nBytes (BitVector (byteWidth - 1))) bvs) - -alignSymbol :: forall n. (KnownNat n, 1 <= n) => BitVector n -alignSymbol = 1 +>>. 0 - -{- | Specialized version of 'aligner' that assumes an 'alignSymbol' is stored in -the MSB of each byte. --} -alignBytesFromMsbs :: - forall n dom. - ( HiddenClock dom - , KnownNat n - , 1 <= n - ) => - -- | Alignment function (typically 'alignLsbFirst' or 'alignMsbFirst') - AlignmentFn n -> - -- | Freeze alignment symbol updates (i.e., keep the current alignment) - Signal dom Bool -> - -- | Data with alignment bits in the byte's MSBs - Signal dom (Bytes n) -> - -- | Aligned data, according to alignment data saved in the last cycle - Signal dom (Bytes n) -alignBytesFromMsbs alignFn freeze dat = - aligner alignFn freeze (oneHotDecoder <$> msbs) dat - where - msbs = unpack . fst . splitMsbs @n <$> dat - oneHotDecoder = fromMaybe 0 . elemIndex True - -data State n = State {prev :: Bytes n, offset :: Index n} - deriving (Show, Generic, ShowX, NFDataX) - --- | Alignment circuit that is generic in its alignment function -aligner :: - forall n dom. - ( HiddenClock dom - , KnownNat n - , 1 <= n - ) => - -- | Alignment function (e.g., 'alignLsbFirst'). Can also be used to dealign. - AlignmentFn n -> - -- | Freeze offset signal (i.e., keep the current offset). Note: it takes one - -- cycle to flush the state of this circuit, so if you don't want to see garbage - -- data, you should make sure the offset signal has been stable for at least - -- two cycles before freezing it. - Signal dom Bool -> - -- | Offset - Signal dom (Index n) -> - -- | Data - Signal dom (Bytes n) -> - -- | Aligned data, according to alignment data saved in the last cycle - Signal dom (Bytes n) -aligner alignFn = - withEnable enableGen - $ withReset noReset - $ curry3 (mealy go State{prev = 0, offset = 0} . bundle) - where - go :: State n -> (Bool, Index n, Bytes n) -> (State n, Bytes n) - go (State{prev, offset}) (freeze, suggestedOffset, current) = - ( State{prev = current, offset = newOffset} - , alignFn offset prev current - ) - where - newOffset = if freeze then offset else suggestedOffset - --- | (De-)alignment function that can be used in 'aligner' -type AlignmentFn n = - (KnownNat n) => - -- | Offset - Index n -> - -- | \"Old\" data - Bytes n -> - -- | \"New\" data - Bytes n -> - -- | (De-)aligned data - Bytes n - --- | De-align bytes for to emulate LSB-first transmission -dealignLsbFirst :: AlignmentFn n -dealignLsbFirst offset old new = takeLsbs $ shiftBytesR (new ++# old) offset - --- | Align bytes for LSB-first transmission -alignLsbFirst :: AlignmentFn n -alignLsbFirst offset old new = takeMsbs $ shiftBytesL (new ++# old) offset - --- | De-align bytes for to emulate MSB-first transmission -dealignMsbFirst :: AlignmentFn n -dealignMsbFirst offset old new = takeLsbs $ shiftBytesR (old ++# new) offset - --- | Align bytes for MSB-first transmission -alignMsbFirst :: AlignmentFn n -alignMsbFirst offset old new = takeMsbs $ shiftBytesL (old ++# new) offset - --- | Like 'shiftR', but for 'Bytes' -shiftBytesR :: forall n. (KnownNat n) => Bytes (2 * n) -> Index n -> Bytes (2 * n) -shiftBytesR bv n = shiftR bv (8 * fromIntegral n) - --- | Like 'shiftL', but for 'Bytes' -shiftBytesL :: forall n. (KnownNat n) => Bytes (2 * n) -> Index n -> Bytes (2 * n) -shiftBytesL bv n = shiftL bv (8 * fromIntegral n) - --- | Take upper bits of given 'BitVector' -takeMsbs :: forall n. (KnownNat n) => Bytes (2 * n) -> Bytes n -takeMsbs = fst . unpack @(Bytes n, Bytes n) - --- | Take lower bits of given 'BitVector' -takeLsbs :: forall n. (KnownNat n) => Bytes (2 * n) -> Bytes n -takeLsbs = truncateB diff --git a/bittide/src/Bittide/Wishbone.hs b/bittide/src/Bittide/Wishbone.hs deleted file mode 100644 index 5c8cef516..000000000 --- a/bittide/src/Bittide/Wishbone.hs +++ /dev/null @@ -1,554 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE PartialTypeSignatures #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=100 #-} -{-# OPTIONS_GHC -fplugin Protocols.Plugin #-} - -module Bittide.Wishbone where - -import Clash.Prelude - -import Bittide.DoubleBufferedRam -import Bittide.SharedTypes - -import Clash.Cores.UART (ValidBaud, uart) -import Clash.Cores.Xilinx.Ila (Depth, IlaConfig (..), ila, ilaConfig) -import Clash.Cores.Xilinx.Unisim.DnaPortE2 -import Clash.Util.Interpolate - -import Data.Bifunctor -import Data.Bool (bool) -import Data.Constraint.Nat.Extra -import Data.Maybe - -import Protocols -import Protocols.Wishbone - -import qualified Protocols.Df as Df -import qualified Protocols.Wishbone as Wishbone - -{- $setup ->>> import Clash.Prelude --} - --- Applying this hint yields a compile error -{-# ANN module "HLint: ignore Functor law" #-} - --- | A vector of base addresses, one for each slave. -type MemoryMap nSlaves = Vec nSlaves (Unsigned (CLog 2 nSlaves)) - --- | Size of a bus that results from a `singleMasterInterconnect` with `nSlaves` slaves. -type MappedBusAddrWidth addr nSlaves = addr - CLog 2 nSlaves - -{-# NOINLINE singleMasterInterconnect #-} - -{- | Component that maps multiple slave devices to a single master device over the wishbone -bus. It routes the incoming control signals to a slave device based on the 'MemoryMap', -a vector of base addresses. --} -singleMasterInterconnect :: - forall dom nSlaves addrW a. - ( HiddenClockResetEnable dom - , KnownNat nSlaves - , 1 <= nSlaves - , KnownNat addrW - , (CLog 2 nSlaves <= addrW) - , BitPack a - , NFDataX a - ) => - MemoryMap nSlaves -> - Circuit - (Wishbone dom 'Standard addrW a) - (Vec nSlaves (Wishbone dom 'Standard (MappedBusAddrWidth addrW nSlaves) a)) -singleMasterInterconnect (fmap pack -> config) = - Circuit go - where - go (masterS, slavesS) = - fmap unbundle . unbundle $ route <$> masterS <*> bundle slavesS - - route master@(WishboneM2S{..}) slaves = (toMaster, toSlaves) - where - oneHotOrZeroSelected = fmap (== addrIndex) config - (addrIndex, newAddr) = - split @_ @_ @(MappedBusAddrWidth addrW nSlaves) addr - toSlaves = - (\newStrobe -> (updateM2SAddr newAddr master){strobe = strobe && newStrobe}) - <$> oneHotOrZeroSelected - toMaster - | busCycle && strobe = - foldMaybes - emptyWishboneS2M{err = True} -- master tries to access unmapped memory - (maskToMaybes slaves oneHotOrZeroSelected) - | otherwise = emptyWishboneS2M - -dupWb :: - forall dom aw. - (KnownDomain dom, KnownNat aw) => - Circuit - (Wishbone dom 'Standard aw (Bytes 4)) - ( Wishbone dom 'Standard aw (Bytes 4) - , ( CSignal dom (WishboneM2S aw 4 (Bytes 4)) - , CSignal dom (WishboneS2M (Bytes 4)) - ) - ) -dupWb = Circuit go - where - go (m2s0, (s2m0, _)) = - (s2m0, (m2s0, (m2s0, s2m0))) - -{- | An ILA monitoring all M2S and S2M signals on a Wishbone bus. Installs two -extra signals 'capture' and 'trigger' that can be used as defaults for triggering -the ILA and conditional capturing. Trigger will be active for every valid -transaction, while capture will be active for as long as trigger and a cycle -after it. --} -ilaWb :: - forall name dom addrW a. - (HiddenClock dom) => - -- | Name of the module of the `ila` wrapper. Naming the internal ILA is - -- unreliable when more than one ILA is used with the same arguments, but the - -- module name can be set reliably. - SSymbol name -> - -- | Number of registers to insert at each probe. Supported values: 0-6. - -- Corresponds to @C_INPUT_PIPE_STAGES@. Default is @0@. - Index 7 -> - -- | Number of samples to store. Corresponds to @C_DATA_DEPTH@. Default set - -- by 'ilaConfig' equals 'D4096'. - Depth -> - Circuit - (Wishbone dom 'Standard addrW a) - (Wishbone dom 'Standard addrW a) -ilaWb SSymbol stages0 depth0 = Circuit $ \(m2s, s2m) -> - let - -- Our HITL test infrastructure looks for 'trigger' and 'capture' and uses - -- it to trigger the ILA and do selective capture. Though defaults are - -- changable using Vivado, we set it to capture only valid Wishbone - -- transactions plus a single cycle after it. - trigger = Wishbone.strobe <$> m2s .&&. Wishbone.busCycle <$> m2s - capture = trigger .||. dflipflop trigger - - ilaInst :: Signal dom () - ilaInst = - setName @name - $ ila - ( ( ilaConfig - $ "m2s_addr" - :> "m2s_writeData" - :> "m2s_busSelect" - :> "m2s_busCycle" - :> "m2s_strobe" - :> "m2s_writeEnable" - :> "s2m_readData" - :> "s2m_acknowledge" - :> "s2m_err" - :> "s2m_stall" - :> "s2m_retry" - :> "capture" - :> "trigger" - :> Nil - ) - { advancedTriggers = True - , stages = stages0 - , depth = depth0 - } - ) - hasClock - (Wishbone.addr <$> m2s) - (Wishbone.writeData <$> m2s) - (Wishbone.busSelect <$> m2s) - (Wishbone.busCycle <$> m2s) - (Wishbone.strobe <$> m2s) - (Wishbone.writeEnable <$> m2s) - (Wishbone.readData <$> s2m) - (Wishbone.acknowledge <$> s2m) - (Wishbone.err <$> s2m) - (Wishbone.stall <$> s2m) - (Wishbone.retry <$> s2m) - capture - trigger - in - ilaInst `hwSeqX` (s2m, m2s) - -{- | Given a vector with elements and a mask, promote all values with a corresponding -'True' to 'Just', others to 'Nothing'. - -Example: - ->>> maskToMaybes ('a' :> 'b' :> Nil) (True :> False :> Nil) -Just 'a' :> Nothing :> Nil --} -maskToMaybes :: Vec n a -> Vec n Bool -> Vec n (Maybe a) -maskToMaybes = zipWith (bool Nothing . Just) - -{- | Fold 'Maybe's to a single value. If the given vector does not contain any 'Just', -the default value is picked. Prefers the leftmost value when the vector contains -multiple 'Just's. - -Example: - ->>> foldMaybes 'a' (Nothing :> Just 'c' :> Nil) -'c' ->>> foldMaybes 'a' (Just 'b' :> Just 'c' :> Nil) -'b' ->>> foldMaybes 'a' (Nothing :> Nothing :> Nil) -'a' --} -foldMaybes :: a -> Vec n (Maybe a) -> a -foldMaybes a Nil = a -foldMaybes dflt v@(Cons _ _) = fromMaybe dflt $ fold (<|>) v - -{- | Version of 'singleMasterInterconnect' that does not use the 'Circuit' abstraction -from @clash-protocols@ but exposes 'Signal's directly. --} -singleMasterInterconnect' :: - forall dom nSlaves addrW a. - ( HiddenClockResetEnable dom - , KnownNat nSlaves - , 1 <= nSlaves - , KnownNat addrW - , CLog 2 nSlaves <= addrW - , BitPack a - , NFDataX a - ) => - MemoryMap nSlaves -> - Signal dom (WishboneM2S addrW (Regs a 8) a) -> - Signal dom (Vec nSlaves (WishboneS2M a)) -> - ( Signal dom (WishboneS2M a) - , Signal dom (Vec nSlaves (WishboneM2S (MappedBusAddrWidth addrW nSlaves) (Regs a 8) a)) - ) -singleMasterInterconnect' config master slaves = (toMaster, bundle toSlaves) - where - Circuit f = singleMasterInterconnect @dom @nSlaves @addrW @a config - (toMaster, toSlaves) = - case divWithRemainder @(Regs a 8) @8 @7 of - Dict -> - f (master, unbundle slaves) - -{- | Takes an input that features no back pressure mechanism and turn it into `Df`. -This function is unsafe because data can be lost when the input is @Just _@ and -the receiving circuit tries to apply back pressure. --} -unsafeToDf :: Circuit (CSignal dom (Maybe a)) (Df dom a) -unsafeToDf = Circuit $ \(cSig, _) -> (pure (), Df.maybeToData <$> cSig) - --- | 'Df' version of 'uart'. -uartDf :: - (HiddenClockResetEnable dom, ValidBaud dom baud) => - SNat baud -> - -- | Left side of circuit: word to send, receive bit - -- Right side of circuit: received word, transmit bit - Circuit - ( Df dom (BitVector 8) - , CSignal dom Bit - ) - ( CSignal dom (Maybe (BitVector 8)) - , CSignal dom Bit - ) -uartDf baud = Circuit go - where - go ((request, rxBit), _) = - ( (Ack <$> ack, pure ()) - , (received, txBit) - ) - where - (received, txBit, ack) = uart baud rxBit (Df.dataToMaybe <$> request) - -{- | Wishbone accessible UART interface with configurable FIFO buffers. - It takes the depths of the transmit and receive buffers and the baud rate as parameters. - The function returns a 'Circuit' with a 'Wishbone' interface and a 'CSignal' for the UART - receive bit as inputs, and outputs a 'CSignal' for the UART transmit bit and a 'CSignal' - tuple indicating the status of the UART. - - The register layout is as follows: - - Address 0 (BitVector 8): UART data register (read/write) - - Address 4 (BitVector 2): UART status register (read-only) - Relevant masks: - - 0b01: Transmit buffer full - - 0b10: Receive buffer empty --} -uartWb :: - forall dom addrW nBytes baudRate transmitBufferDepth receiveBufferDepth. - ( HiddenClockResetEnable dom - , ValidBaud dom baudRate - , 1 <= transmitBufferDepth - , 1 <= receiveBufferDepth - , KnownNat addrW - , KnownNat nBytes - , 1 <= nBytes - ) => - -- | Recommended value: 16. This seems to be a good balance between resource - -- usage and usability. - SNat transmitBufferDepth -> - -- | Recommended value: 16. This seems to be a good balance between resource - -- usage and usability. - SNat receiveBufferDepth -> - -- | Valid baud rates are constrained by @clash-cores@'s 'ValidBaud' constraint. - SNat baudRate -> - Circuit - (Wishbone dom 'Standard addrW (Bytes nBytes), CSignal dom Bit) - (CSignal dom Bit, CSignal dom (Bool, Bool)) -uartWb txDepth@SNat rxDepth@SNat baud = circuit $ \(wb, uartRx) -> do - (txFifoIn, uartStatus) <- wbToDf -< (wb, rxFifoOut, txFifoMeta) - (txFifoOut, txFifoMeta) <- fifoWithMeta txDepth -< txFifoIn - (rxFifoIn, uartTx) <- uartDf baud -< (txFifoOut, uartRx) - (rxFifoOut, _rx') <- fifoWithMeta rxDepth <| unsafeToDf -< rxFifoIn - idC -< (uartTx, uartStatus) - where - wbToDf :: - Circuit - ( Wishbone dom 'Standard addrW (Bytes nBytes) - , Df dom (BitVector 8) - , CSignal dom (FifoMeta txFifoDepth) - ) - ( Df dom (BitVector 8) - , CSignal dom (Bool, Bool) -- (rxEmpty, txFull) - ) - wbToDf = - Circuit - $ bimap unbundle unbundle - . unbundle - . fmap go - . bundle - . bimap bundle bundle - where - go ((WishboneM2S{..}, Df.dataToMaybe -> rxData, fifoFull -> txFull), (Ack txAck, _)) - -- not in cycle - | not (busCycle && strobe) = - ( ((emptyWishboneS2M @()){readData = invalidReq}, Ack False, ()) - , (Df.NoData, status) - ) - -- illegal addr - | not addrLegal = - ( ((emptyWishboneS2M @()){err = True, readData = invalidReq}, Ack False, ()) - , (Df.NoData, status) - ) - -- read at 0 - | not writeEnable && internalAddr == 0 = - ( - ( (emptyWishboneS2M @()) - { acknowledge = True - , readData = resize $ fromMaybe 0 rxData - } - , Ack True - , () - ) - , (Df.NoData, status) - ) - -- write at 0 - | writeEnable && internalAddr == 0 = - ( - ( (emptyWishboneS2M @()) - { acknowledge = txAck - , readData = invalidReq - } - , Ack False - , () - ) - , (Df.Data $ resize writeData, status) - ) - -- read at 1 - | not writeEnable && internalAddr == 1 = - ( - ( (emptyWishboneS2M @()) - { acknowledge = True - , readData = resize $ pack status - } - , Ack False - , () - ) - , (Df.NoData, status) - ) - | otherwise = ((emptyWishboneS2M{err = True}, Ack False, ()), (Df.NoData, status)) - where - internalAddr = bitCoerce $ resize addr :: Index 2 - addrLegal = addr <= 1 - rxEmpty = isNothing rxData - status = (rxEmpty, txFull) - invalidReq = - deepErrorX - [i|uartWb: Invalid request. - BUS: {busCycle} - STR: {strobe} - ADDR: {addr} - WE:{writeEnable} - ACK:{acknowledge} - ERR:{err}|] - --- | State record for the FIFO circuit. -data FifoState depth = FifoState - { readPointer :: Index depth - , dataCount :: Index (depth + 1) - } - deriving (Generic, NFDataX) - --- | Meta information from 'fifoWithMeta'. -data FifoMeta depth = FifoMeta - { fifoEmpty :: Bool - , fifoFull :: Bool - , fifoDataCount :: Index (depth + 1) - } - deriving (Generic, NFDataX) - -{- | A generic First-In-First-Out (FIFO) circuit with a specified depth that exposes -meta information such as in `FifoMeta`. At least one cycle latency. -When the reset is high or the enable is low, there will be no outgoing transactions and -incoming transactions are not acknowledged. --} -fifoWithMeta :: - forall dom a depth. - (HiddenClockResetEnable dom, 1 <= depth, NFDataX a) => - -- | The depth of the FIFO, should be at least 1. - SNat depth -> - -- | Consumes @Df dom a@, produces @Df dom a@ along with ready signal and data count. - Circuit (Df dom a) (Df dom a, CSignal dom (FifoMeta depth)) -fifoWithMeta depth@SNat = Circuit circuitFunction - where - circuitFunction (fifoIn, (readyIn, _)) = (Ack <$> readyOut, (fifoOut, fifoMeta)) - where - circuitActive = unsafeToActiveLow hasReset .&&. fromEnable hasEnable - bramOut = - readNew - (blockRamU NoClearOnReset depth (errorX "No reset function")) - readAddr - writeOp - (readAddr, writeOp, fifoOut, readyOut, fifoMeta) = - mealyB go initialState (circuitActive, fifoIn, readyIn, bramOut) - - -- Initial state of the FIFO - initialState = - FifoState - { readPointer = 0 - , dataCount = 0 - } - go :: - FifoState depth -> - (Bool, Df.Data a, Ack, a) -> - ( FifoState depth - , (Index depth, Maybe (Index depth, a), Df.Data a, Bool, FifoMeta depth) - ) - go state@FifoState{..} (False, _, _, _) = (state, (readPointer, Nothing, Df.NoData, False, fifoMeta)) - where - fifoEmpty = dataCount == 0 - fifoFull = dataCount == maxBound - fifoMeta = FifoMeta{fifoEmpty, fifoFull, fifoDataCount = dataCount} - go FifoState{..} (True, Df.dataToMaybe -> fifoIn, Ack readyIn, bramOut) = (nextState, output) - where - fifoEmpty = dataCount == 0 - fifoFull = dataCount == maxBound - writePointer = satAdd SatWrap readPointer $ resize dataCount - - readSuccess = not fifoEmpty && readyIn - writeSuccess = not fifoFull && isJust fifoIn - - readPointerNext = if readSuccess then satSucc SatWrap readPointer else readPointer - writeOpGo = if writeSuccess then (writePointer,) <$> fifoIn else Nothing - fifoOutGo = if fifoEmpty then Df.NoData else Df.Data bramOut - - dataCountNext = dataCountDx dataCount - dataCountDx = case (writeSuccess, readSuccess) of - (True, False) -> satSucc SatError - (False, True) -> satPred SatError - _ -> id - - nextState = - FifoState - { readPointer = readPointerNext - , dataCount = dataCountNext - } - - fifoMeta = FifoMeta{fifoEmpty, fifoFull, fifoDataCount = dataCount} - output = (readPointerNext, writeOpGo, fifoOutGo, not fifoFull, fifoMeta) - -{- | Transforms a wishbone interface into a vector based interface. -Write operations will produce a 'Just (Bytes nBytes)' on the index corresponding -to the word-aligned Wishbone address. -Read operations will read from the index corresponding to the world-aligned -Wishbone address. --} -wbToVec :: - forall nBytes addrW nRegisters. - ( KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - , KnownNat nRegisters - , 1 <= nRegisters - ) => - -- | Readable data. - Vec nRegisters (Bytes nBytes) -> - -- | Wishbone bus (master to slave) - WishboneM2S addrW nBytes (Bytes nBytes) -> - -- | - -- 1. Written data - -- 2. Outgoing wishbone bus (slave to master) - ( Vec nRegisters (Maybe (Bytes nBytes)) - , WishboneS2M (Bytes nBytes) - ) -wbToVec readableData WishboneM2S{..} = (writtenData, wbS2M) - where - masterActive = strobe && busCycle - err = masterActive && (addr > resize (pack (maxBound :: Index nRegisters))) - acknowledge = masterActive && not err - wbWriting = writeEnable && acknowledge - wbAddr = unpack $ resize addr :: Index nRegisters - readData = readableData !! wbAddr - writtenData - | wbWriting = replace wbAddr (Just writeData) (repeat Nothing) - | otherwise = repeat Nothing - wbS2M = (emptyWishboneS2M @(Bytes 4)){acknowledge, readData, err} - -{- | Wishbone accessible circuit that contains a free running 64 bit counter. We can -observe this counter to get a sense of time, overflows should be accounted for by -the master. --} -timeWb :: - forall dom addrW. - ( HiddenClockResetEnable dom - , KnownNat addrW - , 1 <= DomainPeriod dom - ) => - Circuit (Wishbone dom 'Standard addrW (Bytes 4)) () -timeWb = Circuit $ \(wbM2S, _) -> (mealy goMealy (0, 0) wbM2S, ()) - where - goMealy (frozen, count :: Unsigned 64) wbM2S = ((nextFrozen, succ count), wbS2M) - where - freq = natToNum @(DomainToHz dom) :: Unsigned 64 - nextFrozen = if isJust (head writes) then count else frozen - RegisterBank (splitAtI -> (frozenMsbs, frozenLsbs)) = getRegsBe @8 frozen - RegisterBank (splitAtI -> (freqMsbs, freqLsbs)) = getRegsBe @8 freq - (writes, wbS2M) = - wbToVec - (0 :> fmap pack (frozenLsbs :> frozenMsbs :> freqLsbs :> freqMsbs :> Nil)) - wbM2S - -{- | Wishbone wrapper for DnaPortE2, adds extra register with wishbone interface -to access the DNA device identifier. The DNA device identifier is a 96-bit -value, stored in big-endian format. --} -readDnaPortE2Wb :: - forall dom addrW nBytes. - ( HiddenClockResetEnable dom - , KnownNat addrW - , KnownNat nBytes - , 1 <= nBytes - ) => - -- | Simulation DNA value - BitVector 96 -> - Circuit (Wishbone dom 'Standard addrW (Bytes nBytes)) () -readDnaPortE2Wb simDna = circuit $ \wb -> do - dnaDf <- dnaCircuit -< () - _dna <- reg -< (wb, dnaDf) - idC -< () - where - maybeDna = readDnaPortE2 hasClock hasReset hasEnable simDna - regRst = - unsafeFromActiveHigh - $ register True - $ fmap isNothing maybeDna - .||. unsafeToActiveHigh hasReset - reg = withReset regRst $ registerWbC @dom @_ @nBytes @addrW WishbonePriority 0 - dnaCircuit :: Circuit () (Df dom (BitVector 96)) - dnaCircuit = Circuit $ const ((), Df.maybeToData <$> maybeDna) diff --git a/bittide/src/Clash/Cores/Extra.hs b/bittide/src/Clash/Cores/Extra.hs deleted file mode 100644 index ba0eb78ba..000000000 --- a/bittide/src/Clash/Cores/Extra.hs +++ /dev/null @@ -1,236 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE PostfixOperators #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE ViewPatterns #-} - -module Clash.Cores.Extra where - -import Clash.Annotations.Primitive -import Clash.Explicit.Prelude hiding (Fixed, (:<)) - -import Clash.Netlist.Types (BlackBoxContext (..), HWType (..), TemplateFunction (..)) -import Clash.Netlist.Util (stripVoid) -import Data.Fixed (E3, Fixed (..)) -import Data.List.Infinite (Infinite ((:<)), (...)) -import Data.Maybe (fromMaybe) -import Data.String.Interpolate (__i) - -{- | A typical dual flipflop synchronizer, prepended with a flipflop operating -in the source domain. The two flipflops operating in the target domain are -packed tightly together using Vivado's ASYNC_REG synthesis attribute. For more -information see: - - https://docs.xilinx.com/r/en-US/ug901-vivado-synthesis/ASYNC_REG - -HDL generation also generates an @.sdc@ file for Vivado with the correct -timing constraints for the synchronizer. The HDL contains unique register -names so the SDC can match on just these registers. - -__N.B.__: You cannot synchronize words by combining multiple instantiations - of 'safeDffSynchronizer'. If you want to do this, look into - 'dcFifo'. --} -safeDffSynchronizer :: - forall dom1 dom2 a. - ( KnownDomain dom1 - , KnownDomain dom2 - , NFDataX a - , BitSize a ~ 1 - ) => - Clock dom1 -> - Clock dom2 -> - a -> - Signal dom1 a -> - Signal dom2 a -safeDffSynchronizer clk1 clk2 initVal i = - snd $ safeDffSynchronizer0 clk1 clk2 initVal i - -{- | Like 'safeDffSynchronizer', but the source register is provided on the -output for further use in the source domain --} -safeDffSynchronizer0 :: - forall dom1 dom2 a. - ( KnownDomain dom1 - , KnownDomain dom2 - , NFDataX a - , BitSize a ~ 1 - ) => - Clock dom1 -> - Clock dom2 -> - a -> - Signal dom1 a -> - (Signal dom1 a, Signal dom2 a) -safeDffSynchronizer0 clk1 clk2 initVal i = (sOut, dOut) - where - dOut = - flipflop clk2 - . flipflop clk2 - $ unsafeSynchronizer clk1 clk2 sOut - sOut = flipflop clk1 i - flipflop :: (KnownDomain dom) => Clock dom -> Signal dom a -> Signal dom a - flipflop clk = delay clk enableGen initVal -{-# OPAQUE safeDffSynchronizer0 #-} -{-# ANN safeDffSynchronizer0 hasBlackBox #-} -{-# ANN - safeDffSynchronizer0 - ( let - ( dom1 - :< dom2 - :< _nfdatax - :< _bitsize - :< clock1 - :< clock2 - :< initVal - :< inp - :< _ - ) = ((0 :: Int) ...) - - ( regA - :< regB - :< regC - :< _ - ) = ((0 :: Int) ...) - funcName = 'safeDffSynchronizer0 - tfName = 'safeDffSynchronizerTF - in - InlineYamlPrimitive - [Verilog, SystemVerilog] - [__i| - BlackBox: - kind: Declaration - name: #{funcName} - template: |- - // begin safeDffSynchronizer - (* DONT_TOUCH = "yes" *) reg ~INCLUDENAME[0]_~GENSYM[dff_sync_a][#{regA}] = ~CONST[#{initVal}]; - (* ASYNC_REG = "TRUE" *) reg ~INCLUDENAME[0]_~GENSYM[dff_sync_b][#{regB}] = ~CONST[#{initVal}], ~GENSYM[dff_sync_c][#{regC}] = ~CONST[#{initVal}]; - - always @(~IF~ACTIVEEDGE[Rising][#{dom1}]~THENposedge~ELSEnegedge~FI ~ARG[#{clock1}]) begin - ~INCLUDENAME[0]_~SYM[#{regA}] <= ~VAR[in][#{inp}]; - end - - always @(~IF~ACTIVEEDGE[Rising][#{dom2}]~THENposedge~ELSEnegedge~FI ~ARG[#{clock2}]) begin - ~INCLUDENAME[0]_~SYM[#{regB}] <= ~INCLUDENAME[0]_~SYM[#{regA}]; - ~SYM[#{regC}] <= ~INCLUDENAME[0]_~SYM[#{regB}]; - end - - assign ~RESULT = {~INCLUDENAME[0]_~SYM[#{regA}], ~SYM[#{regC}]}; - // end safeDffSynchronizer - includes: - - extension: sdc - name: dff_sync - format: Haskell - templateFunction: #{tfName} -|] - ) - #-} -{-# ANN - safeDffSynchronizer0 - ( let - ( dom1 - :< dom2 - :< _nfdatax - :< _bitsize - :< clock1 - :< clock2 - :< initVal - :< inp - :< _ - ) = ((0 :: Int) ...) - - ( regA - :< regB - :< regC - :< block - :< _ - ) = ((0 :: Int) ...) - funcName = 'safeDffSynchronizer0 - tfName = 'safeDffSynchronizerTF - in - InlineYamlPrimitive - [VHDL] - [__i| - BlackBox: - kind: Declaration - name: #{funcName} - template: |- - -- begin safeDffSynchronizer - ~GENSYM[_safeDffSynchronizer][#{block}] : block - signal ~INCLUDENAME[0]_~GENSYM[dff_sync_a][#{regA}] : ~TYP[#{inp}] := ~CONST[#{initVal}]; - signal ~INCLUDENAME[0]_~GENSYM[dff_sync_b][#{regB}] : ~TYP[#{inp}] := ~CONST[#{initVal}]; - signal ~GENSYM[dff_sync_c][#{regC}] : ~TYP[#{inp}] := ~CONST[#{initVal}]; - - attribute DONT_TOUCH : string; - attribute DONT_TOUCH of ~INCLUDENAME[0]_~SYM[#{regA}] : signal is "TRUE"; - - attribute ASYNC_REG : string; - attribute ASYNC_REG of ~INCLUDENAME[0]_~SYM[#{regB}] : signal is "TRUE"; - attribute ASYNC_REG of ~SYM[#{regC}] : signal is "TRUE"; - begin - process(~ARG[#{clock1}]) - begin - if ~IF~ACTIVEEDGE[Rising][#{dom1}]~THENrising_edge~ELSEfalling_edge~FI(~ARG[#{clock1}]) then - ~INCLUDENAME[0]_~SYM[#{regA}] <= ~VAR[in][#{inp}]; - end if; - end process; - - process(~ARG[#{clock2}]) - begin - if ~IF~ACTIVEEDGE[Rising][#{dom2}]~THENrising_edge~ELSEfalling_edge~FI(~ARG[#{clock2}]) then - ~INCLUDENAME[0]_~SYM[#{regB}] <= ~INCLUDENAME[0]_~SYM[#{regA}]; - ~SYM[#{regC}] <= ~INCLUDENAME[0]_~SYM[#{regB}]; - end if; - end process; - - ~RESULT <= (~INCLUDENAME[0]_~SYM[#{regA}], ~SYM[#{regC}]); - end block; - -- end safeDffSynchronizer - includes: - - extension: sdc - name: dff_sync - format: Haskell - templateFunction: #{tfName} -|] - ) - #-} - -safeDffSynchronizerTF :: TemplateFunction -safeDffSynchronizerTF = - let - ( dom1Used - :< dom2Used - :< _nfdatax - :< _bitsize - :< _clock1 - :< _clock2 - :< _initVal - :< _inp - :< _ - ) = ((0 :: Int) ...) - in - TemplateFunction [dom1Used, dom2Used] (const True) $ \bbCtx -> - pure . fromMaybe (error "Pattern match failure") $ do - [compName] <- pure (bbQsysIncName bbCtx) - [ (_, stripVoid -> dom1, _) - , (_, stripVoid -> dom2, _) - , _nfdatax - , _bitsize - , _clock1 - , _clock2 - , _initVal - , _inp - ] <- - pure (bbInputs bbCtx) - KnownDomain _ dom1Period _ _ _ _ <- pure dom1 - KnownDomain _ dom2Period _ _ _ _ <- pure dom2 - let minPeriodNs = MkFixed $ min dom1Period dom2Period :: Fixed E3 - [__i| - set_max_delay \\ - -datapath_only \\ - -from \\ - [get_pins -hierarchical *#{compName}_dff_sync_a*/C] \\ - -to \\ - [get_pins -hierarchical *#{compName}_dff_sync_b*/D] \\ - #{minPeriodNs} - |] diff --git a/bittide/src/Clash/Cores/UART/Extra.hs b/bittide/src/Clash/Cores/UART/Extra.hs deleted file mode 100644 index 25367c09b..000000000 --- a/bittide/src/Clash/Cores/UART/Extra.hs +++ /dev/null @@ -1,87 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# OPTIONS_GHC -fplugin Protocols.Plugin #-} - -module Clash.Cores.UART.Extra ( - module Clash.Cores.UART.Extra, - System.IO.stdin, - System.IO.stdout, -) where - -import Clash.Prelude - -import Clash.Cores.UART -import Data.Char -import Data.Maybe -import GHC.IO -import Protocols -import Protocols.Df hiding (catMaybes, pure, sample) -import System.IO - -import Bittide.Wishbone - -import qualified Protocols.Df as Df - --- | The maximum baud rate for a given domain, useful for simulation purposes -type MaxBaudRate dom = Div (DomainToHz dom) 16 - -{- | A simulation function for circuits that expose a UART connection. -This function reads from the provided input handle and feeds that to the UART circuit. -Incoming uart data is written the the output handle. --} -uartIO :: - forall dom baud. - (KnownDomain dom, ValidBaud dom baud) => - -- | A handle for the input data stream, use `stdin` for terminal input. - Handle -> - -- | A handle for the output data stream, use `stdout` for terminal output. - Handle -> - -- | The baud rate for the UART communication. - SNat baud -> - -- | The circuit to be simulated. - Circuit (CSignal dom Bit) (CSignal dom Bit) -> - -- | The IO action that performs simulation. - IO () -uartIO inputHandle outputHandle baud uartCircuit = do - printList . catMaybes $ Df.sample def $ ioCircuit - where - printList :: [(BitVector 8)] -> IO () - printList [] = pure () - printList (x : xs) = do - hPutChar outputHandle . chr $ fromIntegral x - hFlush outputHandle - printList xs - - ioList :: Handle -> [IO (Maybe Char)] - ioList h = flip (:) (ioList h) $ do - charReady <- hReady h - if charReady - then (fmap Just) $ hGetChar h - else pure Nothing - - input = fmap (fmap (fromIntegral . ord) . unsafePerformIO) $ ioList inputHandle - ioCircuit = circuit $ \_n -> do - (receivedByte, txBit) <- uartWithLists baud input -< rxBit - rxBit <- uartCircuit -< txBit - unsafeToDf -< receivedByte - -{- | A simulation function for circuits that expose a UART connection. -This function transforms a list into its respective uart driver, it also returns -a `CSignal` containing the `BitVector 8`s that are received on the incoming uart signal. --} -uartWithLists :: - forall dom baud. - (KnownDomain dom, ValidBaud dom baud) => - -- | The baud rate for the UART communication. - SNat baud -> - -- | List of `BitVector 8`s to be communicated over `UART`. - [Maybe (BitVector 8)] -> - -- | The circuit to be simulated. - Circuit (CSignal dom Bit) (CSignal dom (Maybe (BitVector 8)), CSignal dom Bit) -uartWithLists baud input = - withClock clockGen $ withReset resetGen $ circuit $ \txBit -> do - dfIn <- drive def{resetCycles = 1} input - (receivedByte, rxBit) <- exposeEnable uartDf enableGen baud -< (dfIn, txBit) - idC -< (receivedByte, rxBit) diff --git a/bittide/src/Clash/Cores/Xilinx/Extra.hs b/bittide/src/Clash/Cores/Xilinx/Extra.hs deleted file mode 100644 index e3a7bd18e..000000000 --- a/bittide/src/Clash/Cores/Xilinx/Extra.hs +++ /dev/null @@ -1,99 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} - -module Clash.Cores.Xilinx.Extra ( - ibufds, - readDnaPortE2I, - module GTH, - - -- * Internal - ibufdsTF, -) where - -import Clash.Prelude - -import Data.String.Interpolate (__i) - -import Clash.Annotations.Primitive -import Clash.Backend (Backend) -import Clash.Cores.Xilinx.Unisim.DnaPortE2 (readDnaPortE2) -import Clash.Netlist.Types -import Control.Monad.State (State) -import Data.Text.Prettyprint.Doc.Extra (Doc) -import Text.Show.Pretty (ppShow) - -import Clash.Cores.Xilinx.GTH as GTH - -import qualified Clash.Netlist.Id as Id -import qualified Clash.Primitives.DSL as DSL -import qualified Prelude as P - --- | Like 'dnaPortE2', but with a hidden clock, reset, and enable -readDnaPortE2I :: - (HiddenClockResetEnable dom) => - -- | DNA value to use in simulation - BitVector 96 -> - -- | Extracted DNA value from FPGA. Will take ~100 cycles to become available. - Signal dom (Maybe (BitVector 96)) -readDnaPortE2I = hideClockResetEnable readDnaPortE2 - -{- | A differential input buffer. For more information see: - - https://docs.xilinx.com/r/en-US/ug974-vivado-ultrascale-libraries/IBUFDS --} -ibufds :: (KnownDomain dom) => DiffClock dom -> Clock dom -ibufds !_ = clockGen -{-# ANN ibufds hasBlackBox #-} -{-# OPAQUE ibufds #-} -{-# ANN - ibufds - ( InlineYamlPrimitive - [minBound ..] - [__i| - BlackBox: - name: Clash.Cores.Xilinx.Extra.ibufds - kind: Declaration - format: Haskell - templateFunction: Clash.Cores.Xilinx.Extra.ibufdsTF - |] - ) - #-} - -{- | Template function for 'ibufds'. - -TODO: Upstream to @clash-cores@ --} -ibufdsTF :: TemplateFunction -ibufdsTF = TemplateFunction used valid go - where - used = [0, 1] - valid = const True - - go :: (Backend s) => BlackBoxContext -> State s Doc - go bbCtx - | [_knownDomain, clk] <- P.map fst (DSL.tInputs bbCtx) - , DataCon (Product "Clash.Signal.Internal.DiffClock" _ clkTys) _ clkEs <- DSL.eex clk - , [clkP@(Identifier _ Nothing), clkN@(Identifier _ Nothing)] <- clkEs - , [clkPTy, clkNTy] <- clkTys = - do - instLabel <- Id.makeBasic "ibufds_inst" - - DSL.declarationReturn bbCtx "ibufds_block" $ do - ibufdsOut <- DSL.declare "ibufds_out" Bit - - let - compName = "IBUFDS" - compInps = [("I", Bit), ("IB", Bit)] - compOuts = [("O", Bit)] - inps = [("I", DSL.TExpr clkPTy clkP), ("IB", DSL.TExpr clkNTy clkN)] - outs = [("O", ibufdsOut)] - - DSL.compInBlock compName compInps compOuts - DSL.instDecl Empty (Id.unsafeMake compName) instLabel [] inps outs - - pure [ibufdsOut] - go bbCtx = error ("ibufdsTemplate:\n\n" <> ppShow bbCtx) diff --git a/bittide/src/Clash/Cores/Xilinx/GTH.hs b/bittide/src/Clash/Cores/Xilinx/GTH.hs deleted file mode 100644 index ba7ca1c66..000000000 --- a/bittide/src/Clash/Cores/Xilinx/GTH.hs +++ /dev/null @@ -1,7 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Clash.Cores.Xilinx.GTH (module X) where - -import Clash.Cores.Xilinx.GTH.Internal as X diff --git a/bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs b/bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs deleted file mode 100644 index 23cea2c4c..000000000 --- a/bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs +++ /dev/null @@ -1,279 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ViewPatterns #-} - -module Clash.Cores.Xilinx.GTH.BlackBoxes where - -import Prelude - -import Control.Monad.State (State) -import Data.String (fromString) -import Data.Text (Text) -import Data.Text.Prettyprint.Doc.Extra (Doc) -import GHC.Stack (HasCallStack) -import Text.Show.Pretty (ppShow) - -import Clash.Backend (Backend) -import Clash.Netlist.BlackBox.Types (BlackBoxFunction, emptyBlackBoxMeta) -import Clash.Netlist.BlackBox.Util (exprToString) -import Clash.Netlist.Types - -import Clash.Cores.Xilinx.Internal ( - BraceTcl (..), - IpConfig (properties), - TclPurpose (..), - defIpConfig, - property, - renderTcl, - ) - -import qualified Clash.Netlist.BlackBox.Types as N -import qualified Clash.Netlist.Id as Id -import qualified Clash.Netlist.Types as N -import qualified Clash.Primitives.DSL as DSL - -gthCoreBBF :: (HasCallStack) => BlackBoxFunction -gthCoreBBF _isD _primName _args _resTys = pure $ Right (bbMeta, bb) - where - bbMeta = - emptyBlackBoxMeta - { N.bbKind = N.TDecl - , N.bbIncludes = - [ - ( ("gth", "clash.tcl") - , BBFunction (show 'gthCoreTclTF) 0 gthCoreTclTF - ) - ] - } - - bb :: BlackBox - bb = BBFunction (show 'gthCoreTF) 0 gthCoreTF - -nConstraints :: Int -nConstraints = 6 - -nNameArgs :: Int -nNameArgs = 2 - --- | Instantiate IP generated with 'gthCoreTclTF' -gthCoreTF :: (HasCallStack) => TemplateFunction -gthCoreTF = - TemplateFunction - [0 .. 10] - (const True) - gthCoreBBTF - -gthCoreBBTF :: - (Backend s) => - BlackBoxContext -> - State s Doc -gthCoreBBTF bbCtx - | args@[ _gthrxn_in -- " ::: Signal rxS (BitVector ChansUsed) - , _gthrxp_in -- " ::: Signal rxS (BitVector ChansUsed) - , gtwiz_reset_clk_freerun_in -- " ::: Clock freerun - , _gtwiz_reset_all_in -- " ::: Reset freerun - , _gtwiz_reset_rx_datapath_in -- " ::: Reset freerun - , _gtwiz_userdata_tx_in -- " ::: Signal txUser2 (BitVector (ChansUsed*TX_DATA_WIDTH)) - , _txctrl2_in -- " ::: Signal txUser2 (BitVector (ChansUsed*TX_DATA_WIDTH/8)) - , _gtrefclk0_in -- " ::: Clock refclk0 - ] <- - drop (nConstraints + nNameArgs) $ map fst (DSL.tInputs bbCtx) - , [tResult] <- map DSL.ety (DSL.tResults bbCtx) - , [gthCoreName] <- N.bbQsysIncName bbCtx = - do - gthCoreInstName <- Id.makeBasic "gthcore_inst" - - let - chansUsed = 1 - tX_DATA_WIDTH = 64 - rX_DATA_WIDTH = tX_DATA_WIDTH - compInps = - [ ("gthrxn_in", N.BitVector chansUsed) - , ("gthrxp_in", N.BitVector chansUsed) - , ("gtwiz_reset_clk_freerun_in", N.Clock "freerun") - , ("gtwiz_reset_all_in", N.Reset "freerun") - , ("gtwiz_reset_rx_datapath_in", N.Reset "freerun") - , ("gtwiz_userdata_tx_in", N.BitVector (chansUsed * tX_DATA_WIDTH)) - , ("txctrl2_in", N.BitVector (chansUsed * (tX_DATA_WIDTH `div` 8))) - , -- , ("gtrefclk00_in", N.Clock "refclk00" ) - ("gtrefclk0_in", N.Clock "refclk0") - ] - <> map (fmap DSL.ety) otherInps - - otherInps = - [ ("drpclk_in", gtwiz_reset_clk_freerun_in) - , ("txctrl0_in", DSL.bvLit 16 0) - , ("txctrl1_in", DSL.bvLit 16 0) - , ("gtwiz_reset_tx_pll_and_datapath_in", DSL.bvLit 1 0) - , ("gtwiz_reset_tx_datapath_in", DSL.bvLit 1 0) - , ("gtwiz_reset_rx_pll_and_datapath_in", DSL.bvLit 1 0) - , ("tx8b10ben_in", DSL.bvLit 1 1) - , ("rx8b10ben_in", DSL.bvLit 1 1) - , ("gtwiz_userclk_tx_reset_in", DSL.bvLit 1 0) - , ("gtwiz_userclk_rx_reset_in", DSL.bvLit 1 0) - , ("rxcommadeten_in", DSL.bvLit 1 1) - , ("rxmcommaalignen_in", DSL.bvLit 1 1) - , ("rxpcommaalignen_in", DSL.bvLit 1 1) - ] - compOuts = - [ ("gthtxn_out", N.BitVector chansUsed) - , ("gthtxp_out", N.BitVector chansUsed) - , ("gtwiz_userclk_tx_usrclk2_out", N.Clock "txUser2") - , ("gtwiz_userclk_rx_usrclk2_out", N.Clock "rxUser2") - , ("gtwiz_userdata_rx_out", N.BitVector (chansUsed * rX_DATA_WIDTH)) - , ("gtwiz_reset_tx_done_out", N.BitVector 1) - , ("gtwiz_reset_rx_done_out", N.BitVector 1) - , ("gtwiz_userclk_tx_active_out", N.BitVector 1) - , ("rxctrl0_out", N.BitVector 16) - , ("rxctrl1_out", N.BitVector 16) - , ("rxctrl2_out", N.BitVector 8) - , ("rxctrl3_out", N.BitVector 8) - ] - - DSL.declarationReturn bbCtx "gthCore_inst_block" $ do - DSL.compInBlock gthCoreName compInps compOuts - - let inps = zip (fst <$> compInps) args <> otherInps - outs <- mapM (uncurry DSL.declare) compOuts - DSL.instDecl - N.Empty - (Id.unsafeMake gthCoreName) - gthCoreInstName - [] - inps - (zip (fst <$> compOuts) outs) - pure [DSL.constructProduct tResult outs] -gthCoreBBTF bbCtx = error ("gthCoreBBTF, bad bbCtx:\n\n" <> ppShow bbCtx) - -{- | Renders Tcl file conforming to the /Clash\<->Tcl API/, creating the Xilinx -IP with @create_ip@ --} -gthCoreTclTF :: (HasCallStack) => TemplateFunction -gthCoreTclTF = - TemplateFunction - [0, 1] -- used arguments - (const True) - gthCoreTclBBTF - -gthCoreTclBBTF :: - (Backend s) => - BlackBoxContext -> - State s Doc -gthCoreTclBBTF bbCtx - | [gthCoreName] <- N.bbQsysIncName bbCtx - , (exprToString -> Just channelNm, _, _) : (exprToString -> Just refClkNm, _, _) : _ <- - drop nConstraints (N.bbInputs bbCtx) = - pure (renderTcl [IpConfigPurpose $ ipConfig gthCoreName channelNm refClkNm]) - where - ipConfig nm channelNm refClkNm = - (defIpConfig "gtwizard_ultrascale " "1.7" nm) - { properties = props channelNm refClkNm - } - - props channelNm refClkNm = - [ property @Text "CHANNEL_ENABLE" (fromString channelNm) - , property @Text "LOCATE_COMMON" "CORE" - , property @Text "LOCATE_IN_SYSTEM_IBERT_CORE" "NONE" - , property @Text "LOCATE_RESET_CONTROLLER" "CORE" - , property @Text "LOCATE_RX_BUFFER_BYPASS_CONTROLLER" "CORE" - , property @Text "LOCATE_RX_USER_CLOCKING" "CORE" - , property @Text "LOCATE_TX_BUFFER_BYPASS_CONTROLLER" "CORE" - , property @Text "LOCATE_TX_USER_CLOCKING" "CORE" - , property @Text "LOCATE_USER_DATA_WIDTH_SIZING" "CORE" - , property @Text "FREERUN_FREQUENCY" "125.0" - , property @Text "RX_REFCLK_FREQUENCY" "200" - , -- .X_REFCLK_SOURCE syntax: X0Yn clk[0,1]([+,-]q - property - "RX_REFCLK_SOURCE" - (BraceTcl @Text $ fromString $ unwords [channelNm, refClkNm]) - , property @Text "RX_DATA_DECODING" "8B10B" - , property @Text "RX_INT_DATA_WIDTH" "40" - , -- , property @Text "RX_JTOL_FC" "5.9988002" - property @Text "RX_LINE_RATE" "10" - , -- , property @Text "RX_MASTER_CHANNEL" "X0Y10" - property @Text "RX_OUTCLK_SOURCE" "RXOUTCLKPMA" - , property @Text "RX_PLL_TYPE" "CPLL" - , property @Text "RX_PPM_OFFSET" "200" - , property @Text "RX_USER_DATA_WIDTH" "64" - , property @Text "RX_EQ_MODE" "LPM" - , property @Text "RX_COMMA_PRESET" "K28.5" - , property @Bool "RX_COMMA_P_ENABLE" True - , property @Bool "RX_COMMA_M_ENABLE" True - , -- , property @Text "RX_COMMA_P_VAL" "0101111100" - -- , property @Text "RX_COMMA_M_VAL" "1010000011" - -- , property @Text "RX_COMMA_MASK" "1111111111" - property @Bool "RX_COMMA_SHOW_REALIGN_ENABLE" False - , property @Text "TX_REFCLK_FREQUENCY" "200" - , property - "TX_REFCLK_SOURCE" - (BraceTcl @Text $ fromString $ unwords [channelNm, refClkNm]) - , property @Text "TXPROGDIV_FREQ_VAL" "250" - , property @Text "TX_DATA_ENCODING" "8B10B" - , property @Text "TX_INT_DATA_WIDTH" "40" - , property @Text "TX_LINE_RATE" "10" - , -- , property @Text "TX_MASTER_CHANNEL" "X0Y10" - property @Text "TX_PLL_TYPE" "CPLL" - , property @Text "TXPROGDIV_FREQ_SOURCE" "CPLL" - , property @Text "TX_USER_DATA_WIDTH" "64" - ] -gthCoreTclBBTF bbCtx = error ("gthCoreTclBBTF, bad bbCtx:\n\n" <> ppShow bbCtx) - -ibufds_gte3BBF :: (HasCallStack) => BlackBoxFunction -ibufds_gte3BBF _isD _primName _args _resTys = - let - bbMeta = emptyBlackBoxMeta{N.bbKind = N.TDecl} - - bb :: BlackBox - bb = BBFunction (show 'ibufds_gte3TF) 0 ibufds_gte3TF - in - pure $ Right (bbMeta, bb) - -ibufds_gte3TF :: (HasCallStack) => TemplateFunction -ibufds_gte3TF = - TemplateFunction - [0, 1] - (const True) - ibufds_gte3BBTF - -ibufds_gte3BBTF :: - (Backend s) => - BlackBoxContext -> - State s Doc -ibufds_gte3BBTF bbCtx - | [_knownDomain, clk] <- map fst (DSL.tInputs bbCtx) - , DataCon (Product "Clash.Signal.Internal.DiffClock" _ clkTys) _ clkEs <- DSL.eex clk - , [clkP@(Identifier _ Nothing), clkN@(Identifier _ Nothing)] <- clkEs - , [clkPTy, clkNTy] <- clkTys = - do - ibufds_gte3InstName <- Id.makeBasic "ibufds_gte3_inst" - - let - inps = - [ ("I", DSL.TExpr clkPTy clkP) - , ("IB", DSL.TExpr clkNTy clkN) - , -- Tied off: - ("CEB", DSL.Low) - ] - - compOuts = - [ ("O", Bit) - ] - attrs = - [ ("REFCLK_EN_TX_PATH", DSL.Low) - , ("REFCLK_HROW_CK_SEL", DSL.bvLit 2 0b10) - , ("REFCLK_ICNTL_RX", DSL.bvLit 2 0b00) - ] - ibufds_gte3Name = "IBUFDS_GTE3" - DSL.declarationReturn bbCtx "ibufds_gte3_inst_block" $ do - outs <- mapM (uncurry DSL.declare) compOuts - DSL.instDecl - N.Empty - (Id.unsafeMake ibufds_gte3Name) - ibufds_gte3InstName - attrs - inps - (zip (fst <$> compOuts) outs) - pure outs -ibufds_gte3BBTF bbCtx = error ("ibufds_gte3BBTF, bad bbCtx: " <> show bbCtx) diff --git a/bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs b/bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs deleted file mode 100644 index 353c6ede4..000000000 --- a/bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs +++ /dev/null @@ -1,112 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE QuasiQuotes #-} - -module Clash.Cores.Xilinx.GTH.Internal where - -import Clash.Prelude - -import Clash.Annotations.Primitive (Primitive (InlineYamlPrimitive), hasBlackBox) -import Data.String.Interpolate (__i) - -import Clash.Cores.Xilinx.GTH.BlackBoxes - -type TX_DATA_WIDTH = 64 -type RX_DATA_WIDTH = 64 - -type GthCore txUser2 rxUser2 refclk0 freerun txS rxS serializedData = - ( KnownDomain txUser2 - , KnownDomain rxUser2 - , KnownDomain refclk0 - , KnownDomain freerun - , KnownDomain txS - , KnownDomain rxS - ) => - -- | channel - String -> - -- | refClkSpec - String -> - "gthrxn_in" ::: Signal rxS serializedData -> - "gthrxp_in" ::: Signal rxS serializedData -> - "gtwiz_reset_clk_freerun_in" ::: Clock freerun -> - "gtwiz_reset_all_in" ::: Reset freerun -> - "gtwiz_reset_rx_datapath_in" ::: Reset freerun -> - "gtwiz_userdata_tx_in" ::: Signal txUser2 (BitVector TX_DATA_WIDTH) -> - "txctrl2_in" ::: Signal txUser2 (BitVector (DivRU TX_DATA_WIDTH 8)) -> - "gtrefclk0_in" ::: Clock refclk0 -> - ( "gthtxn_out" ::: Signal txS serializedData - , "gthtxp_out" ::: Signal txS serializedData - , "gtwiz_userclk_tx_usrclk2_out" ::: Clock txUser2 - , "gtwiz_userclk_rx_usrclk2_out" ::: Clock rxUser2 - , "gtwiz_userdata_rx_out" ::: Signal rxUser2 (BitVector RX_DATA_WIDTH) - , "gtwiz_reset_tx_done_out" ::: Signal txUser2 (BitVector 1) - , "gtwiz_reset_rx_done_out" ::: Signal rxUser2 (BitVector 1) - , "gtwiz_userclk_tx_active_out" ::: Signal txUser2 (BitVector 1) - , "rxctrl0_out" ::: Signal rxUser2 (BitVector 16) - , "rxctrl1_out" ::: Signal rxUser2 (BitVector 16) - , "rxctrl2_out" ::: Signal rxUser2 (BitVector 8) - , "rxctrl3_out" ::: Signal rxUser2 (BitVector 8) - ) - -gthCore :: GthCore txUser2 rxUser2 refclk0 freerun txS rxS (BitVector 1) -gthCore - !_channel - !_refClkSpec - !_gthrxn_in - !_gthrxp_in - !_gtwiz_reset_clk_freerun_in - !_gtwiz_reset_all_in - !_gtwiz_reset_rx_datapath_in - !_gtwiz_userdata_tx_in - !_txctrl2_in - !_gtrefclk0_in = - ( undefined - , undefined - , undefined - , undefined - , undefined - , undefined - , undefined - , undefined - , undefined - , undefined - , undefined - , undefined - ) -{-# OPAQUE gthCore #-} -{-# ANN gthCore hasBlackBox #-} -{-# ANN - gthCore - ( let primName = 'gthCore - tfName = 'gthCoreBBF - in InlineYamlPrimitive - [minBound ..] - [__i| - BlackBoxHaskell: - name: #{primName} - templateFunction: #{tfName} - workInfo: Always - |] - ) - #-} - -ibufds_gte3 :: (KnownDomain dom) => DiffClock dom -> Clock dom -ibufds_gte3 !_clk = clockGen -{-# OPAQUE ibufds_gte3 #-} -{-# ANN ibufds_gte3 hasBlackBox #-} -{-# ANN - ibufds_gte3 - ( let primName = 'ibufds_gte3 - tfName = 'ibufds_gte3BBF - in InlineYamlPrimitive - [minBound ..] - [__i| - BlackBoxHaskell: - name: #{primName} - templateFunction: #{tfName} - workInfo: Always - |] - ) - #-} diff --git a/bittide/src/Clash/Cores/Xilinx/SystemMonitor.hs b/bittide/src/Clash/Cores/Xilinx/SystemMonitor.hs deleted file mode 100644 index bd3b06b7d..000000000 --- a/bittide/src/Clash/Cores/Xilinx/SystemMonitor.hs +++ /dev/null @@ -1,266 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE NoFieldSelectors #-} - -module Clash.Cores.Xilinx.SystemMonitor ( - Status (..), - temperatureMonitorCelcius, - temperatureMonitor, - toCelcius, -) where - -import Clash.Prelude - -import Clash.Cores.Xilinx.Xpm.Cdc.Internal -import Clash.Functor.Extra ((<<$>>)) - -{- | A wrapper for a SYSMONE1 instance which only monitors the FPGA temperature. Converts -the measured value of the temperature sensor to the temperature in degree Celcius. - -XXX: This function can lead to timing issues because the instantiated DSP does - not include input/output registers. --} -temperatureMonitorCelcius :: - forall dom. - (HiddenClockResetEnable dom, 1 <= DomainPeriod dom) => - Signal dom (Status, Maybe (Signed 11)) -temperatureMonitorCelcius = bundle (status, temperature) - where - (status, measurement) = unbundle temperatureMonitor - temperature = toSigned <<$>> dflipflop (toCelcius . unpack <<$>> dflipflop measurement) - where - toSigned :: - forall int frac. (KnownNat int, KnownNat frac) => SFixed int frac -> Signed int - toSigned = (resize . bitCoerce) . flip shiftR (natToNum @frac) - -{- | A wrapper for a SYSMONE1 instance which only monitors the FPGA temperature and -outputs the raw measurement. --} -temperatureMonitor :: - forall dom. - (HiddenClockResetEnable dom, 1 <= DomainPeriod dom) => - Signal dom (Status, Maybe (BitVector 10)) -temperatureMonitor = bundle (status, tOut) - where - tOut = mux dRdy (Just <$> measurement) (pure Nothing) - - -- The 10-bit measurement is stored in the MSBs. - measurement = resize . flip shiftR 6 <$> dO - (status, dRdy, dO) = unbundle $ sysMon @dom hasClock hasReset dEn dAddr dWe dI - - dEn = counter .==. pure 0 - dWe = pure False - dAddr = pure 0x00 - dI = pure 0x00 - - counter = register (0 :: Index 10) $ satSucc SatWrap <$> counter - -{- | Convert the measurement to temperature in degrees Celcius. Assumes the temperature -sensor uses the on-chip reference. For more information see: - - https://docs.amd.com/v/u/en-US/ug580-ultrascale-sysmon --} -toCelcius :: - forall adcDepth. (KnownNat adcDepth) => Unsigned adcDepth -> SFixed (adcDepth + 1) 14 -toCelcius measurement = t - where - t = measurementSF * (a `shiftR` (natToNum @adcDepth)) + b - measurementSF :: SFixed (adcDepth + 1) 14 - measurementSF = bitCoerce (resize measurement `shiftL` 14) - a = 501.3743 - b = -273.6777 - -data Status = Status - { busy :: Bool - , channel :: BitVector 6 - , endOfConversion :: Bool - , endOfSequence :: Bool - } - deriving (Eq, Generic, NFDataX) - -{- The internal ADC clock is derived from the DRP clock. The SYSMON supports a DRP clock -frequency up to 250 MHz, whereas the The maximum frequency of the ADC clock is 5.2 MHz. -A clock divider with a minimal value of 2 is used to get the appropriate ADC clock -frequency. --} -type AdcClockDiv dom = Max 2 (HzToPeriod 5_200_000 `DivRU` DomainPeriod dom) - -{- | A System Monitor (SYSMONE1) instance with an exposed Dynamic Reconfiguration Port -(DRP) interface. The SYSMON supports a DRP clock frequency up to 250 MHz. The clock -division ratio between the DRP clock (dClk) and and the lower frequency ADC clock is set -such that the ADC clock frequency is at most 5.2 MHz (the maximum ADCCCLK frequency). -For more information see: - - https://docs.amd.com/r/en-US/ug974-vivado-ultrascale-libraries/SYSMONE1 - -TODO: Make the instance configurable through its parameters. --} -sysMon :: - forall dom. - (KnownDomain dom, 1 <= DomainPeriod dom) => - -- | Clock input for the dynamic reconfiguration port. - Clock dom -> - -- | Reset signal for the SYSMON control logic. - Reset dom -> - -- | Enable signal for the dynamic reconfiguration port. - Signal dom Bool -> - -- | DRP address bus - Signal dom (BitVector 8) -> - -- | DRP write enable - Signal dom Bool -> - -- | DRP input data bus - Signal dom (BitVector 16) -> - -- | (SYSMON status ports, DRP data ready, DRP data) - Signal dom (Status, Bool, BitVector 16) -sysMon dClk rst dEn dAddr dWe dI - | natToInteger @(DomainToHz dom) > 250_000_000 = - clashCompileError "DRP clock frequency cannot be higher than 250 MHz" - | clashSimulation = sim - | otherwise = synth - where - -- Definition used in Clash simulation - sim = pure (status, False, 0) - where - status = - Status - { busy = False - , channel = 0 - , endOfConversion = False - , endOfSequence = False - } - - -- Definition used for HDL generation - clkDiv :: BitVector 8 - clkDiv = natToNum @(AdcClockDiv dom) - synth = bundle (status, dRdy, dO) - where - status = Status <$> busy <*> channel <*> eoc <*> eos - ( _ - , _ - , unPort -> dO - , unPort -> dRdy - , _ - , _ - , unPort -> busy - , unPort -> channel - , unPort -> eoc - , unPort -> eos - , _ - , _ - , _ - , _ - ) = go - - go :: - ( Port "ALM" dom (BitVector 16) - , Port "OT" dom Bool - , Port "DO" dom (BitVector 16) - , Port "DRDY" dom Bool - , Port "I2C_SCLK_TS" dom Bit - , Port "I2C_SDA_TS" dom Bit - , Port "BUSY" dom Bool - , Port "CHANNEL" dom (BitVector 6) - , Port "EOC" dom Bool - , Port "EOS" dom Bool - , Port "JTAGBUSY" dom Bool - , Port "JTAGLOCKED" dom Bool - , Port "JTAGMODIFIED" dom Bool - , Port "MUXADDR" dom (BitVector 5) - ) - go = - inst - (instConfig "SYSMONE1") - { library = Just "UNISIM" - , libraryImport = Just "UNISIM.vcomponents.all" - } -{- ORMOLU_DISABLE -} - -- Analog Bus Register - (Param @"INIT_45" @(BitVector 16) 0) - -- INIT_40 - INIT_44: SYSMON configuration registers - (Param @"INIT_40" @(BitVector 16) 0x9000) -- 16 sample average filter, disable averaging of calibration coefficients - (Param @"INIT_41" @(BitVector 16) 0x2ED0) -- Continuous seq mode, disable ALM[6:4], enable calibration - (Param @"INIT_42" @(BitVector 16) (clkDiv ++# 0)) - (Param @"INIT_43" @(BitVector 16) 0x000F) -- Disable ALM[11:8] - (Param @"INIT_44" @(BitVector 16) 0) - -- INIT_46 - INIT_4F: Sequence Registers - (Param @"INIT_46" @(BitVector 16) 0) - (Param @"INIT_47" @(BitVector 16) 0) - (Param @"INIT_48" @(BitVector 16) 0x4701) -- Enable Temp, VCCINT, VCCAUX, VCCBRAM and calibration - (Param @"INIT_49" @(BitVector 16) 0) - (Param @"INIT_4A" @(BitVector 16) 0) - (Param @"INIT_4B" @(BitVector 16) 0) - (Param @"INIT_4C" @(BitVector 16) 0) - (Param @"INIT_4D" @(BitVector 16) 0) - (Param @"INIT_4E" @(BitVector 16) 0) - (Param @"INIT_4F" @(BitVector 16) 0) - -- INIT_50 - INIT_5F: Alarm Limit Registers - (Param @"INIT_50" @(BitVector 16) 0) - (Param @"INIT_51" @(BitVector 16) 0) - (Param @"INIT_52" @(BitVector 16) 0) - (Param @"INIT_53" @(BitVector 16) 0) - (Param @"INIT_54" @(BitVector 16) 0) - (Param @"INIT_55" @(BitVector 16) 0) - (Param @"INIT_56" @(BitVector 16) 0) - (Param @"INIT_57" @(BitVector 16) 0) - (Param @"INIT_58" @(BitVector 16) 0) - (Param @"INIT_59" @(BitVector 16) 0) - (Param @"INIT_5A" @(BitVector 16) 0) - (Param @"INIT_5B" @(BitVector 16) 0) - (Param @"INIT_5C" @(BitVector 16) 0) - (Param @"INIT_5D" @(BitVector 16) 0) - (Param @"INIT_5E" @(BitVector 16) 0) - (Param @"INIT_5F" @(BitVector 16) 0) - -- INIT_60 - INIT_6F: User Supply Alarms - (Param @"INIT_60" @(BitVector 16) 0) - (Param @"INIT_61" @(BitVector 16) 0) - (Param @"INIT_62" @(BitVector 16) 0) - (Param @"INIT_63" @(BitVector 16) 0) - (Param @"INIT_64" @(BitVector 16) 0) - (Param @"INIT_65" @(BitVector 16) 0) - (Param @"INIT_66" @(BitVector 16) 0) - (Param @"INIT_67" @(BitVector 16) 0) - (Param @"INIT_68" @(BitVector 16) 0) - (Param @"INIT_69" @(BitVector 16) 0) - (Param @"INIT_6A" @(BitVector 16) 0) - (Param @"INIT_6B" @(BitVector 16) 0) - (Param @"INIT_6C" @(BitVector 16) 0) - (Param @"INIT_6D" @(BitVector 16) 0) - (Param @"INIT_6E" @(BitVector 16) 0) - (Param @"INIT_6F" @(BitVector 16) 0) - -- Programmable Inversion Attributes: Specifies the use of the built-in - -- programmable inversion on specific pins. - (Param @"IS_CONVSTCLK_INVERTED" @Bit 0) - (Param @"IS_DCLK_INVERTED" @Bit 0) - -- Analog simulation data file name - (Param @"SIM_MONITOR_FILE" @String "design.txt") - -- SYSMON User voltage monitor - (Param @"SYSMON_VUSER0_BANK" @Integer 0) - (Param @"SYSMON_VUSER0_MONITOR" @String "NONE") - (Param @"SYSMON_VUSER1_BANK" @Integer 0) - (Param @"SYSMON_VUSER1_MONITOR" @String "NONE") - (Param @"SYSMON_VUSER2_BANK" @Integer 0) - (Param @"SYSMON_VUSER2_MONITOR" @String "NONE") - (Param @"SYSMON_VUSER3_BANK" @Integer 0) - (Param @"SYSMON_VUSER3_MONITOR" @String "NONE") - -- Auxiliary Analog-Input Pairs - (Port @"VAUXN" (pure 0 :: Signal dom (BitVector 16))) - (Port @"VAUXP" (pure 0 :: Signal dom (BitVector 16))) - -- SYSMON reset, conversion start and clock inputs - (ResetPort @"RESET" @ActiveHigh rst) - (Port @"CONVSTCLK" (pure 0 :: Signal dom Bit)) - (Port @"CONVST" (pure 0 :: Signal dom Bit)) - -- Dedicated Analog Input Pair - (Port @"VN" (pure 0 :: Signal dom Bit)) - (Port @"VP" (pure 0 :: Signal dom Bit)) - -- Dynamic Reconfiguration Port (DRP) - (ClockPort @"DCLK" dClk) - (Port @"DADDR" dAddr) - (Port @"DEN" dEn) - (Port @"DI" dI) - (Port @"DWE" dWe) - -- I2C interface - (Port @"I2C_SCLK" (pure 0 :: Signal dom Bit)) - (Port @"I2C_SDA" (pure 0 :: Signal dom Bit)) -{- ORMOLU_ENABLE -} diff --git a/bittide/src/Clash/Cores/Xilinx/Xpm/Cdc/Handshake/Extra.hs b/bittide/src/Clash/Cores/Xilinx/Xpm/Cdc/Handshake/Extra.hs deleted file mode 100644 index 46238c420..000000000 --- a/bittide/src/Clash/Cores/Xilinx/Xpm/Cdc/Handshake/Extra.hs +++ /dev/null @@ -1,73 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -module Clash.Cores.Xilinx.Xpm.Cdc.Handshake.Extra where - -import Clash.Explicit.Prelude - -import Clash.Cores.Xilinx.Xpm -import Data.Maybe - -import Bittide.Extra.Maybe - -{- | Reliable CDC component based on `xpmCdcHandshakeMaybe` without backpressure and -with limited throughput. Data will be lost if the `src` domain provides more inputs -than the circuit can handle. Useful for low granularity synchronization, -in our case: synchronizing datacounts. --} -xpmCdcMaybeLossy :: - ( KnownDomain src - , KnownDomain dst - , BitPack a - , NFDataX a - , 1 <= BitSize a - , BitSize a <= 1024 - ) => - -- | Source clock - Clock src -> - -- | Destination clock - Clock dst -> - -- | Data in the source domain - Signal src (Maybe a) -> - -- | Data in the destination domain - Signal dst (Maybe a) -xpmCdcMaybeLossy clkSrc clkDst maybeInp = mux (isRising clkDst noReset enableGen False dstAck) dstOut (pure Nothing) - where - srcReg = - regEn - clkSrc - noReset - enableGen - Nothing - (srcRcv .||. srcRegEmpty) - $ mux (srcRegEmpty .&&. fmap not srcRcv) maybeInp (pure Nothing) - - srcRegEmpty = isNothing <$> srcReg - (srcRcv, dstOut) = xpmCdcHandshakeMaybe clkSrc clkDst srcReg (isJust <$> dstOut) - dstAck = isJust <$> dstOut - --- | `Maybe a` based version of `xpmCdcHandshake`. -xpmCdcHandshakeMaybe :: - ( KnownDomain src - , KnownDomain dst - , BitPack a - , NFDataX a - , 1 <= BitSize a - , BitSize a <= 1024 - ) => - -- | Source clock - Clock src -> - -- | Destination clock - Clock dst -> - -- | Data in the source domain - Signal src (Maybe a) -> - -- | Acknowledgement from destination domain. - Signal dst Bool -> - -- | - -- 1. Acknowledgement in source domain. - -- 2. Data in the destination domain. - (Signal src Bool, Signal dst (Maybe a)) -xpmCdcHandshakeMaybe clkSrc clkDst srcIn dstAck = (srcRcv, orNothing <$> dstReq <*> dstOut) - where - (dstOut, dstReq, srcRcv) = - xpmCdcHandshake clkSrc clkDst (fromMaybe (unpack 0) <$> srcIn) (isJust <$> srcIn) dstAck diff --git a/bittide/src/Clash/Explicit/Reset/Extra.hs b/bittide/src/Clash/Explicit/Reset/Extra.hs deleted file mode 100644 index 3ee01d7f0..000000000 --- a/bittide/src/Clash/Explicit/Reset/Extra.hs +++ /dev/null @@ -1,51 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Clash.Explicit.Reset.Extra where - -import Clash.Cores.Xilinx.Xpm.Cdc.Single -import Clash.Explicit.Prelude - -{- | Configuration value to indicate whether resets should be asserted or -deasserted. Used throughout this module. --} -data Asserted = Asserted | Deasserted - -{- | A reset synchronizer based on 'xpmCdcSingle'. I.e., a reset synchronizer that -is recognized by Vivado as a safe CDC construct. --} -xpmResetSynchronizer :: - (HasSynchronousReset src, KnownDomain dst) => - -- | Initial value of registers in 'xpmCdcSingle' - Asserted -> - Clock src -> - Clock dst -> - Reset src -> - Reset dst -xpmResetSynchronizer asserted clkSrc clkDest = - case asserted of - Asserted -> unsafeFromActiveLow . go . unsafeToActiveLow - Deasserted -> unsafeFromActiveHigh . go . unsafeToActiveHigh - where - go = xpmCdcSingle clkSrc clkDest - -{- | Like 'delay', but for 'Reset'. Can be used to filter glitches caused by -combinatorial logic. --} -delayReset :: - (HasSynchronousReset dom) => - -- | Initial and reset value of register - Asserted -> - Clock dom -> - Reset dom -> - Reset dom -delayReset asserted clk = - unsafeFromActiveHigh - . delay clk enableGen assertedBool - . unsafeToActiveHigh - where - assertedBool = - case asserted of - Asserted -> True - Deasserted -> False diff --git a/bittide/src/Clash/Functor/Extra.hs b/bittide/src/Clash/Functor/Extra.hs deleted file mode 100644 index 4f31b907b..000000000 --- a/bittide/src/Clash/Functor/Extra.hs +++ /dev/null @@ -1,17 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Clash.Functor.Extra where - -import Clash.Prelude - -infixl 4 <<$>> - -(<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b) -(<<$>>) = fmap . fmap - -infixl 4 <<*>> - -(<<*>>) :: (Applicative f, Applicative g) => f (g (a -> b)) -> f (g a) -> f (g b) -(<<*>>) = liftA2 (<*>) diff --git a/bittide/src/Clash/Sized/Extra.hs b/bittide/src/Clash/Sized/Extra.hs deleted file mode 100644 index 76a45efb0..000000000 --- a/bittide/src/Clash/Sized/Extra.hs +++ /dev/null @@ -1,33 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Clash.Sized.Extra where - -import Clash.Prelude - -{- $setup ->>> import Clash.Prelude --} - --- | Safe 'Unsigned' to 'Signed' conversion -unsignedToSigned :: forall n. (KnownNat n) => Unsigned n -> Signed (n + 1) -unsignedToSigned n = bitCoerce (zeroExtend n) - -{- | Combine two 'Unsigned's by concatenating them together. I.e., the first -argument is prepended to the second. - ->>> pack (concatUnsigneds (0b1100 :: Unsigned 4) (0b1111 :: Unsigned 4)) -0b1100_1111 --} -concatUnsigneds :: - forall a b. - (KnownNat a, KnownNat b) => - -- | Most significant bits of result - Unsigned a -> - -- | Least significant bits of result - Unsigned b -> - -- | First and second argument concatenated - Unsigned (a + b) -concatUnsigneds a b = - shiftL (extend a) (natToNum @b) .|. extend b diff --git a/bittide/src/Data/Constraint/Nat/Extra.hs b/bittide/src/Data/Constraint/Nat/Extra.hs deleted file mode 100644 index f94c8198e..000000000 --- a/bittide/src/Data/Constraint/Nat/Extra.hs +++ /dev/null @@ -1,146 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{- -NOTE [constraint solver addition] - -The functions in this module enable us introduce trivial constraints that are not -solved by the constraint solver. - -Machine verifiable Agda proofs of the properties claimed in this file -can be found in @bittide/proofs/TypeNatProofs.agda@ --} -{-# LANGUAGE AllowAmbiguousTypes #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Data.Constraint.Nat.Extra ( - module Data.Constraint.Nat.Extra, - Data.Constraint.Dict (..), -) where - -import Data.Constraint -import Data.Ord ((<=)) -import Data.Proxy -import Data.Type.Bool (If) -import Data.Type.Equality -import GHC.Num ((-)) -import GHC.TypeLits.Extra -import GHC.TypeLits.KnownNat -import GHC.TypeNats -import Unsafe.Coerce - -type family OneMore (n :: Nat) :: Nat where - OneMore 0 = 0 - OneMore _ = 1 - -instance (KnownNat n) => KnownNat1 $(nameToSymbol ''OneMore) n where - natSing1 = case natVal (Proxy @n) of - 0 -> SNatKn 0 - _ -> SNatKn 1 - {-# INLINE natSing1 #-} - --- Note that the first case is redundant, but required as described in this FAQ: --- https://hackage.haskell.org/package/ghc-typelits-knownnat-0.7.10/docs/GHC-TypeLits-KnownNat.html -type family SatSubZero (a :: Nat) (b :: Nat) :: Nat where - SatSubZero 0 b = 0 - SatSubZero a b = If (a <=? b) 0 (a - b) - -instance (KnownNat a, KnownNat b) => KnownNat2 $(nameToSymbol ''SatSubZero) a b where - natSing2 = - let - a = natVal (Proxy @a) - b = natVal (Proxy @b) - z = if a <= b then 0 else a - b - in - SNatKn z - {-# INLINE natSing2 #-} - --- | b <= ceiling(b/a)*a -timesDivRU :: forall a b. (1 <= a) => Dict (b <= (Div (b + (a - 1)) a * a)) -timesDivRU = unsafeCoerce (Dict :: Dict (0 <= 0)) - -{- | Implements logarithmic product rule. Currently hardcoded for specific -constants, which we might relax in the future. --} -clogProductRule :: (1 <= n) => CLog 2 (n * 2) :~: (CLog 2 n + 1) -clogProductRule = unsafeCoerce Refl - -{- | Postulates that multiplying some number /a/ by some constant /b/, and -subsequently dividing that result by /b/ equals /a/. --} -cancelMulDiv :: forall a b. (1 <= b) => Dict (DivRU (a * b) b ~ a) -cancelMulDiv = unsafeCoerce (Dict :: Dict (0 ~ 0)) - -{- | Postulates that adding a constant less than the denominator does not -change the result (for the given specific context). --} -divWithRemainder :: - forall a b c. - (1 <= b, c <= (b - 1)) => - Dict (Div ((a * b) + c) b ~ a) -divWithRemainder = unsafeCoerce (Dict :: Dict (0 ~ 0)) - -{- | Postulates that a part is less than or equal to a sum parts, in context -of 'Max's left argument. --} -leMaxLeft :: forall a b c. Dict (a <= Max (a + c) b) -leMaxLeft = unsafeCoerce (Dict :: Dict (0 <= 0)) - --- | If @c <= a@ and @c <= b@, then @c <= Max a b@ -lessThanMax :: forall a b c. (c <= a, c <= b) => Dict (c <= Max a b) -lessThanMax = unsafeCoerce (Dict :: Dict (0 <= 0)) - -{- | Postulates that a part is less than or equal to a sum parts, in context -of 'Max's right argument. --} -leMaxRight :: forall a b c. Dict (b <= Max a (b + c)) -leMaxRight = unsafeCoerce (Dict :: Dict (0 <= 0)) - --- | if (1 <= a) and (1 <= b) then (1 <= DivRU a b) -strictlyPositiveDivRu :: forall a b. (1 <= a, 1 <= b) => Dict (1 <= DivRU a b) -strictlyPositiveDivRu = unsafeCoerce (Dict :: Dict (0 <= 0)) - -{- | Euclid's third axiom: /If equals be subtracted from equals, the remainders -are equal/. --} -euclid3 :: forall a b c. (a + b <= c) => Dict (a <= c - b) -euclid3 = unsafeCoerce (Dict :: Dict (0 <= 0)) - --- | if (2 <= n) holds, then (1 <= CLog 2 n) also holds. -oneLeCLog2n :: forall n. (2 <= n) => Dict (1 <= CLog 2 n) -oneLeCLog2n = unsafeCoerce (Dict :: Dict (0 <= 0)) - --- | If @1 <= m@ and @n + m <= u@, then @1 + n <= u@ -useLowerLimit :: forall n m u. (1 <= m, n + m <= u) => Dict (1 + n <= u) -useLowerLimit = unsafeCoerce (Dict :: Dict (0 <= 0)) - --- | If @1 <= n@ and @1 <= m@, then @1 <= Div n m + OneMore (Mod n m)@ -oneMore :: forall n m. (1 <= n, 1 <= m) => Dict (1 <= Div n m + OneMore (Mod n m)) -oneMore = unsafeCoerce (Dict :: Dict (0 <= 0)) - --- | If @1 <= n@ and @n <= m@, then @Div n m + OneMore (Mod n m) == 1@ -isOne :: forall n m. (1 <= n, n <= m) => Dict (Div n m + OneMore (Mod n m) ~ 1) -isOne = unsafeCoerce (Dict :: Dict (0 ~ 0)) - --- | Postulates that @SatSubZero a b + Min a b == a@ -satSubZeroMin :: forall a b. Dict (SatSubZero a b + Min a b ~ a) -satSubZeroMin = unsafeCoerce (Dict :: Dict (0 ~ 0)) - --- | Postulates that the minimum of a and b can't be larger than b -minLeq :: forall a b. Dict (Min a b <= b) -minLeq = unsafeCoerce (Dict :: Dict (0 <= 0)) - --- | Postulates that the minimum of a and b can't be larger than b -maxGeqPlus :: forall a b c. Dict (a <= Max a b + c) -maxGeqPlus = unsafeCoerce (Dict :: Dict (0 <= 0)) - -{- | Postulates that multiplying two numbers that are greater than 1 will -result in a number that is greater than 1. --} -leMult :: forall a b. (1 <= a, 1 <= b) => Dict (1 <= a * b) -leMult = unsafeCoerce (Dict :: Dict (0 <= 0)) diff --git a/bittide/src/System/IO/Temp/Extra.hs b/bittide/src/System/IO/Temp/Extra.hs deleted file mode 100644 index 1012e2b27..000000000 --- a/bittide/src/System/IO/Temp/Extra.hs +++ /dev/null @@ -1,35 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -module System.IO.Temp.Extra (withTempBinaryFile) where - -import Prelude - -import Control.Monad.IO.Class -import System.Directory -import System.IO - -import qualified Control.Monad.Catch as MC - -{- | Create, open, and use a temporary binary file in the given directory. - -The temp file is deleted after use. --} -withTempBinaryFile :: - (MonadIO m, MC.MonadMask m) => - -- | Parent directory to create the file in - FilePath -> - -- | File name template - String -> - -- | Callback that can use the file - (FilePath -> Handle -> m a) -> - -- | Callback result - m a -withTempBinaryFile tmpDir template action = - MC.bracket - (liftIO (openBinaryTempFile tmpDir template)) - (\(name, handle) -> liftIO (hClose handle >> ignoringIOErrors (removeFile name))) - (uncurry action) - -ignoringIOErrors :: (MC.MonadCatch m) => m () -> m () -ignoringIOErrors ioe = ioe `MC.catch` (\e -> const (return ()) (e :: IOError)) diff --git a/bittide/tests/Tests/Axi4.hs b/bittide/tests/Tests/Axi4.hs deleted file mode 100644 index 9f086772b..000000000 --- a/bittide/tests/Tests/Axi4.hs +++ /dev/null @@ -1,345 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -Wno-type-defaults #-} -{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} -{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} - -{-# HLINT ignore "Functor law" #-} -{-# HLINT ignore "Used otherwise as a pattern" #-} - -module Tests.Axi4 where - -import Clash.Explicit.Prelude (noReset) -import Clash.Prelude - -import Clash.Hedgehog.Sized.Unsigned -import Data.Either -import Data.Maybe -import Data.Proxy -import Hedgehog -import Protocols -import Protocols.Axi4.Stream -import Test.Tasty -import Test.Tasty.Hedgehog - -import Bittide.Axi4 -import Bittide.Extra.Maybe -import Protocols.Hedgehog -import Tests.Axi4.Generators -import Tests.Axi4.Properties -import Tests.Axi4.Types -import Tests.Shared - -import qualified Data.List as L -import qualified GHC.TypeNats as TN -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range - -tests :: TestTree -tests = - testGroup - "Tests.Axi4" - [ testPropertyNamed - "Read Axi4 Stream packets via Wishbone" - "prop_wbAxisRxBufferReadStreams" - prop_wbAxisRxBufferReadStreams - , testPropertyNamed - "Various operation on Axi4StreamM2S: splitAxi4Stream combineAxi4Stream packAxi4Stream" - "prop_axiOperations" - prop_axiOperations - , testPropertyNamed - "Packet conversion utilies" - "prop_packetConversions" - prop_packetConversions - , testPropertyNamed - "Axi4StreamPacketFifo" - "prop_axi4StreamPacketFifo" - prop_axi4StreamPacketFifo - , testPropertyNamed - "Axi4StreamPacketFifo produces uninterrupted packets" - "prop_axi4StreamPacketFifo_Uninterrupted" - prop_axi4StreamPacketFifo_Uninterrupted - , testPropertyNamed - "axiStreamToByteStream" - "prop_axiStreamToByteStream" - prop_axiStreamToByteStream - , testPropertyNamed - "axiStreamFromByteStream " - "prop_axiStreamFromByteStream" - prop_axiStreamFromByteStream - , testPropertyNamed "packAxi4Stream" "prop_packAxi4Stream" prop_packAxi4Stream - , testPropertyNamed "prop_axiPacking" "prop_axiPacking" prop_axiPacking - ] - --- This test only checks that the data and position bytes are not changed by the component, --- _tdest, _tid and _tuser are not checked. -prop_axiStreamToByteStream :: Property -prop_axiStreamToByteStream = - propWithModel defExpectOptions gen model impl prop - where - impl = wcre @System axiStreamToByteStream - - model = L.concatMap (catMaybes . packetToAxiStream d1) . axiStreamToPackets - - packetGen = - catMaybes - <$> genRandomAxiPacket - d4 - d0 - d0 - [NullByte, DataByte, PositionByte] - (Range.linear 0 16) - (pure ()) - gen = L.concat <$> Gen.list (Range.linear 0 3) packetGen - - prop expected sampled = do - let bytetypes = fmap getPacketByteTypes $ rights $ separatePackets sampled - footnote $ "expected: " <> show expected - footnote $ "sampled: " <> show sampled - footnote $ "bytetypes: " <> show bytetypes - - -- None of the packets start with null bytes - assert $ not (any hasLeadingNullBytes bytetypes) - - -- All packets are packed - assert $ all isPackedAxi4StreamPacket bytetypes - - -- The extracted packets are the same for both the expected and sampled data - axiStreamToPackets expected === axiStreamToPackets sampled - -prop_axi4StreamPacketFifo :: Property -prop_axi4StreamPacketFifo = - idWithModel defExpectOptions gen id impl - where - impl = wcre @System $ axiStreamPacketFifo d8 d64 - - packetGen = - catMaybes - <$> genRandomAxiPacket - d4 - d0 - d0 - [NullByte, DataByte, PositionByte] - (Range.linear 0 32) - (pure ()) - - gen = L.concat <$> Gen.list (Range.linear 0 10) packetGen - -{- | Generate a 'axiStreamFromByteStream' component with variable output bus width -and test if a stream of multiple generated 'Packet's can be routed through it -without being changed. --} -prop_axi4StreamPacketFifo_Uninterrupted :: Property -prop_axi4StreamPacketFifo_Uninterrupted = property $ do - busWidth <- forAll $ Gen.integral $ Range.linear 1 8 - extraFifoDepth <- forAll $ Gen.integral $ Range.linear 2 64 - case ( TN.someNatVal $ fromIntegral busWidth - , TN.someNatVal $ fromIntegral extraFifoDepth - ) of - ( SomeNat (Proxy :: Proxy busWidth) - , SomeNat (Proxy :: Proxy extraFifoDepth) - ) -> do - let packetGen = - genRandomAxiPacket - (SNat @busWidth) - d0 - d0 - [NullByte, DataByte, PositionByte] - (Range.linear 0 (extraFifoDepth - 2)) - (pure ()) - inputData <- forAll (L.concat <$> Gen.list (Range.linear 0 10) packetGen) - let - conf = SimulationConfig 0 100 True - simOut = - withClockResetEnable @System clockGen noReset enableGen - $ sampleC conf - $ axiStreamPacketFifo d2 (SNat @(2 + extraFifoDepth)) - <| driveC conf inputData - - footnote $ "inputData: " <> show inputData - footnote $ "simOut: " <> show simOut - assert $ unInterruptedAxi4Packets simOut - -{- | Verify that the 'axiStreamFromByteStream' component does not change the content of the stream -when converting 1 byte wide transfers to 4 byte wide transfers. --} -prop_axiStreamFromByteStream :: Property -prop_axiStreamFromByteStream = propWithModel defExpectOptions gen model impl prop - where - impl = wcre @System $ axiUserMapC (const ()) <| axiStreamFromByteStream - model = L.concatMap (catMaybes . packetToAxiStream d4) . axiStreamToPackets - - packetGen = - catMaybes - <$> genRandomAxiPacket - d1 - d0 - d0 - [NullByte, DataByte, PositionByte] - (Range.linear 0 16) - (pure ()) - - gen = L.concat <$> Gen.list (Range.linear 0 3) packetGen - - prop expected sampled = do - let bytetypes = fmap getPacketByteTypes $ rights $ separatePackets sampled - footnote $ "expected: " <> show expected - footnote $ "sampled: " <> show sampled - footnote $ "bytetypes: " <> show bytetypes - - -- None of the packets start with null bytes - assert $ not (any hasLeadingNullBytes bytetypes) - - -- All packets are packed - assert $ all isPackedAxi4StreamPacket bytetypes - - -- The extracted packets are the same for both the expected and sampled data - axiStreamToPackets expected === axiStreamToPackets sampled - -prop_packAxi4Stream :: Property -prop_packAxi4Stream = property $ do - -- A transaction can only contain null bytes and be packed if _tlast is True - axiWithNulls <- - forAll $ genAxisM2S d8 d0 d0 [NullByte, DataByte, PositionByte] [True] $ pure () - axiWithoutNulls <- - forAll $ genAxisM2S d8 d0 d0 [DataByte, PositionByte] [True, False] $ pure () - let - resultWithNulls = packAxi4Stream axiWithNulls - resultWithoutNulls = packAxi4Stream axiWithoutNulls - footnote $ "resultWithNulls: " <> show resultWithNulls - footnote $ "resultWithoutNulls: " <> show resultWithoutNulls - assert (isPackedTransfer resultWithNulls) - assert (isPackedTransfer resultWithoutNulls) - -{- | Extract the data and strobe bytes. 'Nothing' if the corresponding keep bit -is low, 'Just' if the keep bit is high. --} -catKeepBytes :: - (KnownNat (DataWidth conf)) => - Axi4StreamM2S conf userType -> - Vec (DataWidth conf) (Maybe (Unsigned 8, Bool)) -catKeepBytes Axi4StreamM2S{..} = orNothing <$> _tkeep <*> zip _tdata _tstrb - -prop_axiOperations :: Property -prop_axiOperations = property $ do - axi <- - forAll $ genAxisM2S d4 d0 d0 [NullByte, DataByte, PositionByte] [True, False] $ pure () - let - keepBytesA = catMaybes $ toList $ catKeepBytes axi - keepBytesB = catMaybes $ toList $ catKeepBytes (packAxi4Stream axi) - splitConcatA = splitAxi4Stream @4 @4 (combineAxi4Stream @4 @4 (Just axi) Nothing) - splitConcatB = splitAxi4Stream @4 @4 (combineAxi4Stream @4 @4 Nothing (Just axi)) - keepBytesA === keepBytesB - -- Differentiate between empty and non-empty transfers - if all not (_tkeep axi) && not (_tlast axi) - then - ( do - (Nothing, Nothing) === splitConcatA - Nothing === uncurry (<|>) splitConcatA - Nothing === uncurry (flip (<|>)) splitConcatA - ) - else do - (Just axi, Nothing) === splitConcatA - Just axi === uncurry (<|>) splitConcatA - Just axi === uncurry (flip (<|>)) splitConcatA - -- TODO: Overhaul of `Axi4Stream` representation for correct `Eq` instance - assert (maybe False (eqAxi4Stream axi) (uncurry (flip (<|>)) splitConcatB)) - -prop_axiPacking :: Property -prop_axiPacking = propWithModel defExpectOptions gen model impl prop - where - impl = wcre @System axiPacking - model = id -- - packetGen = - catMaybes - <$> genRandomAxiPacket - d1 - d0 - d0 - [NullByte, DataByte, PositionByte] - (Range.linear 0 32) - (pure ()) - - gen = L.concat <$> Gen.list (Range.linear 0 3) packetGen - - prop expected sampled = do - let bytetypes = fmap getPacketByteTypes $ rights $ separatePackets sampled - footnote $ "expected: " <> show expected - footnote $ "sampled: " <> show sampled - footnote $ "bytetypes: " <> show bytetypes - - -- None of the packets start with null bytes - assert $ not (any hasLeadingNullBytes bytetypes) - - -- All packets are packed - assert $ all isPackedAxi4StreamPacket bytetypes - - -- The extracted packets are the same for both the expected and sampled data - axiStreamToPackets expected === axiStreamToPackets sampled - -prop_wbAxisRxBufferReadStreams :: Property -prop_wbAxisRxBufferReadStreams = property $ do - let packetGen = - Gen.filter (byteTypeFilter conditions . catMaybes) - $ genRandomAxiPacket - d4 - d0 - d0 - [NullByte, DataByte] - (Range.linear 0 16) - (pure ()) - inputData <- forAll (L.concat <$> Gen.list (Range.linear 0 3) packetGen) - extraBufferBytes <- forAll $ Gen.integral (Range.linear 31 31) - case TN.someNatVal extraBufferBytes of - SomeNat (Proxy :: Proxy extraBufferBytes) -> do - let transfers = - catMaybes - $ wcre - $ sampleC conf - $ tb (SNat @(1 + extraBufferBytes)) - <| driveC conf inputData - footnote $ "transfers: " <> show transfers - footnote $ "inputData: " <> show inputData - axiStreamToPackets (catMaybes inputData) === axiStreamToPackets transfers - where - conditions = - [ isPackedAxi4StreamPacket - , not . hasLeadingNullBytes - ] - conf = SimulationConfig 0 500 False - tb :: - (1 <= bufferBytes, HiddenClockResetEnable System) => - SNat bufferBytes -> - Circuit - (Axi4Stream System ('Axi4StreamConfig 4 0 0) ()) - (Axi4Stream System ('Axi4StreamConfig 4 0 0) ()) - tb bufferBytes = circuit $ \axiIn0 -> do - axiIn1 <- axiUserMapC (const False) -< axiIn0 - _status <- wbAxisRxBufferCircuit @System @30 bufferBytes -< (wb, axiIn1) - (wb, axiOut) <- rxReadMasterC bufferBytes -< () - idC -< axiOut - -prop_packetConversions :: Property -prop_packetConversions = property $ do - packets <- - forAll - $ Gen.list (Range.linear 1 4) - $ Gen.list (Range.linear 1 128) - $ genUnsigned Range.constantBounded - let - transfers = fmap (packetToAxiStream d4) packets - footnote $ "transfers:" <> show transfers - packets === axiStreamToPackets (L.concatMap catMaybes transfers) - -{- | Force all invalid bytes to zero. This is useful for a poor man's version -of '=='. --} -forceKeepLowZero :: Axi4StreamM2S conf userType -> Axi4StreamM2S conf userType -forceKeepLowZero a = a{_tdata = zipWith (\k d -> if k then d else 0) (_tkeep a) (_tdata a)} diff --git a/bittide/tests/Tests/Axi4/Generators.hs b/bittide/tests/Tests/Axi4/Generators.hs deleted file mode 100644 index baa25b625..000000000 --- a/bittide/tests/Tests/Axi4/Generators.hs +++ /dev/null @@ -1,141 +0,0 @@ --- SPDX-FileCopyrightText: 2023 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} - -module Tests.Axi4.Generators where - -import Clash.Prelude - -import Clash.Hedgehog.Sized.Unsigned -import Clash.Hedgehog.Sized.Vector (genVec) -import Data.Maybe -import Data.Proxy -import Hedgehog -import Protocols.Axi4.Stream -import Test.Tasty -import Test.Tasty.Hedgehog - -import Tests.Axi4.Properties -import Tests.Axi4.Types - -import qualified Data.List as L -import qualified GHC.TypeNats as TN -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Internal.Property as H -import qualified Hedgehog.Range as Range - -tests :: TestTree -tests = - testGroup - "Axi4Stream Generators" - [ testProperty "genAxisM2S" prop_genAxisM2S - , testProperty "genRandomAxiPacket" prop_genRandomAxiPacket - ] - --- | Generates a directed Axi4StreamM2S transaction. -genAxisM2S :: - -- | Data width of the Axi4StreamM2S transaction - SNat dataWidth -> - -- | ID width of the Axi4StreamM2S transaction - SNat idWidth -> - -- | Destination width of the Axi4StreamM2S transaction - SNat destWidth -> - -- | Allowed byte types to generate (can be used to skew the distribution of byte types) - [AxiByteType] -> - -- | Allowed _tlast values to generate (can be used to skew the distribution of _tlast values) - [Bool] -> - -- | Generator for user data - Gen userType -> - Gen (Axi4StreamM2S ('Axi4StreamConfig dataWidth idWidth destWidth) userType) -genAxisM2S SNat SNat SNat byteTypes lastValues genUser = do - bytes <- genVec $ Gen.choice $ fmap pure byteTypes - let (_tkeep, _tstrb) = unzip $ map getKeepStrobe bytes - _tdata <- genVec $ genUnsigned Range.constantBounded - _tlast <- Gen.choice $ fmap pure lastValues - _tid <- genUnsigned Range.constantBounded - _tdest <- genUnsigned Range.constantBounded - _tuser <- genUser - pure $ Axi4StreamM2S{..} - -prop_genAxisM2S :: Property -prop_genAxisM2S = property $ do - dataWidth <- forAll (TN.someNatVal . fromIntegral <$> Gen.int (Range.constant 1 4)) - case dataWidth of - SomeNat (Proxy :: Proxy dataWidth) -> do - let - allowedByteTypes = L.filter (not . null) $ L.subsequences [NullByte, DataByte, PositionByte] - allowedLasts = [[False], [True], [False, True]] - byteTypes <- forAll $ Gen.choice $ pure <$> allowedByteTypes - lastValues <- forAll $ Gen.choice $ fmap pure allowedLasts - axi <- forAll $ genAxisM2S (SNat @dataWidth) d8 d8 byteTypes lastValues (pure ()) - let axiBytes = getByteType <$> getTransferBytes axi - cover 40 "tlast" (_tlast axi) - cover 40 "not tlast" (not $ _tlast axi) - mapM_ - ( \byte -> - cover 25 (H.LabelName $ "One or more " <> show byte) (isJust $ elemIndex byte axiBytes) - ) - byteTypes - mapM_ - (\byte -> cover 1 (H.LabelName $ "All " <> show byte) (all (== byte) axiBytes)) - byteTypes - mapM_ - (\byte -> cover 1 (H.LabelName $ "No " <> show byte) (byte `notElem` axiBytes)) - byteTypes - assert (all (`L.elem` byteTypes) $ toList axiBytes) - assert (_tlast axi `L.elem` lastValues) - -data PacketDensity - = Sparse - | Dense - deriving (Show, Eq) - -{- | Generate a list of Axi4StreamM2S transactions that form a single packet, only the last transaction -will have _tlast set to True. The packet --} -genRandomAxiPacket :: - -- | Data width of the Axi4StreamM2S transaction - SNat dataWidth -> - -- | ID width of the Axi4StreamM2S transaction - SNat idWidth -> - -- | Destination width of the Axi4StreamM2S transaction - SNat destWidth -> - -- | Allowed byte types to generate (can be used to skew the distribution of byte types) - [AxiByteType] -> - -- | Range for the length of the packet, excluding the last transaction - Range Int -> - -- | Generator for user data - Gen userType -> - -- | Generator for a list of transactions representing a packet - Gen [Maybe (Axi4StreamM2S ('Axi4StreamConfig dataWidth idWidth destWidth) userType)] -genRandomAxiPacket SNat SNat SNat byteTypes range genUser = do - packetInit <- - Gen.list range (Gen.maybe $ genAxisM2S SNat SNat SNat byteTypes [False] genUser) - packetLast <- genAxisM2S SNat SNat SNat byteTypes [True] genUser - pure (L.tail $ packetInit <> [Just packetLast]) - -prop_genRandomAxiPacket :: Property -prop_genRandomAxiPacket = property $ do - dataWidth <- forAll (TN.someNatVal . fromIntegral <$> Gen.int (Range.constant 1 8)) - case dataWidth of - (SomeNat (Proxy :: Proxy dataWidth)) -> do - let - byteTypes = [NullByte, DataByte, PositionByte] - transfers <- - forAll - $ genRandomAxiPacket (SNat @dataWidth) d0 d0 byteTypes (Range.constant 1 16) (pure ()) - let - packet = catMaybes transfers - axiBytes = getPacketByteTypes packet - cover 1 "hasLeadingNullBytes" $ hasLeadingNullBytes axiBytes - cover 1 "hasTrailingNullBytes" $ hasTrailingNullBytes axiBytes - cover 1 "isPackedAxi4StreamPacket" $ isPackedAxi4StreamPacket axiBytes - cover 1 "isStrictlySparseAxi4StreamPacket" $ isStrictlySparseAxi4StreamPacket axiBytes - cover 1 "isContinuousAxi4StreamPacket" $ isContinuousAxi4StreamPacket axiBytes - cover 1 "isAlignedAxi4StreamPacket" $ isAlignedAxi4StreamPacket axiBytes - cover 1 "isUnalignedAxi4StreamPacket" $ isUnalignedAxi4StreamPacket axiBytes - cover 1 "unInterruptedAxi4Packets" $ unInterruptedAxi4Packets transfers - assert (isSinglePacket packet) diff --git a/bittide/tests/Tests/Axi4/Properties.hs b/bittide/tests/Tests/Axi4/Properties.hs deleted file mode 100644 index 6a7e30db0..000000000 --- a/bittide/tests/Tests/Axi4/Properties.hs +++ /dev/null @@ -1,237 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} - -module Tests.Axi4.Properties where - -import Clash.Prelude -import Protocols.Axi4.Stream - -import qualified Data.List as L -import Data.Maybe -import Test.Tasty -import qualified Test.Tasty.HUnit as HU -import Tests.Axi4.Types - -tests :: TestTree -tests = - testGroup - "Tests.Axi4.Properties" - [ HU.testCase "case_isPackedAxi4StreamPacket" case_isPackedAxi4StreamPacket - , HU.testCase "case_hasLeadingNullBytes" case_hasLeadingNullBytes - , HU.testCase "case_hasTrailingNullBytes" case_hasTrailingNullBytes - , HU.testCase "case_isSinglePacket" case_isSinglePacket - , HU.testCase "case_isStrictlySparseAxi4StreamPacket" case_isStrictlySparseAxi4StreamPacket - , HU.testCase "case_isUnalignedAxi4StreamPacket" case_isUnalignedAxi4StreamPacket - , HU.testCase "case_unInterruptedAxi4Packets" case_unInterruptedAxi4Packets - ] - --- | Apply filters to an Axi4StreamM2S packet -byteTypeFilter :: [[AxiByteType] -> Bool] -> [Axi4StreamM2S conf a] -> Bool -byteTypeFilter filters packet = all ($ getPacketByteTypes packet) filters - --- | A packet does not contain null bytes between the first and last data or position byte -isPackedAxi4StreamPacket :: [AxiByteType] -> Bool -isPackedAxi4StreamPacket = - all (== NullByte) - . dropWhile (/= NullByte) -- There should not be data or position bytes left - . dropWhile (== NullByte) -- Find first null byte -- Find first null byte - -- Find first data or position byte - -{- | A strictly sparse packet contains at least one position byte between the first -and last data byte --} -isStrictlySparseAxi4StreamPacket :: [AxiByteType] -> Bool -isStrictlySparseAxi4StreamPacket = - elem DataByte - . dropWhile (/= PositionByte) -- There should still be data bytes left - . dropWhile (/= DataByte) -- Find first position byte -- Find first position byte - -- Find first data byte - --- | Continuous packets do not contain null bytes between the first and last data byte -isContinuousAxi4StreamPacket :: [AxiByteType] -> Bool -isContinuousAxi4StreamPacket = notElem NullByte - --- | Aligned packets do not contain position bytes -isAlignedAxi4StreamPacket :: [AxiByteType] -> Bool -isAlignedAxi4StreamPacket = notElem PositionByte - -{- | Unaligned packets contain position bytes at the beginning and/or end of the packet, -but not between the first and last data byte. --} -isUnalignedAxi4StreamPacket :: [AxiByteType] -> Bool -isUnalignedAxi4StreamPacket bytes0 - | null bytes1 = False - | otherwise = - ((== PositionByte) (L.head bytes1) || (== PositionByte) (L.last bytes1)) - && not (isStrictlySparseAxi4StreamPacket bytes1) - where - bytes1 = filter (/= NullByte) bytes0 - -{- | Leading null bytes are null bytes that appear before the first data or position byte. -If a packet contains only null bytes, this returns @False@. --} -hasLeadingNullBytes :: [AxiByteType] -> Bool -hasLeadingNullBytes [] = False -hasLeadingNullBytes (x : xs) = x == NullByte && any (/= NullByte) xs - -{- | Trailing null bytes are null bytes at the end of a packet -If a packet contains only null bytes, this returns @False@. --} -hasTrailingNullBytes :: [AxiByteType] -> Bool -hasTrailingNullBytes [] = False -hasTrailingNullBytes xs = (L.last xs == NullByte) && any (/= NullByte) xs - -{- | Check if a list of Axi4StreamM2S transfers form an uninterrupted stream. -When a packet transmission is started, all elements should be Just until the -last transfer of the packet is reached. --} -unInterruptedAxi4Packets :: [Maybe (Axi4StreamM2S conf userType)] -> Bool -unInterruptedAxi4Packets xs = case break (maybe False _tlast) (dropWhile isNothing xs) of - (payload, l : rest) -> - all isJust (payload <> [l]) && unInterruptedAxi4Packets rest - (ys, []) -> all isJust ys - -{- | Check if a list of Axi4StreamM2S transfers form a single packet. -A list of `Axi4StreamM2S` transfers form a single packet if only the last transfer -has `_tlast` set. --} -isSinglePacket :: [Axi4StreamM2S conf userType] -> Bool -isSinglePacket axis = case break _tlast axis of - (_, [_]) -> True - _ -> False - -case_isPackedAxi4StreamPacket :: HU.Assertion -case_isPackedAxi4StreamPacket = do - HU.assertBool "expected packed" $ isPackedAxi4StreamPacket [] - HU.assertBool "expected packed" $ isPackedAxi4StreamPacket [DataByte] - HU.assertBool "expected packed" $ isPackedAxi4StreamPacket [DataByte, NullByte] - HU.assertBool "expected packed" $ isPackedAxi4StreamPacket [PositionByte, NullByte] - HU.assertBool "expected packed" $ isPackedAxi4StreamPacket [NullByte, DataByte] - HU.assertBool "expected packed" $ isPackedAxi4StreamPacket [DataByte, NullByte] - HU.assertBool "expected packed" $ isPackedAxi4StreamPacket [NullByte, NullByte, NullByte] - HU.assertBool "expected unpacked" - $ (not . isPackedAxi4StreamPacket) [DataByte, NullByte, DataByte] - HU.assertBool "expected unpacked" - $ (not . isPackedAxi4StreamPacket) [NullByte, PositionByte, NullByte, PositionByte] - HU.assertBool "expected unpacked" - $ (not . isPackedAxi4StreamPacket) [NullByte, DataByte, NullByte, PositionByte] - HU.assertBool "expected unpacked" - $ (not . isPackedAxi4StreamPacket) [DataByte, NullByte, PositionByte, NullByte] - -case_isStrictlySparseAxi4StreamPacket :: HU.Assertion -case_isStrictlySparseAxi4StreamPacket = do - HU.assertBool "expected not sparse" $ (not . isStrictlySparseAxi4StreamPacket) [] - HU.assertBool "expected not sparse" $ (not . isStrictlySparseAxi4StreamPacket) [DataByte] - HU.assertBool "expected not sparse" - $ (not . isStrictlySparseAxi4StreamPacket) [PositionByte] - HU.assertBool "expected not sparse" - $ (not . isStrictlySparseAxi4StreamPacket) [DataByte, NullByte] - HU.assertBool "expected not sparse" - $ (not . isStrictlySparseAxi4StreamPacket) [PositionByte, NullByte] - HU.assertBool "expected not sparse" - $ (not . isStrictlySparseAxi4StreamPacket) [PositionByte, DataByte] - HU.assertBool "expected not sparse" - $ (not . isStrictlySparseAxi4StreamPacket) [PositionByte, DataByte, PositionByte] - HU.assertBool "expected sparse" - $ isStrictlySparseAxi4StreamPacket [DataByte, PositionByte, DataByte, PositionByte] - HU.assertBool "expected sparse" - $ isStrictlySparseAxi4StreamPacket [DataByte, PositionByte, DataByte] - HU.assertBool "expected sparse" - $ isStrictlySparseAxi4StreamPacket [DataByte, PositionByte, DataByte, NullByte] - HU.assertBool "expected sparse" - $ isStrictlySparseAxi4StreamPacket [PositionByte, DataByte, PositionByte, DataByte] - -case_isUnalignedAxi4StreamPacket :: HU.Assertion -case_isUnalignedAxi4StreamPacket = do - HU.assertBool "expected unaligned" $ isUnalignedAxi4StreamPacket [PositionByte] - HU.assertBool "expected unaligned" $ isUnalignedAxi4StreamPacket [PositionByte, DataByte] - HU.assertBool "expected unaligned" $ isUnalignedAxi4StreamPacket [DataByte, PositionByte] - HU.assertBool "expected unaligned" - $ isUnalignedAxi4StreamPacket [PositionByte, DataByte, PositionByte] - HU.assertBool "expected unaligned" - $ isUnalignedAxi4StreamPacket [PositionByte, NullByte, DataByte, PositionByte] - HU.assertBool "expected unaligned" - $ isUnalignedAxi4StreamPacket [NullByte, DataByte, PositionByte] - HU.assertBool "expected unaligned" - $ isUnalignedAxi4StreamPacket [DataByte, PositionByte, NullByte] - HU.assertBool "expected not unaligned" $ not $ isUnalignedAxi4StreamPacket [] - HU.assertBool "expected not unaligned" $ not $ isUnalignedAxi4StreamPacket [DataByte] - HU.assertBool "expected not unaligned" - $ not - $ isUnalignedAxi4StreamPacket [DataByte, PositionByte, DataByte] - HU.assertBool "expected not unaligned" - $ not - $ isUnalignedAxi4StreamPacket [PositionByte, DataByte, PositionByte, DataByte] - HU.assertBool "expected not unaligned" - $ not - $ isUnalignedAxi4StreamPacket [DataByte, PositionByte, DataByte, PositionByte] - -case_unInterruptedAxi4Packets :: HU.Assertion -case_unInterruptedAxi4Packets = do - HU.assertBool "expected uninterrupted" $ unInterruptedAxi4Packets [Nothing, lastTransfer] - HU.assertBool "expected uninterrupted" - $ unInterruptedAxi4Packets [Nothing, lastTransfer, Nothing] - HU.assertBool "expected uninterrupted" - $ unInterruptedAxi4Packets [Nothing, payloadTransfer, lastTransfer] - HU.assertBool "expected uninterrupted" - $ unInterruptedAxi4Packets [payloadTransfer, payloadTransfer, lastTransfer] - HU.assertBool "expected uninterrupted" - $ unInterruptedAxi4Packets [lastTransfer, payloadTransfer, lastTransfer] - HU.assertBool "expected not uninterrupted" - $ unInterruptedAxi4Packets [lastTransfer, Nothing, payloadTransfer, lastTransfer] - HU.assertBool "expected not uninterrupted" - $ not - $ unInterruptedAxi4Packets [Nothing, payloadTransfer, Nothing, lastTransfer] - where - payloadTransfer = Just $ mkDummyM2S (repeat True) False - lastTransfer = Just $ mkDummyM2S (repeat True) True - -case_isSinglePacket :: HU.Assertion -case_isSinglePacket = do - HU.assertBool "expected single packet" $ isSinglePacket [lastTransfer] - HU.assertBool "expected single packet" $ isSinglePacket [payloadTransfer, lastTransfer] - HU.assertBool "expected not single packet" $ not $ isSinglePacket [] - HU.assertBool "expected not single packet" $ not $ isSinglePacket [payloadTransfer] - HU.assertBool "expected not single packet" - $ not - $ isSinglePacket [lastTransfer, payloadTransfer] - HU.assertBool "expected not single packet" - $ not - $ isSinglePacket [lastTransfer, payloadTransfer, lastTransfer] - where - payloadTransfer = mkDummyM2S (repeat True) False - lastTransfer = mkDummyM2S (repeat True) True - -case_hasLeadingNullBytes :: HU.Assertion -case_hasLeadingNullBytes = do - HU.assertBool "expected leading null bytes" $ hasLeadingNullBytes [NullByte, DataByte] - HU.assertBool "expected not leading null bytes" $ not $ hasLeadingNullBytes [] - HU.assertBool "expected not leading null bytes" $ not $ hasLeadingNullBytes [DataByte] - HU.assertBool "expected not leading null bytes" - $ not - $ hasLeadingNullBytes [DataByte, NullByte] - HU.assertBool "expected not leading null bytes" $ not $ hasLeadingNullBytes [NullByte] - -case_hasTrailingNullBytes :: HU.Assertion -case_hasTrailingNullBytes = do - HU.assertBool "expected trailing null bytes" $ hasTrailingNullBytes [DataByte, NullByte] - HU.assertBool "expected not trailing null bytes" $ not $ hasTrailingNullBytes [] - HU.assertBool "expected not trailing null bytes" $ not $ hasTrailingNullBytes [DataByte] - HU.assertBool "expected not trailing null bytes" $ not $ hasTrailingNullBytes [NullByte] - HU.assertBool "expected not trailing null bytes" - $ not - $ hasTrailingNullBytes [NullByte, DataByte] - -mkDummyM2S :: Vec 4 Bool -> Bool -> Axi4StreamM2S ('Axi4StreamConfig 4 0 0) () -mkDummyM2S keep last0 = - Axi4StreamM2S - { _tdata = repeat 0 - , _tkeep = keep - , _tstrb = repeat True - , _tlast = last0 - , _tid = 0 - , _tdest = 0 - , _tuser = () - } diff --git a/bittide/tests/Tests/Axi4/Types.hs b/bittide/tests/Tests/Axi4/Types.hs deleted file mode 100644 index 27b4997cb..000000000 --- a/bittide/tests/Tests/Axi4/Types.hs +++ /dev/null @@ -1,119 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE RecordWildCards #-} - -module Tests.Axi4.Types where - -import Clash.Prelude - -import Bittide.Extra.Maybe -import Clash.Sized.Vector (unsafeFromList) -import Data.Maybe -import Protocols.Axi4.Stream - -import qualified Data.List as L - -type Packet = [Unsigned 8] - --- | Bytes in Axi4StreamM2S transfer -data AxiByte - = Null - | Data (Unsigned 8) - | Position (Unsigned 8) - deriving (Generic, NFDataX, Show, Eq) - --- | Different types of bytes in Axi4StreamM2S transfer -data AxiByteType - = NullByte - | DataByte - | PositionByte - deriving (Generic, NFDataX, Show, Eq) - -type Keep = Bool -type Strobe = Bool - --- | Get a list of bytes for a given Axi4StreamM2S transaction -getTransferBytes :: Axi4StreamM2S conf a -> Vec (DataWidth conf) AxiByte -getTransferBytes Axi4StreamM2S{..} = zipWith3 getByte _tkeep _tstrb _tdata - --- | Get a byte based on a keep, strobe and data value -getByte :: Keep -> Strobe -> Unsigned 8 -> AxiByte -getByte True True d = Data d -getByte True False p = Position p -getByte False False _ = Null -getByte False True _ = deepErrorX "Reserved byte" - -{- | Get a list of byte types from an Axi4StreamM2S packet. -Input must satisfy `isSinglePacket` --} -getPacketByteTypes :: [Axi4StreamM2S conf a] -> [AxiByteType] -getPacketByteTypes = L.concatMap (toList . fmap getByteType . getTransferBytes) - --- | Get the byte type based on a keep and strobe value -getByteType :: AxiByte -> AxiByteType -getByteType (Data _) = DataByte -getByteType (Position _) = PositionByte -getByteType Null = NullByte - --- | Get the keep and strobe values based on a byte type -getKeepStrobe :: AxiByteType -> (Keep, Strobe) -getKeepStrobe NullByte = (False, False) -getKeepStrobe DataByte = (True, True) -getKeepStrobe PositionByte = (True, False) - --- | Transform a list of Axi Stream operations into a 'Packet'. -axiStreamToPackets :: - (KnownNat nBytes) => - [Axi4StreamM2S ('Axi4StreamConfig nBytes 0 0) ()] -> - [Packet] -axiStreamToPackets = L.reverse . snd . L.foldl go ([], []) - where - go (partialPacket, packets) Axi4StreamM2S{..} - | _tlast = ([], L.reverse newPartial : packets) - | otherwise = (newPartial, packets) - where - newPartial = L.reverse (catMaybes (toList $ orNothing <$> _tkeep <*> _tdata)) <> partialPacket - --- Transform a 'Packet' into a list of Axi Stream operations. -packetToAxiStream :: - forall nBytes. - SNat nBytes -> - Packet -> - [Maybe (Axi4StreamM2S ('Axi4StreamConfig nBytes 0 0) ())] -packetToAxiStream w@SNat !bs - | rest /= [] = Just axis : packetToAxiStream w rest - | otherwise = [Just axis] - where - busWidth = natToNum @nBytes - (firstWords, rest) = L.splitAt busWidth bs - word = L.take busWidth (firstWords <> L.repeat 0) - axis = - Axi4StreamM2S - { _tdata = unsafeFromList word - , _tkeep = keeps - , _tstrb = repeat True - , _tlast = null rest - , _tid = 0 - , _tdest = 0 - , _tuser = deepErrorX "" - } - keeps = - unsafeFromList - $ L.replicate (L.length bs) True - <> L.repeat False - --- | Separate a list of Axi4Stream operations into a list of Axi4Stream packets. -separatePackets :: - forall nBytes. - (KnownNat nBytes) => - [Axi4StreamM2S ('Axi4StreamConfig nBytes 0 0) ()] -> - [ Either - [Axi4StreamM2S ('Axi4StreamConfig nBytes 0 0) ()] - [Axi4StreamM2S ('Axi4StreamConfig nBytes 0 0) ()] - ] -separatePackets [] = [] -separatePackets axis = case L.break _tlast axis of - (payload, l : rest) -> Right (payload <> [l]) : separatePackets rest - (remainder, _) -> [Left remainder] diff --git a/bittide/tests/Tests/Calendar.hs b/bittide/tests/Tests/Calendar.hs deleted file mode 100644 index 3347a4e84..000000000 --- a/bittide/tests/Tests/Calendar.hs +++ /dev/null @@ -1,553 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} -{-# OPTIONS_GHC -Wno-orphans #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} - -module Tests.Calendar (tests, genCalendarConfig, genValidEntry) where - -import Clash.Prelude - -import Clash.Hedgehog.Sized.Index (genIndex) -import Clash.Hedgehog.Sized.Unsigned -import Clash.Hedgehog.Sized.Vector -import Clash.Sized.Vector (unsafeFromList) -import Data.Proxy -import Data.String -import Data.Type.Equality ((:~:) (Refl)) -import Hedgehog -import Hedgehog.Gen as Gen -import Hedgehog.Range as Range -import Protocols.Wishbone -import Test.Tasty -import Test.Tasty.Hedgehog - -import Bittide.Calendar -import Bittide.SharedTypes -import Tests.Shared - -import qualified Clash.Sized.Vector as V -import qualified Clash.Util.Interpolate as I -import qualified Data.Set as Set -import qualified GHC.TypeNats as TN -import qualified Prelude as P - -tests :: TestTree -tests = - testGroup - "Tests.Calendar" - [ testPropertyNamed "Reading the calendar." "readCalendar" readCalendar - , testPropertyNamed "Writing and reading new calendars" "reconfigCalendar" reconfigCalendar - , testPropertyNamed - "Reading shadow buffer with wishbone" - "readShadowCalendar" - readShadowCalendar - , testPropertyNamed "Metacycle signal generation" "metaCycleIndication" metaCycleIndication - ] - -{- | A vector with a minimum size of 1 elements containing Bitvectors of arbitrary size. -This data type enables us to generate differently sized calendars that satisfy the constraints -imposed by the calendar component. --} -data BVCalendar addrW where - BVCalendar :: - ( KnownNat addrW - , KnownNat n - , 2 <= n - , KnownNat bits - , 1 <= bits - , KnownNat validityBits - ) => - -- | Amount of entries in the BitVector calendar - SNat n -> - -- | Amount of bits per BitVector in the calendar. - SNat bits -> - -- | Number of bits for entry validity. - SNat validityBits -> - -- | Vector of (n+1) entries containing BitVectors of size bits. - Vec n (ValidEntry (BitVector bits) validityBits) -> - BVCalendar addrW - -instance Show (BVCalendar addrW) where - show (BVCalendar _ _ _ bvvec) = show bvvec - -{- | Generates a configuration for 'Bittide.Calendar.calendar', with as first argument -the maximum depth of the stored calendar and as second argument a generator for the -calendar entries. --} -genCalendarConfig :: - forall nBytes addrW a validityBits. - ( KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - , KnownNat (BitSize a) - , KnownNat validityBits - , BitPack a - , NFDataX a - , Show a - , ShowX a - ) => - -- | Maximum amount of entries a calendar based on the returned configuration can hold per calendar. - Natural -> - -- | Generator for the entries in the shadow calendar and active calendar. - Gen (ValidEntry a validityBits) -> - Gen (CalendarConfig nBytes addrW a) -genCalendarConfig ms elemGen = do - dA <- Gen.enum 1 ms - dB <- Gen.enum 1 ms - case (TN.someNatVal (ms - 2), TN.someNatVal dA, TN.someNatVal dB) of - ( SomeNat (addSNat d2 . snatProxy -> maxSize) - , SomeNat (snatProxy -> depthA) - , SomeNat (snatProxy -> depthB) - ) -> do - let - regAddrBits = - SNat - @(NatRequiredBits (Regs (ValidEntry a validityBits) (nBytes * 8) + ExtraRegs)) - bsCalEntry = SNat @(BitSize a) - case ( isInBounds d1 depthA maxSize - , isInBounds d1 depthB maxSize - , compareSNat regAddrBits (SNat @addrW) - , compareSNat d1 bsCalEntry - ) of - (InBounds, InBounds, SNatLE, SNatLE) -> go maxSize depthA depthB - (a, b, c, d) -> - error - [I.i| - genCalendarConfig: calEntry constraints not satisfied: - - a: #{a} - b: #{b} - c: #{c} - d: #{d} - - ... - |] - where - go :: - forall maxDepth depthA depthB. - ( LessThan depthA maxDepth - , LessThan depthB maxDepth - , 1 <= depthA - , 1 <= depthB - , 2 <= maxDepth - , NatFitsInBits (Regs (ValidEntry a validityBits) (nBytes * 8) + ExtraRegs) addrW - ) => - SNat maxDepth -> - SNat depthA -> - SNat depthB -> - Gen (CalendarConfig nBytes addrW a) - go dMax SNat SNat = do - calActive <- genVec @_ @depthA elemGen - calShadow <- genVec @_ @depthB elemGen - return $ CalendarConfig dMax calActive calShadow - -genValidEntry :: SNat repetitionBits -> Gen a -> Gen (ValidEntry a repetitionBits) -genValidEntry SNat genA = - (\veEntry veRepeat -> ValidEntry{veEntry, veRepeat}) - <$> genA - <*> genUnsigned Range.linearBounded - --- | Generates a 'BVCalendar' of a certain size and width for the stored BitVectors. -genBVCalendar :: Integer -> Integer -> Integer -> Gen (BVCalendar 32) -genBVCalendar calSize bitWidth validityBits = do - let - calNat = TN.someNatVal (fromIntegral $ calSize - 2) - bitNat = TN.someNatVal (fromIntegral bitWidth) - valNat = TN.someNatVal (fromIntegral validityBits) - case (calNat, bitNat, valNat) of - (SomeNat size, SomeNat bits, SomeNat validity) -> - go (addSNat d2 $ snatProxy size) (snatProxy bits) (snatProxy validity) - where - go :: - forall calSize bitWidth validityBits. - (KnownNat calSize, 2 <= calSize, KnownNat bitWidth) => - SNat calSize -> - SNat bitWidth -> - SNat validityBits -> - Gen (BVCalendar 32) - go s b v@SNat = do - let - calNatBits = clogBaseSNat d2 s - case ( compareSNat calNatBits d32 - , compareSNat d1 calNatBits - , compareSNat d1 b - ) of - (SNatLE, SNatLE, SNatLE) -> do - cal <- - Gen.list (Range.singleton $ fromIntegral calSize) - $ genValidEntry - (SNat @validityBits) - (genDefinedBitVector @bitWidth) - return (BVCalendar s b v $ unsafeFromList cal) - _ -> - error - $ "genIntCalendar: Constraints not satisfied: 1 <= " - <> show calNatBits - <> " <= 32." - --- | This test checks if we can read the initialized calendars. -readCalendar :: Property -readCalendar = property $ do - calSize <- forAll $ Gen.int $ Range.constant 2 31 - bitWidth <- forAll $ Gen.enum 1 100 - validityBits <- forAll $ Gen.enum 0 2 - bvCal <- forAll $ genBVCalendar (fromIntegral calSize) bitWidth validityBits - case bvCal of - BVCalendar calSize' SNat SNat cal -> do - let - -- 1 to compensate for reset, length for 1 cycle per element, sum of snds for - -- additional validity delays. - simLength = 1 + length cal + sum (fmap (fromIntegral . veRepeat) cal) - topEntity = - (\(a, _, _) -> a) - $ withClockResetEnable - clockGen - resetGen - enableGen - calendarWbSpecVal - calSize' - cal - cal - $ pure (emptyWishboneM2S @32 @(BitVector 32)) - simOut = sampleN @System (fromIntegral simLength) topEntity - expected = toList $ fmap veEntry cal - footnote . fromString $ "simOut: " <> show simOut - footnote . fromString $ "expected: " <> show expected - - Set.fromList simOut === Set.fromList expected - -{- | This test checks if we can write to the shadowbuffer and read back the written -elements later. --} -reconfigCalendar :: Property -reconfigCalendar = property $ do - calSize <- forAll $ Gen.int $ Range.constant 2 32 - bitWidth <- forAll $ Gen.enum 1 100 - validityBits <- forAll $ Gen.enum 0 2 - bvCal <- forAll $ genBVCalendar (fromIntegral calSize) bitWidth validityBits - case bvCal of - BVCalendar calSize' _ (SNat :: SNat validityBits) cal -> do - newEntries <- - forAll - . Gen.list (Range.singleton calSize) - $ genValidEntry (SNat @validityBits) genDefinedBitVector - let - (entries0, delays0) = unzip $ fmap (\e -> (veEntry e, veRepeat e)) cal - (entries1, delays1) = P.unzip $ fmap (\e -> (veEntry e, veRepeat e)) newEntries - cal0Duration = calSize + sum (fmap fromIntegral delays0) - cal1Duration = calSize + sum (fmap fromIntegral delays1) - writeOps = P.zip (cycle [0 .. indexOf calSize']) newEntries - swapCall = let a = (bitWidth + validityBits) `divRU` 32 + 3 in wbWriteOp (a, 0) - wbWrites = wbNothingM2S @4 @32 : P.concatMap writeWithWishbone writeOps <> [swapCall] - -- Arming has one cycle delay, - writeDuration = 1 + P.length wbWrites - -- It may take multiple metacycles to write the new calendar. - simLength = cal0Duration * (writeDuration `divRU` cal0Duration) + cal1Duration - topEntity writePort = - (\(a, _, _) -> a) - $ withClockResetEnable - clockGen - resetGen - enableGen - calendar - calSize' - cal - cal - writePort - topEntityInput = P.take simLength $ wbWrites <> P.repeat wbNothingM2S - simOut = simulateN @System simLength topEntity topEntityInput - expected = P.take simLength $ toList entries0 <> entries1 - footnote . fromString $ "simOut: " <> show simOut - footnote . fromString $ "expected: " <> show expected - footnote . fromString $ "Write operations: " <> show wbWrites - footnote . fromString $ "Write operations: " <> show writeOps - Set.fromList simOut === Set.fromList expected - -{- | This test checks if we can write to the shadowbuffer and read back the written -elements later. --} -readShadowCalendar :: Property -readShadowCalendar = property $ do - calSize <- forAll $ Gen.enum 2 32 - bitWidth <- forAll $ Gen.enum 1 100 - validityBits <- forAll $ Gen.enum 0 2 - calA <- forAll $ genBVCalendar calSize bitWidth validityBits - calS <- forAll $ genBVCalendar calSize bitWidth validityBits - case (calA, calS) of - (BVCalendar snatA bwA valA calA', BVCalendar snatS bwS valS calS') -> - case ( sameNat (asProxy snatA) (asProxy snatS) - , sameNat (asProxy bwA) (asProxy bwS) - , sameNat (asProxy valA) (asProxy valS) - ) of - (Just Refl, Just Refl, Just Refl) -> do - let - entryRegs = snatToInteger $ requiredRegs (bwS `addSNat` valS) d32 - readAddresses = fmap fromIntegral [0 .. indexOf snatS] - simLength = P.length wbReads + 1 - wbReads = P.concatMap (\i -> wbReadEntry @4 @30 i entryRegs) readAddresses - topEntity writePort = - (\(_, _, wb) -> wb) - $ withClockResetEnable - clockGen - resetGen - enableGen - calendar - (addSNat d2 snatS) - calA' - calS' - writePort - topEntityInput = P.take simLength $ wbReads <> P.repeat wbNothingM2S - simOut = simulateN @System simLength topEntity topEntityInput - wbOutEntries = directedWbDecoding topEntityInput simOut - wbOutEntries === toList calS' - _ -> error "readShadowCalendar: Calendar sizes or bitwidths do not match." - -{- | This test checks if the metacycle signal (which indicates that the last entry of the -active calendar is present at the output), is correctly being generated. --} -metaCycleIndication :: Property -metaCycleIndication = property $ do - calSize <- forAll $ Gen.enum 3 4 - bitWidth <- forAll $ Gen.enum 1 100 - validityBits <- forAll $ Gen.enum 0 1 - bvCal <- forAll $ genBVCalendar calSize bitWidth validityBits - metaCycles <- forAll $ Gen.int $ Range.constant 2 5 - case bvCal of - BVCalendar (calSize' :: SNat calDepth) _ _ cal -> do - let - genDepth = - fromIntegral - <$> genIndex @_ @calDepth - (Range.constant 2 (fromIntegral $ pred calSize)) - newDepths <- forAll $ Gen.list (Range.singleton (metaCycles - 1)) genDepth - let - reqRegs = (bitWidth + validityBits) `divRU` 32 - newDepthAddr = reqRegs + 2 - swapAddr = reqRegs + 3 - allDepths = (fromIntegral calSize - 1) : newDepths - delayPerDepth = tail $ scanl (\a b -> a + fromIntegral (veRepeat b)) 0 cal - allDurations = fmap (\d -> d + delayPerDepth !! d) allDepths - simLength <- forAll $ fromIntegral <$> Gen.enum 0 (sum allDurations + sum newDepths) - let - swapCall = wbWriteOp @4 @32 (swapAddr, 0) - wbWrites = P.drop 3 $ P.concatMap writeAndSwitch (P.zip allDepths allDurations) - writeAndSwitch (dep, dur) = - P.take (succ dur) - $ [wbWriteOp (newDepthAddr, fromIntegral dep), swapCall] - <> P.repeat wbNothingM2S - topEntity writePort = - (\(_, m, _) -> m) - $ withClockResetEnable - clockGen - resetGen - enableGen - calendarWbSpecVal - calSize' - cal - cal - writePort - topEntityInput = wbWrites <> P.repeat wbNothingM2S - simOut = simulateN @System simLength topEntity topEntityInput - expectedOut = - P.take simLength - $ P.concatMap - (\(fromIntegral -> n) -> P.replicate n False <> [True]) - (repeatLast allDurations) - footnote . fromString $ "Simulation: " <> show simOut - footnote . fromString $ "Expected: " <> show expectedOut - footnote . fromString $ "All Depths: " <> show allDurations - footnote . fromString $ "wishbone in: " <> show (P.take simLength wbWrites) - simOut === expectedOut - -repeatLast :: [a] -> [a] -repeatLast [] = [] -repeatLast [l] = P.repeat l -repeatLast (l : ist) = l : repeatLast ist - --- | Gets the index of element (n+1) -indexOf :: forall n. (KnownNat n, 1 <= n) => SNat n -> Index n -indexOf = leToPlus @1 @n (fromSNat . predSNat) - --- | Interpret SNat as Proxy for use by 'sameNat'. -asProxy :: SNat n -> Proxy n -asProxy SNat = Proxy - --- | Get the amount of required registers for storing a BitVector bits in registers of regSize. -requiredRegs :: - (1 <= regSize) => - SNat bits -> - SNat regSize -> - SNat (Regs (BitVector bits) regSize) -requiredRegs SNat SNat = SNat - --- | idle 'Protocols.Wishbone.WishboneM2S' bus. -wbNothingM2S :: - forall nBytes addrW. - (KnownNat nBytes, KnownNat addrW) => - WishboneM2S addrW nBytes (Bytes nBytes) -wbNothingM2S = - (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = 0 - , writeData = 0 - , busSelect = 0 - } - -{- | Write an entry to some address in 'Bittide.Calendar.calendar', this may require -multiple write operations. --} -writeWithWishbone :: - forall nBytes addrW n entry. - (KnownNat nBytes, 1 <= nBytes, KnownNat addrW, KnownNat n, Paddable entry) => - (Index n, entry) -> - [WishboneM2S addrW nBytes (Bytes nBytes)] -writeWithWishbone (a, entry) = - case getRegsLe entry of - RegisterBank vec -> toList $ fmap wbWriteOp $ zip indicesI (vec :< fromIntegral a) - -{- | Use both the wishbone M2S bus and S2M bus to decode the S2M bus operations into the -expected type a. --} -directedWbDecoding :: - forall nBytes addrW a. - ( KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - , Paddable a - ) => - [WishboneM2S addrW nBytes (Bytes nBytes)] -> - [WishboneS2M (Bytes nBytes)] -> - [a] -directedWbDecoding (wbM2S : m2sRest) (_ : s2mRest) = out - where - active = strobe wbM2S && busCycle wbM2S - foundBeginning = writeEnable wbM2S && active - - expectReadData :: - ( WishboneM2S addrW nBytes (Bytes nBytes) - , WishboneS2M (Bytes nBytes) - ) -> - Bool - expectReadData (WishboneM2S{strobe, busCycle, writeEnable}, _) = - strobe && busCycle && not writeEnable - - entryList = - fmap (readData . snd) - $ takeWhile expectReadData - . filterNoOps - $ P.zip m2sRest s2mRest - - filterNoOps l = [(m2s, s2m) | (m2s, s2m) <- l, m2s /= wbNothingM2S] - entry = case V.fromList $ P.reverse entryList of - Just (vec :: Vec (Regs a (nBytes * 8)) (Bytes nBytes)) -> getDataLe (RegisterBank vec) - Nothing -> - error - $ "directedWbDecoding: list to vector conversion failed: " - <> show entryList - <> "from " - <> show (wbM2S : m2sRest) - - consumedReads = P.length entryList - remainingM2S = P.drop consumedReads m2sRest - remainingS2M = P.drop consumedReads s2mRest - - out - | foundBeginning = entry : directedWbDecoding remainingM2S remainingS2M - | otherwise = directedWbDecoding m2sRest s2mRest -directedWbDecoding _ _ = [] - -{- | Returns the wishbone M2S bus inputs required to read a calendar entry from -'Bittide.Calendar.calendar'. It first writes the entry's address to the read register, -then adds the read operations. --} -wbReadEntry :: - forall nBytes addrW i. - (KnownNat nBytes, KnownNat addrW, Integral i) => - i -> - i -> - [WishboneM2S addrW nBytes (Bytes nBytes)] -wbReadEntry i dataRegs = addrWrite : wbNothingM2S : dataReads - where - addrWrite = - (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = fromIntegral (dataRegs + 1) - , writeData = fromIntegral i - , busSelect = maxBound - , busCycle = True - , strobe = True - , writeEnable = True - } - dataReads = readReg <$> P.reverse [0 .. (dataRegs - 1)] - readReg n = - (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = fromIntegral n - , writeData = 0 - , busSelect = maxBound - , busCycle = True - , strobe = True - , writeEnable = False - } - -{- | Transform a target address i and a bitvector to a Wishbone write operation that writes -the bitvector to address i. --} -wbWriteOp :: - forall nBytes addrW i. - (KnownNat nBytes, KnownNat addrW, Integral i) => - (i, Bytes nBytes) -> - WishboneM2S addrW nBytes (Bytes nBytes) -wbWriteOp (i, bv) = - (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = fromIntegral i - , writeData = bv - , busSelect = maxBound - , busCycle = True - , strobe = True - , writeEnable = True - } - --- | Version of 'Bittide.Calendar.calendar' which performs Wishbone spec validation -calendarWbSpecVal :: - forall dom nBytes addrW maxCalDepth a validityBits bootstrapSizeA bootstrapSizeB. - ( HiddenClockResetEnable dom - , KnownNat addrW - , KnownNat bootstrapSizeA - , 1 <= bootstrapSizeA - , KnownNat bootstrapSizeB - , 1 <= bootstrapSizeB - , KnownNat nBytes - , 1 <= nBytes - , KnownNat validityBits - , 2 <= maxCalDepth - , LessThan bootstrapSizeA maxCalDepth - , LessThan bootstrapSizeB maxCalDepth - , Paddable a - , ShowX a - , Show a - ) => - -- | The maximum amount of entries that can be stored in the individual calendars. - SNat maxCalDepth -> - -- | Bootstrap calendar for the active buffer. - Calendar bootstrapSizeA a validityBits -> - -- | Bootstrap calendar for the shadow buffer. - Calendar bootstrapSizeB a validityBits -> - -- | Incoming wishbone interface - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | Currently active entry, Metacycle indicator and outgoing wishbone interface. - (Signal dom a, Signal dom Bool, Signal dom (WishboneS2M (Bytes nBytes))) -calendarWbSpecVal mDepth bootstrapActive bootstrapShadow m2s0 = - (active, metaIndicator, s2m1) - where - (active, metaIndicator, s2m0) = - calendar @dom @nBytes @addrW - mDepth - bootstrapActive - bootstrapShadow - m2s1 - (m2s1, s2m1) = validateWb m2s0 s2m0 diff --git a/bittide/tests/Tests/ClockControl/Si539xSpi.hs b/bittide/tests/Tests/ClockControl/Si539xSpi.hs deleted file mode 100644 index 42dddaa94..000000000 --- a/bittide/tests/Tests/ClockControl/Si539xSpi.hs +++ /dev/null @@ -1,84 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} -{-# OPTIONS_GHC -Wno-orphans #-} - -module Tests.ClockControl.Si539xSpi where - -import Clash.Cores.SPI -import Clash.Prelude -import Clash.Signal.Internal (Signal ((:-))) - -import Bittide.ClockControl.Si5391A -import Bittide.ClockControl.Si539xSpi -import Bittide.SharedTypes - -import qualified Data.Map as Map -import Test.Tasty -import Test.Tasty.HUnit - -createDomain vXilinxSystem{vPeriod = hzToPeriod 1e6, vName = "Basic1"} - -tests :: TestTree -tests = - testGroup - "Tests.ClockControl.Si539xSpi" - [testCase "Configuration succeeds" configureSucceeds] - -topEntity :: Signal Basic1 (Bool, Bool) -topEntity = bundle (masterBusy, configState .==. pure Finished) - where - (_, masterBusy, configState, (sclk, mosi, ss)) = - withClockResetEnable clockGen resetGen enableGen - $ si539xSpi testConfigA (SNat @50000) (pure Nothing) miso - miso = si5391Mock sclk mosi ss - -si5391Mock :: - forall dom. - (KnownDomain dom) => - Signal dom Bool -> - Signal dom Bit -> - Signal dom Bool -> - Signal dom Bit -si5391Mock sck mosi ss = readFromBiSignal miso - where - slaveOut :: Signal dom (Maybe (Bytes 2)) - (veryUnsafeToBiSignalIn -> miso, _, slaveOut) = - withClockResetEnable clockGen resetGen enableGen - $ spiSlaveLatticeSBIO SPIMode0 False sck mosi miso ss slaveIn - - slaveIn = - si5391Model - (deepErrorX "", deepErrorX "", Map.fromList [(0x00FE, 0xF), (0x00C0, 0x00)]) - slaveOut - - si5391Model :: - (Page, Address, Map.Map (Bytes 2) Byte) -> - Signal dom (Maybe (Bytes 2)) -> - Signal dom (Bytes 2) - si5391Model oldState@(page, addr, regs) (maybeInput :- inputs) = - case maybeInput of - Nothing -> output :- si5391Model oldState inputs - Just input -> output :- si5391Model newState inputs - where - (command, byte) = split input - newState = case (shiftR command 5, addr) of - (0, _) -> (page, byte, regs) - (2, 1) -> (byte, addr, regs) - (2, _) -> (page, addr, Map.insert key byte regs) - (4, _) -> (page, addr, regs) - (3, _) -> (page, succ addr, Map.insert key byte regs) - (5, _) -> (page, succ addr, regs) - _ -> newState - where - key = pack (page, addr) - output = maybe undefinedValue resize (Map.lookup key regs) - undefinedValue = - deepErrorX $ "si5391Model: Tried reading uninitialized value at " <> show (page, addr) - -configureSucceeds :: Assertion -configureSucceeds = - assertBool "Eventually the configuration indicates becomes true" (or $ fmap snd simOut) - where - simOut = takeWhile fst $ sample topEntity diff --git a/bittide/tests/Tests/Counter.hs b/bittide/tests/Tests/Counter.hs deleted file mode 100644 index 7aa2e1b52..000000000 --- a/bittide/tests/Tests/Counter.hs +++ /dev/null @@ -1,80 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE TemplateHaskell #-} -{-# OPTIONS_GHC -Wno-orphans #-} - -module Tests.Counter where - -import Clash.Explicit.Prelude -import qualified Prelude as P - -import Control.Monad (forM_) - -import Test.Tasty -import Test.Tasty.HUnit -import Test.Tasty.TH - -import Bittide.Counter (domainDiffCounter) - -createDomain vXilinxSystem{vName = "D10", vPeriod = hzToPeriod 100e6} -createDomain vXilinxSystem{vName = "D17", vPeriod = hzToPeriod 170e6} -createDomain vXilinxSystem{vName = "D20", vPeriod = hzToPeriod 200e6} - -noRst :: (KnownDomain dom) => Reset dom -noRst = unsafeFromActiveHigh (pure False) - -rst :: (KnownDomain dom) => Reset dom -rst = unsafeFromActiveHigh (pure True) - -rstN :: (KnownDomain dom) => Int -> Reset dom -rstN n = unsafeFromActiveHigh (fromList (P.replicate n True <> P.repeat False)) - -top :: - forall src dst. - ( KnownDomain src - , KnownDomain dst - ) => - Reset src -> - Reset dst -> - Signal dst (Signed 32) -top rstSrc rstDst = fst <$> domainDiffCounter clockGen rstSrc clockGen rstDst - --- | 'domainDiffCounter' should continuously emit zeros when applied to the same domain -case_zeroSameDomain :: Assertion -case_zeroSameDomain = sampleN 1000 (top @D10 @D10 noRst noRst) @?= P.replicate 1000 0 - --- | 'domainDiffCounter' should continuously emit zeros when src reset is kept asserted -case_zeroSrcRst :: Assertion -case_zeroSrcRst = sampleN 1000 (top @D10 @D17 rst noRst) @?= P.replicate 1000 0 - --- | 'domainDiffCounter' should continuously emit zeros when dst reset is kept asserted -case_zeroDstRst :: Assertion -case_zeroDstRst = sampleN 1000 (top @D10 @D17 noRst rst) @?= P.replicate 1000 0 - --- | No matter when we release the destination reset, we should zeros followed by counting -case_glitchless :: Assertion -case_glitchless = - -- - forM_ [0 .. 512] $ \n -> do - let - sampled = sampleN 1000 (dut (rstN n)) - sampledNonZero = P.dropWhile (== 0) sampled - len = P.length sampledNonZero - assertBool (">1 @ " <> show n) (len > 1) - assertEqual ("exp @ " <> show n) sampledNonZero (P.take len expected) - where - dut = top @D10 @D20 noRst - expected = P.concat [[n, n] | n <- [1 ..]] - -tests :: TestTree -tests = $(testGroupGenerator) - --- Run with: --- --- ghcid -c cabal repl bittide:unittests -T Tests.Counter.main --- --- Add -W if you want to run tests in spite of warnings --- -main :: IO () -main = defaultMain tests diff --git a/bittide/tests/Tests/DoubleBufferedRam.hs b/bittide/tests/Tests/DoubleBufferedRam.hs deleted file mode 100644 index 4f7004010..000000000 --- a/bittide/tests/Tests/DoubleBufferedRam.hs +++ /dev/null @@ -1,1085 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} -{-# OPTIONS_GHC -Wno-orphans #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} - -module Tests.DoubleBufferedRam (tests) where - -import Clash.Prelude - -import Clash.Hedgehog.Sized.Index -import Clash.Hedgehog.Sized.Unsigned -import Clash.Hedgehog.Sized.Vector -import Data.Constraint.Nat.Extra -import Data.Maybe -import Data.Proxy -import Data.String -import Data.Type.Equality (type (:~:) (Refl)) -import Hedgehog -import Hedgehog.Range as Range -import Numeric (showHex) -import Protocols.Hedgehog.Internal -import Protocols.Wishbone -import Protocols.Wishbone.Standard.Hedgehog -import Test.Tasty -import Test.Tasty.Hedgehog - -import Bittide.DoubleBufferedRam -import Bittide.SharedTypes -import Tests.Shared - -import qualified Clash.Sized.Vector as V -import qualified Data.IntMap as I -import qualified Data.List as L -import qualified Data.Set as Set -import qualified GHC.TypeNats as TN -import qualified Hedgehog.Gen as Gen hiding (resize) -import qualified Prelude as P - -tests :: TestTree -tests = - testGroup - "Tests.DoubleBufferedRam" - [ testPropertyNamed - "Reading the buffer." - "readDoubleBufferedRam" - readDoubleBufferedRam - , testPropertyNamed - "Writing and reading back buffers." - "readWriteDoubleBufferedRam" - readWriteDoubleBufferedRam - , testPropertyNamed - "Byte addressable blockRam matches behavioral model." - "readWriteByteAddressableBlockram" - readWriteByteAddressableBlockram - , testPropertyNamed - "Byte addressable blockRam with always high byteEnables behaves like blockRam" - "byteAddressableBlockRamAsBlockRam" - byteAddressableBlockRamAsBlockRam - , testPropertyNamed - "Byte addressable double buffered blockRam matches behavioral model." - "doubleBufferedRamByteAddressable0" - doubleBufferedRamByteAddressable0 - , testPropertyNamed - "Byte addressable double buffered blockRam with always high byteEnables behaves like 'doubleBufferedRam'" - "doubleBufferedRamByteAddressable1" - doubleBufferedRamByteAddressable1 - , testPropertyNamed - "Byte addressable register can be written to and read from with byte enables." - "readWriteRegisterByteAddressable" - readWriteRegisterByteAddressable - , testPropertyNamed - "registerWb function as a normal register." - "registerWbSigToSig" - registerWbSigToSig - , testPropertyNamed - "registerWb can be written to with wishbone." - "registerWbWbToSig" - registerWbWbToSig - , testPropertyNamed - "registerWb can be read from with wishbone." - "registerWbSigToWb" - registerWbSigToWb - , testPropertyNamed - "registerWb write conflict resolution matches set priorities" - "registerWbWriteCollisions" - registerWbWriteCollisions - , testPropertyNamed - "Simulate the contentGenerator for an arbitrary vector." - "testContentGen" - testContentGen - , testPropertyNamed - "Test wishboneStorage spec compliance" - "wbStorageSpecCompliance" - wbStorageSpecCompliance - , testPropertyNamed - "Test whether wbStorage acts the same its Behavioral model" - "wbStorageBehavior" - wbStorageBehavior - , testPropertyNamed - "Test whether wbStorage reports errors at out-of-bounds accesses" - "wbStorageRangeErrors" - wbStorageRangeErrors - , testPropertyNamed - "Test whether wbStorage acts the same its Behavioral model (clash-protocols)" - "wbStorageProtocolsModel" - wbStorageProtocolsModel - ] - -genRamContents :: (MonadGen m, Integral i) => i -> m a -> m (SomeVec 1 a) -genRamContents memDepth = genSomeVec (Range.singleton $ fromIntegral (memDepth - 1)) - --- | This test checks if we can read the initial values of the double buffered Ram. -readDoubleBufferedRam :: Property -readDoubleBufferedRam = property $ do - ramDepth <- forAll . Gen.int $ Range.constant 1 31 - ramContents <- - forAll - $ genRamContents ramDepth - $ genUnsigned @_ @64 Range.constantBounded - case ramContents of - SomeVec SNat (contentsSingle :: Vec (n + 1) (Unsigned 64)) -> do - simLength <- forAll $ Gen.int (Range.constant 1 100) - let - contentsDouble = concatMap (replicate d2) contentsSingle - simRange = Range.singleton simLength - switchSignal <- forAll $ Gen.list simRange (Gen.element [A, B]) - readAddresses <- forAll . Gen.list simRange . genIndex $ Range.constantBounded - let - topEntity (unbundle -> (switch, readAddr)) = - withClockResetEnable @System - clockGen - resetGen - enableGen - $ doubleBufferedRam @_ @(n + 1) - (Vec contentsDouble) - switch - readAddr - (pure Nothing) - topEntityInput = P.zip switchSignal readAddresses - simOut = P.tail $ simulateN simLength topEntity topEntityInput - expectedOut = fmap (contentsSingle !!) readAddresses - simOut === P.init expectedOut - --- | This test checks if we can write new values to the double buffered 'blockRam' and read them. -readWriteDoubleBufferedRam :: Property -readWriteDoubleBufferedRam = property $ do - ramDepth <- forAll $ Gen.enum 1 31 - ramContents <- - forAll - $ genRamContents ramDepth - $ genUnsigned @_ @64 Range.constantBounded - let minSimLength = 2 * ramDepth - simLength <- forAll $ Gen.int (Range.constant minSimLength 100) - case ramContents of - SomeVec SNat (contentsSingle :: Vec (n + 1) (Unsigned 64)) -> do - let - contentsDouble = concatMap (replicate d2) contentsSingle - topEntity (unbundle -> (switch, readAddr, writePort)) = - withClockResetEnable - @System - clockGen - resetGen - enableGen - $ doubleBufferedRam @_ @(n + 1) - (Vec contentsDouble) - switch - readAddr - writePort - let - addresses = cycle $ fmap fromIntegral [0 .. ramDepth - 1] - switchSignal = cycle $ L.replicate ramDepth A <> L.replicate ramDepth B - writeEntries <- - forAll - ( Gen.list (Range.singleton simLength) - $ genUnsigned Range.constantBounded - ) - let - topEntityInput = - L.zip3 switchSignal addresses - $ fmap Just (P.zip addresses writeEntries) - simOut = simulateN @System simLength topEntity topEntityInput - expected = toList contentsSingle <> L.take (simLength - ramDepth - 1) writeEntries - Set.fromList simOut === Set.fromList expected - -data BitvecVec where - BitvecVec :: - (1 <= bits, 1 <= memDepth, 1 <= Regs (BitVector bits) 8) => - SNat memDepth -> - SNat bits -> - Vec memDepth (BitVector bits) -> - BitvecVec - -instance Show BitvecVec where - show (BitvecVec SNat SNat v) = show v - -genBlockRamContents :: Int -> Int -> Gen BitvecVec -genBlockRamContents memDepth bits = do - case ( TN.someNatVal $ fromIntegral (memDepth - 1) - , TN.someNatVal $ fromIntegral $ bits - 1 - ) of - (SomeNat depth0, SomeNat bits0) -> go (snatProxy depth0) (snatProxy bits0) - where - go :: forall memDepth bits. SNat memDepth -> SNat bits -> Gen BitvecVec - go depth0@SNat bits0@SNat = - case compareSNat d1 (SNat @(Regs (BitVector (bits + 1)) 8)) of - SNatLE -> - BitvecVec (succSNat depth0) (succSNat bits0) - <$> genNonEmptyVec genDefinedBitVector - _ -> error "genBlockRamContents: Generated BitVector is of size 0." - -{- | This test checks if we can write new values to the byte addressable 'blockRam' -('blockRamByteAddressable') and read them. It uses 'byteAddressableRamBehavior' as- -reference model. --} -readWriteByteAddressableBlockram :: Property -readWriteByteAddressableBlockram = property $ do - ramDepth <- forAll $ Gen.enum 1 31 - nrOfBits <- forAll $ Gen.enum 1 31 - simLength <- forAll $ bitCoerce @_ @Int <$> genUnsigned (Range.constant 2 100) - ramContents <- forAll $ genBlockRamContents ramDepth nrOfBits - case ramContents of - BitvecVec SNat SNat contents -> do - let - simRange = Range.singleton simLength - topEntity (unbundle -> (readAddr, writePort, byteSelect)) = - withClockResetEnable - clockGen - resetGen - enableGen - blockRamByteAddressable - (Vec contents) - readAddr - writePort - byteSelect - writeAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - readAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - writeEntries <- forAll (Gen.list simRange $ Gen.maybe genDefinedBitVector) - byteSelectSignal <- forAll $ Gen.list simRange genDefinedBitVector - let - topEntityInput = - L.zip3 - readAddresses - (P.zipWith (\adr wr -> (adr,) <$> wr) writeAddresses writeEntries) - byteSelectSignal - - simOut = simulateN @System simLength topEntity topEntityInput - (_, expectedOut) = - L.mapAccumL - byteAddressableRamBehavior - (L.head topEntityInput, contents) - $ L.tail topEntityInput - -- TODO: Due to some unexpected mismatch between the expected behavior of either - -- blockRam or the behavioral model, the boot behavior is inconsistent. We drop the first - -- expectedOutput cycle too, we expect this is due to the resets supplied b simulateN. - -- An issue has been made regarding this. - L.drop 2 simOut === L.tail expectedOut - -{- | This test checks if 'blockRamByteAddressable' behaves the same as 'blockRam' when the -byteEnables are always high. --} -byteAddressableBlockRamAsBlockRam :: Property -byteAddressableBlockRamAsBlockRam = property $ do - ramDepth <- forAll $ Gen.enum 1 31 - nrOfBits <- forAll $ Gen.enum 1 31 - simLength <- forAll $ Gen.int $ Range.constant 2 100 - ramContents <- forAll $ genBlockRamContents ramDepth nrOfBits - case ramContents of - BitvecVec SNat SNat contents -> do - let - simRange = Range.singleton simLength - -- topEntity returns a tuple with the outputs of (byteAddressableRam,blockRam) - topEntity (unbundle -> (readAddr, writePort)) = - withClockResetEnable clockGen resetGen enableGen - $ bundle - ( blockRamByteAddressable (Vec contents) readAddr writePort (pure maxBound) - , blockRam contents readAddr writePort - ) - writeAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - readAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - writeEntries <- forAll (Gen.list simRange $ Gen.maybe genDefinedBitVector) - let - topEntityInput = - L.zip - readAddresses - (P.zipWith (\adr wr -> (adr,) <$> wr) writeAddresses writeEntries) - simOut = simulateN @System simLength topEntity topEntityInput - (fstOut, sndOut) = L.unzip simOut - footnote . fromString $ "simOut: " <> showX simOut - fstOut === sndOut - -{- | This test checks if we can write new values to the byte addressable double buffered -'blockRam' ('doubleBufferedRamByteAddressable') and read them. --} -doubleBufferedRamByteAddressable0 :: Property -doubleBufferedRamByteAddressable0 = property $ do - ramDepth <- forAll $ Gen.enum 1 31 - nrOfBits <- forAll $ Gen.enum 1 31 - simLength <- forAll $ Gen.int $ Range.constant 2 100 - ramContents <- forAll $ genBlockRamContents ramDepth nrOfBits - case ramContents of - BitvecVec SNat SNat (contentsSingle :: Vec memDepth (BitVector bits)) -> do - let - contentsDouble = concatMap (replicate d2) contentsSingle - simRange = Range.singleton simLength - topEntity (unbundle -> (switch, readAddr, writePort, byteSelect)) = - withClockResetEnable @System clockGen resetGen enableGen - $ doubleBufferedRamByteAddressable - (Vec contentsDouble) - switch - readAddr - writePort - byteSelect - writeAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - readAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - writeEntries <- forAll (Gen.list simRange $ Gen.maybe genDefinedBitVector) - byteSelectSignal <- forAll $ Gen.list simRange genDefinedBitVector - switchSignal <- forAll $ Gen.list simRange (Gen.element [A, B]) - let - topEntityInput = - L.zip4 - switchSignal - readAddresses - (P.zipWith (\adr wr -> (adr,) <$> wr) writeAddresses writeEntries) - byteSelectSignal - simOut = simulateN @System simLength topEntity topEntityInput - (_, expectedOut) = - L.mapAccumL - byteAddressableDoubleBufferedRamBehavior - (L.head topEntityInput, contentsSingle, contentsSingle) - $ L.tail topEntityInput - -- TODO: Due to some unexpected mismatch between the expected behavior of either - -- blockRam or the behavioral model, the boot behavior is inconsistent. We drop the first - -- expectedOutput cycle too, we expect this is due to the resets supplied b simulateN. - -- An issue has been made regarding this. - L.drop 2 simOut === L.tail expectedOut - -{- | This test checks if 'doubleBufferedRamByteAddressable' behaves the same as -'doubleBufferedRam' when the byteEnables are always high. --} -doubleBufferedRamByteAddressable1 :: Property -doubleBufferedRamByteAddressable1 = property $ do - ramDepth <- forAll $ Gen.enum 1 31 - nrOfBits <- forAll $ Gen.enum 1 31 - simLength <- forAll $ Gen.int $ Range.constant 2 100 - ramContents <- forAll $ genBlockRamContents ramDepth nrOfBits - case ramContents of - BitvecVec SNat SNat (contentsSingle :: Vec memDepth (BitVector bits)) -> do - let - contentsDouble = concatMap (replicate d2) contentsSingle - simRange = Range.singleton simLength - topEntity (unbundle -> (switch, readAddr, writePort)) = - withClockResetEnable @System clockGen resetGen enableGen - $ bundle - ( doubleBufferedRamByteAddressable - (Vec contentsDouble) - switch - readAddr - writePort - (pure maxBound) - , doubleBufferedRam (Vec contentsDouble) switch readAddr writePort - ) - writeAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - readAddresses <- forAll $ Gen.list simRange $ genIndex Range.constantBounded - writeEntries <- forAll (Gen.list simRange $ Gen.maybe genDefinedBitVector) - switchSignal <- forAll $ Gen.list simRange (Gen.element [A, B]) - let - topEntityInput = - L.zip3 - switchSignal - readAddresses - (P.zipWith (\adr wr -> (adr,) <$> wr) writeAddresses writeEntries) - simOut = simulateN @System simLength topEntity topEntityInput - (duvOut, refOut) = L.unzip simOut - duvOut === refOut - -{- | This test checks that we can generate a 'registerByteAddressable' that stores a -configurable amount of bytes and selectively update its contents on a per byte basis. --} -readWriteRegisterByteAddressable :: Property -readWriteRegisterByteAddressable = property $ do - nBytes <- forAll $ Gen.enum 1 10 - case TN.someNatVal nBytes of - SomeNat p -> case compareSNat d1 (snatProxy p) of - SNatLE -> go p - _ -> error "readWriteRegisterByteAddressable: Amount of nBytes == 0." - where - go :: - forall nBytes m. - ( KnownNat nBytes - , 1 <= nBytes - , KnownNat (nBytes * 8) - , 1 <= (nBytes * 8) - , Monad m - ) => - Proxy nBytes -> - PropertyT m () - go Proxy = - case sameNat (Proxy @nBytes) (Proxy @(Regs (Vec nBytes Byte) 8)) of - Just Refl -> do - simLength <- forAll $ Gen.enum 1 100 - let - writeGen = genNonEmptyVec @_ @nBytes $ genDefinedBitVector @8 - initVal <- forAll writeGen - writes <- forAll $ Gen.list (Range.singleton simLength) writeGen - byteEnables <- - forAll - $ Gen.list (Range.singleton simLength) - $ genDefinedBitVector @(Regs (Vec nBytes Byte) 8) - let - topEntity (unbundle -> (newVal, byteEnable)) = - withClockResetEnable @System clockGen resetGen enableGen - $ registerByteAddressable initVal newVal byteEnable - expectedOut = P.scanl useByteEnable initVal $ P.zip writes byteEnables - useByteEnable olds (news, unpack -> enables) = - (\(enable, old, new) -> if enable then new else old) <$> zip3 enables olds news - simOut = simulateN simLength topEntity $ P.zip writes byteEnables - simOut === P.take simLength expectedOut - _ -> - error "readWriteRegisterByteAddressable: Amount of bytes not equal to registers required." - -{- | This test checks that 'registerWb' can be written to and read from via its wishbone bus. -This test makes sure that writing and reading with the wishbone bus works both with -'CircuitPriority' and 'WishbonePriority' enabled. During this test the circuit input does -not write to the register. --} -registerWbSigToSig :: Property -registerWbSigToSig = property $ do - bits <- forAll $ Gen.enum 1 100 - case TN.someNatVal bits of - SomeNat p -> case compareSNat d1 (snatProxy p) of - SNatLE -> go p - _ -> error "registerWbSigToSig: Amount of bits == 0." - where - go :: forall bits m. (KnownNat bits, 1 <= bits, Monad m) => Proxy bits -> PropertyT m () - go Proxy = case compareSNat d1 $ SNat @(Regs (BitVector bits) 32) of - SNatLE -> do - initVal <- forAll $ genDefinedBitVector @bits - writes <- forAll $ Gen.list (Range.constant 1 25) $ genDefinedBitVector @bits - let - simLength = L.length writes + 1 - someReg prio sigIn = - fst - $ withClockResetEnable clockGen resetGen enableGen - $ registerWbSpecVal @_ @_ @4 @32 prio initVal (pure emptyWishboneM2S) sigIn - topEntity sigIn = bundle (someReg CircuitPriority sigIn, someReg WishbonePriority sigIn) - topEntityInput = (Just <$> writes) <> [Nothing] - simOut = simulateN @System simLength topEntity topEntityInput - (fstOut, sndOut) = L.unzip simOut - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "input:" <> showX topEntityInput - footnote . fromString $ "expected" <> showX writes - fstOut === sndOut - writes === L.tail fstOut - _ -> error "registerWbSigToSig: Registers required to store bitvector == 0." - -{- | This test checks that 'registerWb' can be written to with the wishbone bus and read from -with the circuit output. This test makes sure that the behavior with 'CircuitPriority' -and 'WishbonePriority' is identical. During this test the circuit input does not write -to the register. --} -registerWbWbToSig :: Property -registerWbWbToSig = property $ do - bits <- forAll $ Gen.enum 1 100 - case TN.someNatVal bits of - SomeNat p -> case compareSNat d1 (snatProxy p) of - SNatLE -> go p - _ -> error "registerWbWbToSig: Amount of bits == 0." - where - go :: forall bits m. (KnownNat bits, 1 <= bits, Monad m) => Proxy bits -> PropertyT m () - go Proxy = case compareSNat d1 $ SNat @(Regs (BitVector bits) 32) of - SNatLE -> do - let regs = (natToNum @(DivRU bits 32)) - initVal <- forAll $ genDefinedBitVector @bits - writes <- forAll $ Gen.list (Range.constant 1 25) $ genDefinedBitVector @bits - let - simLength = L.length writes * regs + 2 - someReg prio wbIn = - fst - $ withClockResetEnable clockGen resetGen enableGen - $ registerWbSpecVal @System @_ @4 @30 prio initVal wbIn (pure Nothing) - topEntity wbIn = bundle (someReg CircuitPriority wbIn, someReg WishbonePriority wbIn) - topEntityInput = L.concatMap wbWrite writes <> L.repeat emptyWishboneM2S - simOut = simulateN simLength topEntity topEntityInput - (fstOut, sndOut) = L.unzip simOut - filteredOut = everyNth regs $ L.tail fstOut - - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "filteredOut:" <> showX filteredOut - footnote . fromString $ "input:" <> showX (L.take simLength topEntityInput) - footnote . fromString $ "expected" <> showX writes - fstOut === sndOut - writes === L.take (L.length writes) filteredOut - _ -> error "registerWbWbToSig: Registers required to store bitvector == 0." - where - wbWrite v = L.zipWith bv2WbWrite [0 .. L.length l - 1] l - where - RegisterBank (toList -> l) = getRegsLe v - everyNth n l - | L.length l >= n = L.head xs : everyNth n (L.tail xs) - | otherwise = [] - where - xs = L.drop (n - 1) l - -{- | This test checks that 'registerWb' can be written to by the circuit and read from -with the wishbone bus. This test makes sure that the behavior with 'CircuitPriority' -and 'WishbonePriority' is identical. During this test the wishbone bus does not write -to the register. --} -registerWbSigToWb :: Property -registerWbSigToWb = property $ do - bits <- forAll $ Gen.enum 1 100 - case TN.someNatVal bits of - SomeNat p -> case compareSNat d1 (snatProxy p) of - SNatLE -> go p - _ -> error "registerWbSigToWb: Amount of bits == 0." - where - go :: forall bits m. (KnownNat bits, 1 <= bits, Monad m) => Proxy bits -> PropertyT m () - go Proxy = case compareSNat d1 $ SNat @(Regs (BitVector bits) 32) of - SNatLE -> do - initVal <- forAll $ genDefinedBitVector @bits - writes <- forAll $ Gen.list (Range.constant 1 25) $ genDefinedBitVector @bits - let - someReg prio sigIn wbIn = - snd - $ withClockResetEnable clockGen resetGen enableGen - $ registerWbSpecVal @_ @_ @4 @32 prio initVal wbIn sigIn - topEntity (unbundle -> (sigIn, wbIn)) = - bundle - (someReg CircuitPriority sigIn wbIn, someReg WishbonePriority sigIn wbIn) - padWrites x = L.take (natToNum @(Regs (BitVector bits) 32)) $ Just x : L.repeat Nothing - readOps = - emptyWishboneM2S - : cycle - (wbRead <$> [(0 :: Int) .. (natToNum @(Regs (BitVector bits) 32) - 1)]) - topEntityInput = L.zip (L.concatMap padWrites writes <> [Nothing]) readOps - simLength = L.length topEntityInput - simOut = simulateN @System simLength topEntity topEntityInput - (fstOut, sndOut) = L.unzip simOut - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "input:" <> showX topEntityInput - footnote . fromString $ "expected" <> showX writes - postProcWb fstOut === postProcWb sndOut - writes === wbDecoding (L.tail fstOut) - _ -> error "registerWbSigToWb: Registers required to store bitvector == 0." - where - wbDecoding :: ([WishboneS2M (BitVector 32)] -> [BitVector bits]) - wbDecoding (wbNow : wbRest) - | acknowledge wbNow = entry : wbDecoding rest - | otherwise = wbDecoding wbRest - where - (fmap readData -> entryList, rest) = - L.splitAt (natToNum @(Regs (BitVector bits) 32)) (wbNow : wbRest) - entry = case V.fromList entryList of - Just (vec :: Vec (Regs (BitVector bits) 32) (BitVector 32)) -> - getDataLe @32 (RegisterBank vec) - Nothing -> - error - $ "wbDecoding: list to vector conversion failed: " - <> show entryList - <> "from " - <> show (wbNow : wbRest) - wbDecoding [] = [] - wbRead i = - (emptyWishboneM2S @32) - { addr = resize (pack i) - , busCycle = True - , busSelect = maxBound - , strobe = True - } - postProcWb (WishboneS2M{..} : wbRest) - | acknowledge = Just readData : postProcWb wbRest - | err = Nothing : postProcWb wbRest - | otherwise = postProcWb wbRest - postProcWb _ = [] - -{- | This test checks that the behavior of 'registerWb' matches the set priorities when -a write conflict occurs. It is expected that with 'WishbonePriority', the circuit -ignores write operations from the circuit during a wishbone write operation. -With 'CircuitPriority', wishbone write operations are acknowledged, but silently -ignored during a circuit write cycle. --} -registerWbWriteCollisions :: Property -registerWbWriteCollisions = property $ do - bits <- forAll $ Gen.enum 1 32 - case TN.someNatVal bits of - SomeNat p -> case compareSNat d1 (snatProxy p) of - SNatLE -> go p - _ -> error "registerWbWriteCollisions: Amount of bits == 0." - where - go :: forall bits m. (KnownNat bits, 1 <= bits, Monad m) => Proxy bits -> PropertyT m () - go Proxy = case compareSNat d1 $ SNat @(Regs (BitVector bits) 32) of - SNatLE -> do - initVal <- forAll $ genDefinedBitVector @bits - writeAmount <- forAll $ Gen.enum 1 25 - sigWrites <- forAll $ Gen.list (Range.singleton writeAmount) $ genDefinedBitVector @bits - wbWrites <- forAll $ Gen.list (Range.singleton writeAmount) $ genDefinedBitVector @bits - let - simLength = writeAmount + 1 - someReg prio sigIn wbIn = - fst - $ withClockResetEnable clockGen resetGen enableGen - $ registerWbSpecVal @System @_ @4 @30 prio initVal wbIn sigIn - topEntity (unbundle -> (sigIn, wbIn)) = - bundle - (someReg CircuitPriority sigIn wbIn, someReg WishbonePriority sigIn wbIn) - topEntityInput = - L.zip - (Just <$> sigWrites) - (L.concatMap wbWrite wbWrites <> L.repeat emptyWishboneM2S) - simOut = simulateN simLength topEntity topEntityInput - (fstOut, sndOut) = L.unzip simOut - - footnote . fromString $ "WishbonePrio out: " <> showX sndOut - footnote . fromString $ "CircuitPrio out: " <> showX fstOut - footnote . fromString $ "input:" <> showX (L.take simLength topEntityInput) - footnote . fromString $ "wbIn" <> showX wbWrites - footnote . fromString $ "sigIn" <> showX sigWrites - sigWrites === L.tail fstOut - wbWrites === L.tail sndOut - _ -> error "registerWbWriteCollisions: Registers required to store bitvector == 0." - where - wbWrite v = L.zipWith bv2WbWrite (L.reverse [0 .. L.length l - 1]) l - where - RegisterBank (toList -> l) = getRegsLe v - -bv2WbWrite :: - (BitPack a, Enum a) => - a -> - ("DAT_MOSI" ::: BitVector 32) -> - WishboneM2S 30 4 (BitVector 32) -bv2WbWrite i v = - (emptyWishboneM2S @30 @(BitVector 32)) - { addr = resize (pack i) - , writeData = v - , writeEnable = True - , busCycle = True - , strobe = True - , busSelect = maxBound - } - -{- | Model for 'byteAddressableRam', it stores the inputs in its state for a one cycle delay -and updates the Ram based on the the write operation and byte enables. -Furthermore it contains read-before-write behavior based on the readAddr. --} -byteAddressableRamBehavior :: - forall bits memDepth nBytes. - ( KnownNat memDepth - , 1 <= memDepth - , KnownNat nBytes - , 1 <= nBytes - , nBytes ~ Regs (BitVector bits) 8 - , KnownNat bits - , 1 <= bits - ) => - ( (Index memDepth, Maybe (LocatedBits memDepth bits), BitVector nBytes) - , Vec memDepth (BitVector bits) - ) -> - (Index memDepth, Maybe (LocatedBits memDepth bits), BitVector nBytes) -> - ( ( ( Index memDepth - , Maybe (LocatedBits memDepth bits) - , BitVector nBytes - ) - , Vec memDepth (BitVector bits) - ) - , BitVector bits - ) -byteAddressableRamBehavior state input = (state', ram !! readAddr) - where - ((readAddr, writeOp, byteEnable), ram) = state - (writeAddr, writeData0) = fromMaybe (0, 0) writeOp - writeTrue = isJust writeOp - RegisterBank oldData = getRegsBe $ ram !! writeAddr - RegisterBank newData = getRegsBe writeData0 - newEntry = - getDataBe @8 - . RegisterBank - $ zipWith - (\sel (old, new) -> if sel then new else old) - (unpack byteEnable) - (zip oldData newData) - - ram1 = if writeTrue then replace writeAddr newEntry ram else ram - state' = (input, ram1) - -{- | Model for 'byteAddressableDoubleBufferedRamBehavior', it stores the inputs in its -state for a one cycle delay and updates the Ram based on the the write operation and -byte enables. Furthermore it contains read-before-write behavior based on the readAddr. -The only addition compared to byteAddressableRam is the fact that there's two buffers -(one read only, one write only), that can be swapped. --} -byteAddressableDoubleBufferedRamBehavior :: - forall bits memDepth nBytes. - ( KnownNat memDepth - , 1 <= memDepth - , KnownNat nBytes - , 1 <= nBytes - , nBytes ~ Regs (BitVector bits) 8 - , KnownNat bits - , 1 <= bits - ) => - ( (AorB, Index memDepth, Maybe (LocatedBits memDepth bits), BitVector nBytes) - , Vec memDepth (BitVector bits) - , Vec memDepth (BitVector bits) - ) -> - (AorB, Index memDepth, Maybe (LocatedBits memDepth bits), BitVector nBytes) -> - ( ( (AorB, Index memDepth, Maybe (LocatedBits memDepth bits), BitVector nBytes) - , Vec memDepth (BitVector bits) - , Vec memDepth (BitVector bits) - ) - , BitVector bits - ) -byteAddressableDoubleBufferedRamBehavior state input = (state', out) - where - ((switchBuffers, readAddr, writeOp, byteEnable), bufA0, bufB0) = state - (out, bufA1, bufB1) - | switchBuffers == B = (bufA0 !! readAddr, bufA0, updateEntry bufB0 writeOp) - | otherwise = (bufB0 !! readAddr, updateEntry bufA0 writeOp, bufB0) - - updateEntry buf op - | isJust writeOp = replace writeAddr newEntry buf - | otherwise = buf - where - newEntry = getNewEntry (buf !! writeAddr) writeData0 - (writeAddr, writeData0) = fromMaybe (0, 0) op - - getNewEntry old new = - getDataBe @8 - . RegisterBank - $ zipWith - (\sel (a, b) -> if sel then b else a) - (unpack byteEnable) - $ zip oldData newData - where - RegisterBank oldData = getRegsBe old - RegisterBank newData = getRegsBe new - - state' = (input, bufA1, bufB1) - -{- | Version of 'Bittide.DoubleBufferedRam.registerWb' which performs wishbone - spec validation. --} -registerWbSpecVal :: - forall dom a nBytes addrW. - ( HiddenClockResetEnable dom - , Paddable a - , KnownNat nBytes - , 1 <= nBytes - , KnownNat addrW - , BitPack a - , ShowX a - ) => - -- | Determines the write priority on write collisions - RegisterWritePriority -> - -- | Initial value. - a -> - -- | Wishbone bus (master to slave) - Signal dom (WishboneM2S addrW nBytes (Bytes nBytes)) -> - -- | New circuit value. - Signal dom (Maybe a) -> - -- | - -- 1. Outgoing stored value - -- 2. Outgoing wishbone bus (slave to master) - (Signal dom a, Signal dom (WishboneS2M (Bytes nBytes))) -registerWbSpecVal writePriority initVal m2s0 sigIn = (storedVal, s2m1) - where - (storedVal, s2m0) = registerWb @dom @a @nBytes @addrW writePriority initVal m2s1 sigIn - (m2s1, s2m1) = validateWb m2s0 s2m0 - -{- | Generate an input vector for 'contentGenerator' and check if it generates write -operations from this vector. --} -testContentGen :: Property -testContentGen = property $ do - nat <- forAll $ Gen.enum 0 32 - case TN.someNatVal nat of - SomeNat n -> go n - where - go (Proxy :: Proxy v) = do - content <- forAll $ genVec @_ @v (genUnsigned @_ @32 Range.constantBounded) - let - l = length content - simLength = 2 + l * 2 - !(writes, dones) = - L.unzip - $ sampleN @System - simLength - (bundle $ contentGenerator @_ @v @(v + 1) (Vec content)) - expectedDones - | l == 0 = L.repeat True - | otherwise = L.replicate (l + 2) False <> L.repeat True - dones === L.take simLength expectedDones - catMaybes writes === toList (zip (iterateI succ 0) content) - -wbStorageSpecCompliance :: Property -wbStorageSpecCompliance = property $ do - nat <- forAll $ Gen.enum 1 32 - case TN.someNatVal (nat - 1) of - SomeNat (succSNat . snatProxy -> n) -> go n - where - go :: forall v m. (KnownNat v, 1 <= v, Monad m) => SNat v -> PropertyT m () - go SNat = do - content <- forAll $ genNonEmptyVec @_ @v (genDefinedBitVector @32) - wcre - $ wishbonePropWithModel @System - defExpectOptions - (\_ _ () -> Right ()) - (wbStorage (Reloadable $ Vec content)) - (genRequests (snatToNum (SNat @v) - 1)) - () - - genRequests size = - Gen.list - (Range.linear 0 32) - (genWishboneTransfer @32 size (genDefinedBitVector @32)) - - genWishboneTransfer :: - (KnownNat addressWidth, KnownNat (BitSize a)) => - Int -> - -- \^ size - Gen a -> - Gen (WishboneMasterRequest addressWidth a) - genWishboneTransfer size genA = - let - validAddr = fromIntegral <$> Gen.enum 0 (size - 1) - invalidAddr = fromIntegral <$> Gen.enum size (size * 2) - in - Gen.choice - [ Read <$> validAddr <*> pure (succ 0) - , Write <$> validAddr <*> pure (succ 0) <*> genA - , Read <$> invalidAddr <*> pure (succ 0) - , Write <$> invalidAddr <*> pure (succ 0) <*> genA - ] - -deriving instance (ShowX a) => ShowX (RamOp i a) - -{- | Behavioral test for 'wbStorage', it checks whether the behavior of 'wbStorage' matches -the 'wbStorageBehaviorModel'. --} -wbStorageBehavior :: Property -wbStorageBehavior = property $ do - nWords <- forAll $ Gen.enum 2 32 - case TN.someNatVal (nWords - 2) of - SomeNat (addSNat d2 . snatProxy -> nWords0) -> go nWords0 - where - go :: - forall words m. (KnownNat words, 2 <= words, Monad m) => SNat words -> PropertyT m () - go SNat = do - content <- forAll $ genVec @_ @words genDefinedBitVector - wbRequests <- - forAll - $ Gen.list - (Range.linear 0 32) - (genWishboneTransfer @30 (natToNum @words) (genDefinedBitVector @32)) - - let - master = driveStandard defExpectOptions $ fmap snd wbRequests - slave = wcre $ wbStorage @System (NonReloadable $ Vec content) - simTransactions = exposeWbTransactions (Just 1000) master slave - goldenTransactions = wbStorageBehaviorModel (toList content) $ fmap (fmap fst) wbRequests - - footnote $ "goldenTransactions" <> show goldenTransactions - footnote $ "simTransactions" <> show simTransactions - footnote $ "wbRequests" <> show wbRequests - - simTransactions === goldenTransactions - where - genWishboneTransfer :: - (KnownNat addressWidth, KnownNat (BitSize a)) => - Int -> - -- \^ size - Gen a -> - Gen (Bool, (WishboneMasterRequest addressWidth a, Int)) - genWishboneTransfer size genA = - let - validAddr = fromIntegral <$> Gen.enum 0 (size - 1) - invalidAddr = fromIntegral <$> Gen.enum size (size * 2) - -- Make wbOps that won't be repeated - mkRead address bs = (Read address bs, 0) - mkWrite address bs a = (Write address bs a, 0) - in - -- Generate valid and invalid operations. The boolean represents the validity of the operation. - Gen.choice - [ (True,) <$> (mkRead <$> validAddr <*> genDefinedBitVector) - , (True,) <$> (mkWrite <$> validAddr <*> genDefinedBitVector <*> genA) - , (False,) <$> (mkRead <$> invalidAddr <*> genDefinedBitVector) - , (False,) <$> (mkWrite <$> invalidAddr <*> genDefinedBitVector <*> genA) - ] - --- | Behavioral model for 'wbStorage'. -wbStorageBehaviorModel :: - forall addrW bytes. - (1 <= addrW, KnownNat bytes) => - (KnownNat addrW) => - [Bytes bytes] -> - [(Bool, WishboneMasterRequest addrW (Bytes bytes))] -> - [Transaction addrW bytes (Bytes bytes)] -wbStorageBehaviorModel initList initWbOps = case (cancelMulDiv @bytes @8) of - Dict -> snd $ L.mapAccumL f initList initWbOps - where - -- Invalid request - f storedList (False, op) = (storedList, Error (wbMasterRequestToM2S op)) - -- Successful Read - f storedList (True, op@(Read i _)) = (storedList, ReadSuccess wbM2S wbS2M) - where - dat = storedList L.!! (fromIntegral i) - wbM2S = wbMasterRequestToM2S op - wbS2M = (emptyWishboneS2M @(Bytes bytes)){acknowledge = True, readData = dat} - - -- Successful Write - f storedList (True, op@(Write i bs a)) = (newList, WriteSuccess wbM2S wbS2M) - where - wbM2S = wbMasterRequestToM2S op - wbS2M = emptyWishboneS2M{acknowledge = True} - (preEntry, postEntry) = L.splitAt (fromIntegral i) storedList - oldEntry = L.head postEntry - newList = preEntry <> (pack newEntry : L.tail postEntry) - - newEntry :: Vec bytes Byte - newEntry = - zipWith3 - (\b old new -> if b then new else old) - (unpack bs) - (unpack oldEntry) - (unpack a) - -wbStorageRangeErrors :: Property -wbStorageRangeErrors = property $ do - nat <- forAll $ Gen.enum 1 32 - case TN.someNatVal (nat - 1) of - SomeNat (succSNat . snatProxy -> n) -> go n - where - go :: forall v m. (KnownNat v, 1 <= v, Monad m) => SNat v -> PropertyT m () - go SNat = do - content <- forAll $ genNonEmptyVec @_ @v (genDefinedBitVector @32) - wcre - $ wishbonePropWithModel @System - defExpectOptions - model - (wbStorage (Reloadable $ Vec content)) - (genRequests (snatToNum (SNat @v))) - (snatToInteger (SNat @v)) - - genRequests size = - Gen.list - (Range.linear 0 32) - (genWishboneTransfer @32 size (genDefinedBitVector @32)) - - genWishboneTransfer :: - (KnownNat addressWidth, KnownNat (BitSize a)) => - Int -> - -- \^ size - Gen a -> - Gen (WishboneMasterRequest addressWidth a) - genWishboneTransfer size genA = - let - validAddr = fromIntegral <$> Gen.enum 0 (size - 1) - invalidAddr = fromIntegral <$> Gen.enum size (size * 2) - in - Gen.choice - [ Read <$> validAddr <*> pure maxBound - , Write <$> validAddr <*> pure maxBound <*> genA - , Read <$> invalidAddr <*> pure maxBound - , Write <$> invalidAddr <*> pure maxBound <*> genA - ] - - model (Read addr _) s2m@WishboneS2M{..} st0 - | addr >= fromIntegral st0 && err = Right st0 - | addr >= fromIntegral st0 && not err = - Left - $ "address out of range on read should error: " - <> "addr: " - <> showHex addr "" - <> ", size " - <> showHex st0 "" - | acknowledge = Right st0 - | otherwise = - Left - $ "An in-range read should be ACK'd " - <> "addr: " - <> showHex addr "" - <> ", size " - <> showHex st0 "" - <> " - " - <> show s2m - model (Write addr _ _) s2m@WishboneS2M{..} st0 - | addr >= fromIntegral st0 && err = Right st0 - | addr >= fromIntegral st0 && not err = - Left - $ "address out of range on write should error: " - <> "addr: " - <> showHex addr "" - <> ", size " - <> showHex st0 "" - | acknowledge = Right st0 - | otherwise = - Left - $ "An in-range write should be ACK'd " - <> "addr: " - <> showHex addr "" - <> ", size " - <> showHex st0 "" - <> " - " - <> show s2m - -wbStorageProtocolsModel :: Property -wbStorageProtocolsModel = property $ do - nat <- forAll $ Gen.enum 1 32 - case TN.someNatVal (nat - 1) of - SomeNat (succSNat . snatProxy -> n) -> go n - where - go :: forall v m. (KnownNat v, 1 <= v, Monad m) => SNat v -> PropertyT m () - go SNat = do - content <- forAll $ genNonEmptyVec @_ @v (genDefinedBitVector @32) - wcre - $ wishbonePropWithModel @System - defExpectOptions - model - (wbStorage (Reloadable $ Vec content)) - (genRequests (snatToNum (SNat @v))) - (I.fromAscList $ L.zip [0 ..] (toList content)) - - genRequests size = - Gen.list - (Range.linear 0 32) - (genWishboneTransfer @32 size (genDefinedBitVector @32)) - - genWishboneTransfer :: - (KnownNat addressWidth, KnownNat (BitSize a)) => - Int -> - -- \^ size - Gen a -> - Gen (WishboneMasterRequest addressWidth a) - genWishboneTransfer size genA = - let - validAddr = fromIntegral <$> Gen.enum 0 (size - 1) - in - -- only generating _valid_ requests here - Gen.choice - [ Read <$> validAddr <*> pure maxBound - , Write <$> validAddr <*> pure maxBound <*> genA - ] - - model (Read addr _) s2m@WishboneS2M{..} st0 - | err || retry = - Left - $ "An in-range read should be ACK'd " - <> "addr: " - <> showHex addr "" - <> ", size " - <> showHex (I.size st0 * 4) "" - <> " - " - <> show s2m - | otherwise = - let val = st0 I.! modelAddr - in if val == readData - then Right st0 - else - Left - $ "Read from model results in different value. Model: " - <> showHex val "" - <> ", Circuit: " - <> showHex readData "" - where - modelAddr = fromIntegral addr - model (Write addr _ wr) s2m@WishboneS2M{..} st0 - | err || retry = - Left - $ "An in-range write should be ACK'd " - <> "addr: " - <> showHex addr "" - <> ", size " - <> showHex (I.size st0 * 4) "" - <> " - " - <> show s2m - | otherwise = - Right $ I.insert modelAddr wr st0 - where - modelAddr = fromIntegral addr diff --git a/bittide/tests/Tests/ElasticBuffer.hs b/bittide/tests/Tests/ElasticBuffer.hs deleted file mode 100644 index d94d50f3f..000000000 --- a/bittide/tests/Tests/ElasticBuffer.hs +++ /dev/null @@ -1,204 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE TemplateHaskell #-} -{-# OPTIONS_GHC -Wno-orphans #-} - -module Tests.ElasticBuffer where - -import Clash.Prelude - -import Test.Tasty -import Test.Tasty.HUnit - -import Bittide.ClockControl (RelDataCount, targetDataCount) -import Bittide.ElasticBuffer - -import qualified Data.List as L - -createDomain vXilinxSystem{vPeriod = hzToPeriod 200e6, vName = "Fast"} -createDomain vXilinxSystem{vPeriod = hzToPeriod 20e6, vName = "Slow"} - -tests :: TestTree -tests = - testGroup - "Tests.ElasticBuffer" - [ testGroup - "xilinxElasticBuffer" - [ testCase "case_xilinxElasticBufferMaxBound" case_xilinxElasticBufferMaxBound - , testCase "case_xilinxElasticBufferMinBound" case_xilinxElasticBufferMinBound - , testCase "case_xilinxElasticBufferEq" case_xilinxElasticBufferEq - ] - , testGroup - "resettableXilinxElasticBuffer" - [ testCase "case_resettableXilinxElasticBufferEq" case_resettableXilinxElasticBufferEq - , testCase - "case_resettableXilinxElasticBufferMaxBound" - case_resettableXilinxElasticBufferMaxBound - , testCase - "case_resettableXilinxElasticBufferMinBound" - case_resettableXilinxElasticBufferMinBound - ] - ] - -{- | When the xilinxElasticBuffer is written to more quickly than it is being read from, -its data count should overflow. --} -case_xilinxElasticBufferMaxBound :: Assertion -case_xilinxElasticBufferMaxBound = do - let - ebMode = fromList $ L.replicate 3 Fill <> L.repeat Pass - wData = pure (0 :: Unsigned 8) - underflows = - sampleN - 256 - ( (\(_, under, _, _) -> under) - (xilinxElasticBuffer @6 (clockGen @Slow) (clockGen @Fast) resetGen ebMode wData) - ) - overflows = - sampleN - 256 - ( (\(_, _, over, _) -> over) - (xilinxElasticBuffer @6 (clockGen @Slow) (clockGen @Fast) resetGen ebMode wData) - ) - - assertBool "elastic buffer should overflow" (or overflows) - assertBool "elastic buffer should not underflow" (not $ or underflows) - -{- | When the xilinxElasticBuffer is read from more quickly than it is being written to, -its data count should underflow. --} -case_xilinxElasticBufferMinBound :: Assertion -case_xilinxElasticBufferMinBound = do - let - ebMode = fromList $ L.replicate 32 Fill <> L.repeat Pass - wData = pure (0 :: Unsigned 8) - underflows = - sampleN - 256 - ( (\(_, under, _, _) -> under) - (xilinxElasticBuffer @6 (clockGen @Fast) (clockGen @Slow) resetGen ebMode wData) - ) - overflows = - sampleN - 256 - ( (\(_, _, over, _) -> over) - (xilinxElasticBuffer @6 (clockGen @Fast) (clockGen @Slow) resetGen ebMode wData) - ) - - assertBool "elastic buffer should underflow" (or underflows) - assertBool "elastic buffer should not overflow" (not $ or overflows) - -{- | When the xilinxElasticBuffer is written to as quickly to as it is read from, it should -neither overflow nor underflow. --} -case_xilinxElasticBufferEq :: Assertion -case_xilinxElasticBufferEq = do - let - ebMode = fromList $ L.replicate 32 Fill <> L.repeat Pass - wData = pure (0 :: Unsigned 8) - underflows = - sampleN - 256 - ( (\(_, under, _, _) -> under) - (xilinxElasticBuffer @5 (clockGen @Slow) (clockGen @Slow) resetGen ebMode wData) - ) - overflows = - sampleN - 256 - ( (\(_, _, over, _) -> over) - (xilinxElasticBuffer @5 (clockGen @Slow) (clockGen @Slow) resetGen ebMode wData) - ) - - assertBool "elastic buffer should not underflow" (not $ or underflows) - assertBool "elastic buffer should not overflow" (not $ or overflows) - -{- | When the resettableXilinxElasticBuffer is written to as quickly as it is read from, it eventually - stabalises. --} -case_resettableXilinxElasticBufferEq :: Assertion -case_resettableXilinxElasticBufferEq = do - let - wData = pure (0 :: Unsigned 8) - (dataCounts, underflows, overflows, ebModes, _) = - L.unzip5 - . L.dropWhile (\(_, _, _, eb, _) -> eb == Drain || eb == Fill) - $ sampleN - 256 - ( bundle (resettableXilinxElasticBuffer @5 (clockGen @Slow) (clockGen @Slow) resetGen wData) - ) - dataCountBounds = - L.all - ((< 3) . abs . subtract (toInteger (targetDataCount :: RelDataCount 5)) . toInteger) - dataCounts - - assertBool "elastic buffer should get out of its Fill state" ((> 0) $ L.length ebModes) - assertBool "elastic buffer should not overflow after stabalising" (not $ or overflows) - assertBool "elastic buffer should not underflow after stabalising" (not $ or underflows) - assertBool - "elastic buffer should be in Pass mode after stabalising" - (L.all (== Pass) ebModes) - assertBool - "elastic buffer datacount should be `targetDataCount` (margin 3 elements) after stabalising" - dataCountBounds - -{- | When the xilinxElasticBuffer is written to more quickly than it is being read from, -its data count should overflow. Upon an overflow, the fifo is Drained and then filled -to half full, after which the cycle repeats. --} -case_resettableXilinxElasticBufferMaxBound :: Assertion -case_resettableXilinxElasticBufferMaxBound = do - let - wData = pure (0 :: Unsigned 8) - (_, underflows, overflows, _, _) = - L.unzip5 - . L.filter (\(_, _, _, eb, _) -> eb == Pass) - $ sampleN - 256 - ( bundle (resettableXilinxElasticBuffer @5 (clockGen @Slow) (clockGen @Fast) resetGen wData) - ) - - -- After the fifo overflows, it should Drain the buffer, then fill it to half full and - -- reset. - assertBool - "elastic buffer should reset after an overflow" - ([True, False] `L.isInfixOf` overflows) - -- Since the overflows list is filtered on eb==Pass, the Drain and Fill operations are - -- left out. Therefore, the fifo should not return the overflow signal twice in a row. - assertBool - "elastic buffer should not overflow twice in a row" - (not $ L.isInfixOf [True, True] overflows) - assertBool - "elastic buffer should not underflow when written to faster than read from" - (not $ or underflows) - -{- | When the xilinxElasticBuffer is read from more quickly than it is being written to, -its data count should overflow. Upon an overflow, the fifo is Drained and then filled -to half full, after which the cycle repeats. --} -case_resettableXilinxElasticBufferMinBound :: Assertion -case_resettableXilinxElasticBufferMinBound = do - let - wData = pure (0 :: Unsigned 8) - (_, underflows, overflows, _, _) = - L.unzip5 - . L.filter (\(_, _, _, eb, _) -> eb == Pass) - $ sampleN - 512 - ( bundle (resettableXilinxElasticBuffer @5 (clockGen @Fast) (clockGen @Slow) resetGen wData) - ) - - -- After the fifo underflows, it should Drain for 1 cycle and then fill it to half - -- full and reset. - assertBool - "elastic buffer should reset after an underflow" - ([True, False] `L.isInfixOf` underflows) - -- Since the underflows list is filtered on eb==Pass, the Drain and Fill operations are - -- left out. Therefore, the fifo should not return the underflow signal twice in a row. - assertBool - "elastic buffer should not underflow twice in a row" - (not $ L.isInfixOf [True, True] underflows) - assertBool - "elastic buffer should not overflow when read from faster than written to" - (not $ or overflows) diff --git a/bittide/tests/Tests/Haxioms.hs b/bittide/tests/Tests/Haxioms.hs deleted file mode 100644 index 8838c0243..000000000 --- a/bittide/tests/Tests/Haxioms.hs +++ /dev/null @@ -1,27 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE NumericUnderscores #-} - -module Tests.Haxioms where - -import Clash.Prelude -import Hedgehog -import Test.Tasty -import Test.Tasty.Hedgehog - -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range - -prop_leMult :: Property -prop_leMult = property $ do - a <- forAll $ Gen.int (Range.constant 1 1_000_000) - b <- forAll $ Gen.int (Range.constant 1 1_000_000) - assert (1 <= a * b) - -tests :: TestTree -tests = - testGroup - "Haxioms" - [ testProperty "prop_leMult" prop_leMult - ] diff --git a/bittide/tests/Tests/ProcessingElement/ReadElf.hs b/bittide/tests/Tests/ProcessingElement/ReadElf.hs deleted file mode 100644 index cb7cd906b..000000000 --- a/bittide/tests/Tests/ProcessingElement/ReadElf.hs +++ /dev/null @@ -1,295 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Tests.ProcessingElement.ReadElf where - -import Prelude - -import Data.Elf -import Data.IntMap as I -import Numeric -import Test.Tasty (TestTree, testGroup) -import Test.Tasty.HUnit (assertEqual, testCase, (@?=)) - -import Bittide.ProcessingElement.ReadElf (readElf) - -import qualified Data.ByteString as BS -import qualified Data.List as L - -riscvElfEmpty :: Elf -riscvElfEmpty = - Elf - { elfClass = ELFCLASS32 - , elfData = ELFDATA2LSB - , elfVersion = 1 - , elfOSABI = ELFOSABI_SYSV - , elfABIVersion = 1 - , elfType = ET_EXEC - , elfMachine = EM_EXT 0xF3 -- RISC-V - , elfEntry = 0x80000000 - , elfSections = [] - , elfSegments = [] - } - -textSection :: ElfSection -textSection = - ElfSection - { elfSectionName = ".text" - , elfSectionType = SHT_PROGBITS - , elfSectionFlags = [SHF_ALLOC, SHF_EXECINSTR] - , elfSectionAddr = 0x80000000 - , elfSectionSize = 0 - , elfSectionLink = 0 - , elfSectionInfo = 0 - , elfSectionAddrAlign = 0x00010000 - , elfSectionEntSize = 0 - , elfSectionData = BS.empty - } - -dataSection :: ElfSection -dataSection = - ElfSection - { elfSectionName = ".data" - , elfSectionType = SHT_PROGBITS - , elfSectionFlags = [SHF_ALLOC, SHF_WRITE] - , elfSectionAddr = 0x80000000 - , elfSectionSize = 0 - , elfSectionLink = 0 - , elfSectionInfo = 0 - , elfSectionAddrAlign = 0x00010000 - , elfSectionEntSize = 0 - , elfSectionData = BS.empty - } - -rodataSection :: ElfSection -rodataSection = - dataSection - { elfSectionName = ".rodata" - , elfSectionFlags = [SHF_ALLOC] - } - -bssSection :: ElfSection -bssSection = - ElfSection - { elfSectionName = ".bss" - , elfSectionType = SHT_NOBITS - , elfSectionFlags = [SHF_ALLOC, SHF_WRITE] - , elfSectionAddr = 0x80000000 - , elfSectionSize = 0 - , elfSectionLink = 0 - , elfSectionInfo = 0 - , elfSectionAddrAlign = 0x00010000 - , elfSectionEntSize = 0 - , elfSectionData = BS.empty - } - -instrSegment :: ElfSegment -instrSegment = - ElfSegment - { elfSegmentType = PT_LOAD - , elfSegmentFlags = [PF_R, PF_X] - , elfSegmentVirtAddr = 0x80000000 - , elfSegmentPhysAddr = 0x80000000 - , elfSegmentAlign = 0x00010000 - , elfSegmentData = BS.empty - , elfSegmentMemSize = 0 - } - -dataSegment :: ElfSegment -dataSegment = - ElfSegment - { elfSegmentType = PT_LOAD - , elfSegmentFlags = [PF_R, PF_W] - , elfSegmentVirtAddr = 0x80000000 - , elfSegmentPhysAddr = 0x80000000 - , elfSegmentAlign = 0x00010000 - , elfSegmentData = BS.empty - , elfSegmentMemSize = 0 - } - -tests :: TestTree -tests = - testGroup - "Read ELF Tests" - [ testCase "ELF file empty" $ do - let - elf = riscvElfEmpty - (entry, iMem, dMem) = readElf elf - - elfEntry elf @?= fromIntegral entry - iMem @?= I.fromList [] - dMem @?= I.fromList [] - , testCase "ELF file, only .text" $ do - let - iData = L.replicate 100 0xAB - elf = - riscvElfEmpty - { elfSections = - [ textSection - { elfSectionAddr = 0x80000000 - , elfSectionSize = fromIntegral $ L.length iData - , elfSectionData = BS.pack iData - } - ] - , elfSegments = - [ instrSegment - { elfSegmentVirtAddr = 0x80000000 - , elfSegmentPhysAddr = 0x80000000 - , elfSegmentData = BS.pack iData - , elfSegmentMemSize = fromIntegral $ L.length iData - } - ] - } - (entry, iMem, dMem) = readElf elf - iDataMap = I.fromList (L.zip [0x80000000 ..] (fromIntegral <$> iData)) - - elfEntry elf @?= fromIntegral entry - assertEqual - "instruction memory contains instruction data" - iDataMap - (I.intersection iMem iDataMap) - dMem @?= I.fromList [] - , testCase "ELF file, .data and .rodata" $ do - let - data' = L.replicate 100 0xAB - roData = L.replicate 50 0x0F - elf = - riscvElfEmpty - { elfSections = - [ dataSection - { elfSectionAddr = 0x80000000 - , elfSectionSize = fromIntegral $ L.length data' - , elfSectionData = BS.pack data' - } - , rodataSection - { elfSectionAddr = 0x80000000 + fromIntegral (L.length data') - , elfSectionSize = fromIntegral $ L.length roData - , elfSectionData = BS.pack roData - } - ] - , elfSegments = - [ dataSegment - { elfSegmentVirtAddr = 0x80000000 - , elfSegmentPhysAddr = 0x80000000 - , elfSegmentData = BS.pack (data' <> roData) - , elfSegmentMemSize = fromIntegral $ L.length data' + L.length roData - } - ] - } - (entry, iMem, dMem) = readElf elf - dataMap = I.fromList (L.zip [0x80000000 ..] (fromIntegral <$> (data' <> roData))) - - elfEntry elf @?= fromIntegral entry - iMem @?= I.fromList [] - assertEqual - "instruction memory contains instruction data" - dataMap - (I.intersection dMem dataMap) - , testCase "ELF file, .text and .data" $ do - let - iData = L.replicate 100 0xAB - dData = L.replicate 1000 0xB3 - elf = - riscvElfEmpty - { elfSections = - [ textSection - { elfSectionAddr = 0x80000000 - , elfSectionSize = fromIntegral $ L.length iData - , elfSectionData = BS.pack iData - } - , dataSection - { elfSectionAddr = 0x80000000 + fromIntegral (L.length iData) - , elfSectionSize = fromIntegral $ L.length dData - , elfSectionData = BS.pack dData - } - ] - , elfSegments = - [ instrSegment - { elfSegmentVirtAddr = 0x80000000 - , elfSegmentPhysAddr = 0x80000000 - , elfSegmentData = BS.pack iData - , elfSegmentMemSize = fromIntegral $ L.length iData - } - , dataSegment - { elfSegmentVirtAddr = 0x80000000 + fromIntegral (L.length iData) - , elfSegmentPhysAddr = 0x80000000 + fromIntegral (L.length iData) - , elfSegmentData = BS.pack dData - , elfSegmentMemSize = fromIntegral $ L.length dData - } - ] - } - (entry, iMem, dMem) = readElf elf - iDataMap = I.fromList (L.zip [0x80000000 ..] (fromIntegral <$> iData)) - dDataMap = - I.fromList - (L.zip [(0x80000000 + fromIntegral (L.length iData)) ..] (fromIntegral <$> dData)) - - elfEntry elf @?= fromIntegral entry - assertEqual - "instruction memory contains instruction data" - iDataMap - (I.intersection iMem iDataMap) - assertEqual "data memory contains data contents" dDataMap (I.intersection dMem dDataMap) - , testCase "ELF file, .text, .data and .bss" $ do - let - iData = L.replicate 100 0xAB - dData = L.replicate 1000 0xB3 - bssLen = 500 - - iStart = 0x80000000 - dStart = iStart + L.length iData - bssStart = dStart + L.length dData - elf = - riscvElfEmpty - { elfSections = - [ textSection - { elfSectionAddr = fromIntegral iStart - , elfSectionSize = fromIntegral $ L.length iData - , elfSectionData = BS.pack iData - } - , dataSection - { elfSectionAddr = fromIntegral dStart - , elfSectionSize = fromIntegral $ L.length dData - , elfSectionData = BS.pack dData - } - , bssSection - { elfSectionAddr = fromIntegral bssStart - , elfSectionSize = bssLen - , elfSectionData = BS.empty - } - ] - , elfSegments = - [ instrSegment - { elfSegmentVirtAddr = fromIntegral iStart - , elfSegmentPhysAddr = fromIntegral iStart - , elfSegmentData = BS.pack iData - , elfSegmentMemSize = fromIntegral $ L.length iData - } - , dataSegment - { elfSegmentVirtAddr = fromIntegral dStart - , elfSegmentPhysAddr = fromIntegral dStart - , elfSegmentData = BS.pack dData - , elfSegmentMemSize = fromIntegral (L.length dData) + fromIntegral bssLen - } - ] - } - (entry, iMem, dMem) = readElf elf - iDataMap = I.fromList (L.zip [iStart ..] (fromIntegral <$> iData)) - dDataMap = - I.unionWithKey - ( \k _ _ -> - error $ - "Tests.ContranomySim.ReadElf : Overlapping elements in `.data` and `.bss` memory at address 0x" - <> showHex k "" - ) - (I.fromList (L.zip [dStart ..] (fromIntegral <$> dData))) - (I.fromList (L.zip [bssStart ..] (L.replicate (fromIntegral bssLen) 0))) - - elfEntry elf @?= fromIntegral entry - assertEqual - "instruction memory contains instruction data" - iDataMap - (I.intersection iMem iDataMap) - assertEqual "data memory contains data contents" dDataMap (I.intersection dMem dDataMap) - ] diff --git a/bittide/tests/Tests/ScatterGather.hs b/bittide/tests/Tests/ScatterGather.hs deleted file mode 100644 index cae65cf01..000000000 --- a/bittide/tests/Tests/ScatterGather.hs +++ /dev/null @@ -1,398 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE ViewPatterns #-} --- For Show (SNatLE a b) -{-# OPTIONS_GHC -Wno-orphans #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} - -module Tests.ScatterGather (tests) where - -import Clash.Prelude hiding (fromList) -import qualified Prelude as P - -import Clash.Sized.Vector (fromList) -import Data.Maybe -import Data.String -import Hedgehog -import Protocols.Wishbone -import Test.Tasty -import Test.Tasty.Hedgehog - -import Bittide.Calendar hiding (ExtraRegs) -import Bittide.ScatterGather -import Bittide.SharedTypes -import Tests.Shared - -import qualified Bittide.Calendar as Cal (ExtraRegs) -import qualified Clash.Util.Interpolate as I -import qualified GHC.TypeNats as TN -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range - -{- | The extra in SomeCalendar extra defines the minimum amount of elements in the vector -and the minimum addressable indexes in the vector elements. I.e, vectors of 0 elements -and Index 0 as element are not allowed. --} -data SomeCalendar extra where - SomeCalendar :: - (1 <= (extra + n)) => SNat n -> Vec (n + extra) (Index (n + extra)) -> SomeCalendar extra - -instance Show (SomeCalendar extra) where - show (SomeCalendar SNat list) = show list - -genData :: Gen (BitVector 64) -genData = Gen.integral Range.linearBounded - -genFrame :: Gen (Maybe (BitVector 64)) -genFrame = Gen.maybe genData - -genFrameList :: Range Int -> Gen [Maybe (BitVector 64)] -genFrameList range = Gen.list range genFrame - -tests :: TestTree -tests = - testGroup - "Tests.ScatterGather" - [ testPropertyNamed - "scatterUnitWb - No overwriting implies no lost frames." - "scatterUnitNoFrameLoss" - scatterUnitNoFrameLoss - , testPropertyNamed - "gatherUnitWb - No overwriting implies no lost frames." - "gatherUnitNoFrameLoss" - gatherUnitNoFrameLoss - , testPropertyNamed - "S/G units - Ack stalling address at metacycle end." - "metacycleStalling" - metacycleStalling - ] - --- | Generates a 'CalendarConfig' for the 'gatherUnitWb' or 'scatterUnitWb' -genCalendarConfig :: - forall nBytes addrW calEntry maxDepth. - ( KnownNat nBytes - , 1 <= nBytes - , KnownNat maxDepth - , 2 <= maxDepth - , KnownNat addrW - , calEntry ~ Index maxDepth - ) => - SNat maxDepth -> - Gen (CalendarConfig nBytes addrW calEntry) -genCalendarConfig sizeNat@(snatToNum -> dMax) = do - dA <- Gen.enum 1 dMax - dB <- Gen.enum 1 dMax - case (TN.someNatVal dA, TN.someNatVal dB) of - ( SomeNat (snatProxy -> depthA) - , SomeNat (snatProxy -> depthB) - ) -> do - let - regAddrBits = SNat @(2 + NatRequiredBits (Regs calEntry (nBytes * 8) + Cal.ExtraRegs)) - bsCalEntry = SNat @(BitSize calEntry) - case ( isInBounds d1 depthA sizeNat - , isInBounds d1 depthB sizeNat - , compareSNat regAddrBits (SNat @addrW) - , compareSNat d1 bsCalEntry - ) of - (InBounds, InBounds, SNatLE, SNatLE) -> go depthA depthB - (a, b, c, d) -> - error - [I.i| - genCalendarConfig: calEntry constraints not satisfied: - - a: #{a} - b: #{b} - c: #{c} - d: #{d} - - ... - |] - where - go :: - forall depthA depthB. - ( 1 <= depthA - , 1 <= depthB - , LessThan depthA maxDepth - , LessThan depthB maxDepth - ) => - SNat depthA -> - SNat depthB -> - Gen (CalendarConfig nBytes addrW (Index maxDepth)) - go SNat SNat = do - calActive <- - fmap nonRepeatingEntry - . fromMaybe errmsg - . fromList @depthA - . P.take (natToNum @depthA) - <$> Gen.shuffle @_ @(Index maxDepth) - [0 .. natToNum @(maxDepth - 1)] - calShadow <- - fmap nonRepeatingEntry - . fromMaybe errmsg - . fromList @depthB - . P.take (natToNum @depthB) - <$> Gen.shuffle @_ @(Index maxDepth) - [0 .. natToNum @(maxDepth - 1)] - return $ CalendarConfig sizeNat calActive calShadow - errmsg = errorX "genCalendarConfig: list to vector conversion failed" - --- | Check if the scatter unit with wishbone interface loses no frames. -scatterUnitNoFrameLoss :: Property -scatterUnitNoFrameLoss = property $ do - maxCalSize <- forAll $ Gen.enum 2 32 - case TN.someNatVal (maxCalSize - 2) of - SomeNat (addSNat d2 . snatProxy -> p) -> do - runTest =<< forAll (genCalendarConfig @4 @32 p) - where - runTest :: - (KnownNat maxSize, 1 <= maxSize) => - CalendarConfig 4 32 (Index maxSize) -> - PropertyT IO () - runTest calConfig@(CalendarConfig _ calA@(length -> memDepth) _) = do - -- Number of metacycles of input to generate - metaCycles <- forAll $ Gen.enum 1 10 - let - -- reset cycle + cycle delay, last metacycle's writes can be read in (metacycles + 1) - simLength = 2 + (1 + metaCycles) * memDepth - inputGen = Gen.list (Range.singleton metaCycles) - metaCycleNothing = P.replicate memDepth Nothing - -- Generate at most memDepth `div` 2 elements to be written each metacycle since - -- we need two cycles to read a written element. - metaCycleGen = genFrameList (Range.singleton $ memDepth `div` 2) - - inputFrames <- - forAll - $ padToLength (simLength `div` memDepth + 1) metaCycleNothing - <$> inputGen (padToLength memDepth Nothing <$> metaCycleGen) - let - topEntity (unbundle -> (wbIn, linkIn)) = - fst - $ withClockResetEnable - clockGen - resetGen - enableGen - (scatterUnitWb @System @32) - (ScatterConfig calConfig) - (pure emptyWishboneM2S) - linkIn - wbIn - - wbReadOps = - P.take simLength $ P.replicate memDepth emptyWishboneM2S - P.++ P.concat - ( padToLength memDepth emptyWishboneM2S - . P.concat - . P.zipWith wbRead (toList $ fmap veEntry calA) - <$> inputFrames - ) - - topEntityInput = P.zip wbReadOps (P.concat inputFrames) - simOut = simulateN simLength topEntity topEntityInput - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "simIn: " <> showX wbReadOps - footnote . fromString $ "cal: " <> showX calA - wbDecoding simOut === P.take simLength (catMaybes (P.concat inputFrames)) - padToLength l padElement g = P.take l (g P.++ P.repeat padElement) - --- | Check if the gather unit with wishbone interface loses no frames. -gatherUnitNoFrameLoss :: Property -gatherUnitNoFrameLoss = property $ do - maxCalSize <- forAll $ Gen.enum 2 32 - case TN.someNatVal (maxCalSize - 2) of - SomeNat (addSNat d2 . snatProxy -> p) -> do - runTest =<< forAll (genCalendarConfig @4 @32 p) - where - runTest :: - (KnownNat maxSize, 1 <= maxSize) => - CalendarConfig 4 32 (Index maxSize) -> - PropertyT IO () - runTest calConfig@(CalendarConfig _ calA@(length -> memDepth) _) = do - metaCycles <- forAll $ Gen.enum 1 10 - let - activeEntryList = toList $ fmap veEntry calA - simLength = 2 + (1 + metaCycles) * memDepth - inputGen = Gen.list (Range.singleton metaCycles) - metaCycleNothing = P.replicate memDepth Nothing - metaCycleGen = genFrameList (Range.singleton $ memDepth `div` 2) - inputFrames <- - forAll - $ padToLength (simLength `div` memDepth + 1) metaCycleNothing - <$> inputGen (padToLength memDepth Nothing <$> metaCycleGen) - let - topEntity wbIn = - (\(a, _, _) -> a) - $ withClockResetEnable - clockGen - resetGen - enableGen - (gatherUnitWb @System @30) - (GatherConfig calConfig) - (pure emptyWishboneM2S) - wbIn - - wbWriteOps = - P.take simLength - . P.concat - $ padToLength memDepth emptyWishboneM2S - . P.concat - . P.zipWith wbWrite activeEntryList - <$> inputFrames - - simOut = simulateN simLength topEntity wbWriteOps - addressedFrames = P.zip (P.concat inputFrames) (cycle activeEntryList) - writtenFrames = [if snd e /= 0 then fst e else Nothing | e <- addressedFrames] - prePad items = P.replicate (1 + memDepth) Nothing P.++ items - expectedOutput = P.take simLength (fromMaybe 1 <$> P.filter isJust writtenFrames) - - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "simIn: " <> showX wbWriteOps - footnote . fromString $ "cal: " <> showX calA - footnote . fromString $ "writtenFrames: " <> showX writtenFrames - - directedDecode (prePad writtenFrames) simOut === expectedOutput - - padToLength l padElement g = P.take l (g P.++ P.repeat padElement) - -directedDecode :: [Maybe a] -> [Maybe b] -> [b] -directedDecode ((Just _) : as) ((Just b) : bs) = b : directedDecode as bs -directedDecode (Nothing : as) (_ : bs) = directedDecode as bs -directedDecode _ _ = [] - -{- | Simple test which generates a 'scatterUnitWb' and 'gatherUnitWb' with a certain calendar -Their wishbone busses are statically hooked up to a transaction that reads from the -stalling address. This test checks that it generates an acknowledge on this address -one cycle after the end of each metacycle (at the start of every _new_ metacycle). --} -metacycleStalling :: Property -metacycleStalling = property $ do - maxCalSize <- forAll $ Gen.enum 2 32 - case TN.someNatVal (maxCalSize - 2) of - SomeNat (addSNat d2 . snatProxy -> p) -> do - runTest =<< forAll (genCalendarConfig @4 @32 p) - where - runTest :: - forall maxSize. - (KnownNat maxSize, 2 <= maxSize) => - CalendarConfig 4 32 (Index maxSize) -> - PropertyT IO () - runTest calConfig@(CalendarConfig _ (length -> calSize) _) = do - metacycles <- forAll $ Gen.enum 1 5 - let - simLength = 1 + metacycles * calSize - topEntity = bundle (acknowledge <$> suWB, acknowledge <$> guWB) - where - suWB = - wcre - $ fst - $ scatterUnitWb @System - (ScatterConfig calConfig) - (pure emptyWishboneM2S) - linkIn - wbStall - guWB = - wcre - $ (\(_, x, _) -> x) - $ gatherUnitWb @System - (GatherConfig calConfig) - (pure emptyWishboneM2S) - wbStall - wbStall = - pure - $ (emptyWishboneM2S @32) - { -- 2 because addressing is 64 bit aligned. - addr = (2 * (natToNum @maxSize @(BitVector 32))) - , busCycle = True - , strobe = True - } - linkIn = pure $ deepErrorX "linkIn undefined." - expectedAcks = - P.take simLength - $ P.replicate (1 + calSize) False - <> cycle (True : P.replicate (calSize - 1) False) - simOut = sampleN simLength topEntity - simOut === fmap (\a -> (a, a)) expectedAcks - -{- | Decode an incoming slave bus by consuming two acknowledged signals and concatenating -their readData's. --} -wbDecoding :: - (KnownNat nBytes) => - [WishboneS2M (Bytes nBytes)] -> - [Bytes (nBytes + nBytes)] -wbDecoding (s2m0 : s2m1 : s2ms) - | acknowledge s2m0 && acknowledge s2m1 = out : wbDecoding s2ms - | otherwise = wbDecoding (s2m1 : s2ms) - where - out = readData s2m0 ++# readData s2m1 -wbDecoding _ = [] - -{- | Tranform a read address with expected frame into a wishbone read operation for testing -the 'scatterUnitWb'. The second argument indicate wether or not a frame can be read from -that read address. The read operation reads data over 2 read cycles. --} -wbRead :: - forall nBytes addrW maxIndex a. - ( KnownNat nBytes - , KnownNat addrW - , KnownNat maxIndex - , 1 <= maxIndex - ) => - Index maxIndex -> - Maybe a -> - [WishboneM2S addrW nBytes (Bytes nBytes)] -wbRead readAddr (Just _) = - [ (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = (`shiftL` 1) . resize $ pack readAddr - , busCycle = True - , strobe = True - , busSelect = maxBound - } - , (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = 1 .|. ((`shiftL` 1) . resize $ pack readAddr) - , busCycle = True - , strobe = True - , busSelect = maxBound - } - ] -wbRead _ Nothing = [] - -{- | Transform a write address with frame to a wishbone write operation for testing the -'gatherUnitWb'. The write operation writes the incoming bitvector over 2 write cycles. --} -wbWrite :: - forall nBytes addrW maxIndex. - ( KnownNat nBytes - , KnownNat addrW - , KnownNat maxIndex - , 1 <= maxIndex - ) => - Index maxIndex -> - Maybe (Bytes (nBytes * 2)) -> - [WishboneM2S addrW nBytes (Bytes nBytes)] -wbWrite writeAddr (Just frame) = - [ (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = (`shiftL` 1) . resize $ pack writeAddr - , busSelect = maxBound - , busCycle = True - , strobe = True - , writeEnable = True - , writeData = lower - } - , (emptyWishboneM2S @addrW @(Bytes nBytes)) - { addr = 1 .|. ((`shiftL` 1) . resize $ pack writeAddr) - , busSelect = maxBound - , busCycle = True - , strobe = True - , writeEnable = True - , writeData = upper - } - ] - where - (lower, upper) = split frame -wbWrite _ Nothing = [] diff --git a/bittide/tests/Tests/Shared.hs b/bittide/tests/Tests/Shared.hs deleted file mode 100644 index 4d3f489fc..000000000 --- a/bittide/tests/Tests/Shared.hs +++ /dev/null @@ -1,248 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE RecordWildCards #-} - -module Tests.Shared where - -import Clash.Prelude - -import Clash.Hedgehog.Sized.Unsigned - -import Data.Constraint (Dict (Dict)) -import Data.Constraint.Nat.Extra (divWithRemainder) -import GHC.Stack (HasCallStack) -import Hedgehog -import Protocols (Circuit (..), toSignals) -import Protocols.Wishbone as Wb -import Protocols.Wishbone.Standard.Hedgehog (WishboneMasterRequest (..), validatorCircuit) - -import Bittide.Calendar -import Bittide.SharedTypes - -import qualified Data.List as L -import qualified GHC.TypeNats as TypeNats -import qualified Hedgehog.Range as Range - -data IsInBounds a b c where - InBounds :: (a <= b, b <= c) => IsInBounds a b c - NotInBounds :: IsInBounds a b c - -deriving instance Show (IsInBounds a b c) - -data SomeSNat where - SomeSNat :: forall m. SNat m -> SomeSNat - --- Like 'TypeNats.someNatVal', but generates 'SomeSNat' -someSNat :: Natural -> SomeSNat -someSNat n = - case TypeNats.someNatVal n of - SomeNat p -> - SomeSNat (snatProxy p) - --- | Returns 'InBounds' when a <= b <= c, otherwise returns 'NotInBounds'. -isInBounds :: SNat a -> SNat b -> SNat c -> IsInBounds a b c -isInBounds a b c = case (compareSNat a b, compareSNat b c) of - (SNatLE, SNatLE) -> InBounds - _ -> NotInBounds - -{- | We use a custom generator for BitVector's because the current Clash implementation -uses genVec which is slow. --} -genDefinedBitVector :: forall n m. (MonadGen m, KnownNat n) => m (BitVector n) -genDefinedBitVector = pack <$> genUnsigned Range.constantBounded - --- | Single datatype to represent successful and unsuccessful Wishbone transactions. -data Transaction addrW selWidth a - = WriteSuccess (WishboneM2S addrW selWidth a) (WishboneS2M a) - | ReadSuccess (WishboneM2S addrW selWidth a) (WishboneS2M a) - | Error (WishboneM2S addrW selWidth a) - | Retry (WishboneM2S addrW selWidth a) - | Stall (WishboneM2S addrW selWidth a) - | Ignored (WishboneM2S addrW selWidth a) - | Illegal (WishboneM2S addrW selWidth a) (WishboneS2M a) - deriving (Generic) - --- | Show Instance for 'Transaction' that hides fields irrelevant for the transaction. -instance (KnownNat addrW, Show a) => Show (Transaction addrW selWidth a) where - show (WriteSuccess WishboneM2S{..} _) = - "WriteSuccess: (addr: " - <> show addr - <> ", writeData:" - <> show writeData - <> ")" - show (ReadSuccess WishboneM2S{..} WishboneS2M{..}) = - "ReadSuccess: (" - <> show addr - <> ", " - <> show readData - <> ")" - show (Error _) = "Error" - show (Retry _) = "Retry" - show (Stall _) = "Stall" - show (Illegal _ _) = "Illegal" - show (Ignored _) = "Ignored" - --- | Show Instance for 'Transaction' that hides fields irrelevant for the transaction. -instance (KnownNat addrW, KnownNat selWidth, ShowX a) => ShowX (Transaction addrW selWidth a) where - showX (WriteSuccess WishboneM2S{..} _) = - "WriteSuccess: (addr: " - <> showX addr - <> ", writeData:" - <> showX writeData - <> ")" - showX (ReadSuccess WishboneM2S{..} WishboneS2M{..}) = - "ReadSuccess: (" - <> showX addr - <> ", " - <> showX readData - <> ")" - showX (Error _) = "Error" - showX (Retry _) = "Retry" - showX (Stall _) = "Stall" - showX (Illegal _ _) = "Illegal" - showX (Ignored _) = "Ignored" - -{- | Equality instance for 'Transaction' that only looks at the fields relevant for the -transaction (e.g. 'writeData' is not relevant during a read transaction). --} -instance - (KnownNat addrW, KnownNat selWidth, Eq a, NFDataX a) => - Eq (Transaction addrW selWidth a) - where - (WriteSuccess mA _) == (WriteSuccess mB _) = - checkField "addr" addr mA mB - && checkField "buSelect" busSelect mA mB - && checkField "writeData" writeData mA mB - (ReadSuccess mA sA) == (ReadSuccess mB sB) = - checkField "addr" addr mA mB - && checkField "busSelect" busSelect mA mB - && checkField "readData" readData sA sB - (Error _) == (Error _) = True - (Retry _) == (Retry _) = True - (Stall _) == (Stall _) = True - (Illegal _ _) == (Illegal _ _) = True - (Ignored _) == (Ignored _) = True - _ == _ = False - -checkField :: (NFDataX a, Eq a) => String -> (t -> a) -> t -> t -> Bool -checkField str f a b - | hasUndefined (f a) || hasUndefined (f b) = - deepErrorX ("checkField: " <> str <> ", is undefined for one of the transactions.") - | otherwise = f a == f b - -{- | Consumes a list of 'WishboneM2S' requests and a list of 'WishboneS2M' responses -and transforms them to a list of 'Transaction'. --} -wbToTransaction :: - (Eq a, KnownNat addressWidth, KnownNat selWidth, ShowX a) => - [WishboneM2S addressWidth selWidth a] -> - [WishboneS2M a] -> - [Transaction addressWidth selWidth a] -wbToTransaction (m@WishboneM2S{..} : restM) (s@WishboneS2M{..} : restS) - | not strobe || not busCycle = nextTransaction - | hasMultipleTrues [acknowledge, err, retry, stall] = Illegal m s : nextTransaction - | acknowledge && writeEnable = WriteSuccess m s : nextTransaction - | acknowledge = ReadSuccess m s : nextTransaction - | err = Error m : nextTransaction - | retry = Retry m : nextTransaction - | stall = Stall m : nextTransaction - | Wb.busCycle nextM && Wb.strobe nextM = nextTransaction - | otherwise = Ignored m : nextTransaction - where - nextM = L.head restM - nextTransaction = wbToTransaction restM restS - hasMultipleTrues :: [Bool] -> Bool - hasMultipleTrues [] = False - hasMultipleTrues [_] = False - hasMultipleTrues (b0 : (b1 : brest)) - | b0 && b1 = True - | otherwise = hasMultipleTrues ((b0 || b1) : brest) -wbToTransaction _ _ = [] - --- | Take a wishbone master and a wishbone slave and return their transactions. -exposeWbTransactions :: - (KnownDomain dom, Eq a, KnownNat addrW, ShowX a, KnownNat (BitSize a)) => - Maybe Int -> - Circuit () (Wishbone dom mode addrW a) -> - Circuit (Wishbone dom mode addrW a) () -> - [Transaction addrW (DivRU (BitSize a) 8) a] -exposeWbTransactions maybeSampleLength (Circuit master) (Circuit slave) = - let ~((), m2s) = master ((), s2m) - ~(s2m, ()) = slave (m2s, ()) - in uncurry wbToTransaction $ L.unzip $ sampleF $ bundle (m2s, s2m) - where - sampleF = case maybeSampleLength of - Just n -> sampleN_lazy n - Nothing -> sample_lazy - --- | Transform a `WishboneMasterRequest` into `WishboneM2S` -wbMasterRequestToM2S :: - forall addrW a. - ( KnownNat addrW - , KnownNat (BitSize a) - , NFDataX a - ) => - WishboneMasterRequest addrW a -> - WishboneM2S addrW (DivRU (BitSize a) 8) a -wbMasterRequestToM2S = \case - Read i busSelect -> - (emptyWishboneM2S @addrW @a) - { addr = resize (pack i) - , busCycle = True - , strobe = True - , busSelect = busSelect - , writeEnable = True - } - Write i busSelect a -> - (emptyWishboneM2S @addrW @a) - { addr = resize (pack i) - , busCycle = True - , strobe = True - , busSelect = busSelect - , writeEnable = True - , writeData = a - } - -{- | Consumes a list of 'RamOp's and a list of corresponding results @a@ and transforms -them into a list of 'Transaction's. --} -wbOpToTransaction :: - forall addrW a. - ( KnownNat addrW - , KnownNat (BitSize a) - , NFDataX a - ) => - WishboneMasterRequest addrW a -> - a -> - Transaction addrW (DivRU (BitSize a) 8) a -wbOpToTransaction ramOp response = case ramOp of - Read _ _ -> ReadSuccess wbM2S slaveResponse - Write _ _ _ -> WriteSuccess wbM2S slaveResponse - where - wbM2S = wbMasterRequestToM2S ramOp - slaveResponse = (emptyWishboneS2M @a){acknowledge = True, readData = response} - -validateWb :: - forall dom aw bs. - (HasCallStack, HiddenClockResetEnable dom, KnownNat aw, KnownNat bs) => - Signal dom (WishboneM2S aw bs (Bytes bs)) -> - Signal dom (WishboneS2M (Bytes bs)) -> - (Signal dom (WishboneM2S aw bs (Bytes bs)), Signal dom (WishboneS2M (Bytes bs))) -validateWb m2s0 s2m0 = (m2s1, s2m1) - where - validate = toSignals $ validatorCircuit @dom @aw @(Bytes bs) - (s2m1, m2s1) = - case divWithRemainder @bs @8 @7 of - Dict -> - validate (m2s0, s2m0) - --- | Satisfies implicit control signal constraints by using default values. -wcre :: (KnownDomain dom) => ((HiddenClockResetEnable dom) => r) -> r -wcre = withClockResetEnable clockGen resetGen enableGen - --- | Make any @a@ into a non-repeating `ValidEntry` without repetition bits. -nonRepeatingEntry :: a -> ValidEntry a 0 -nonRepeatingEntry a = ValidEntry{veEntry = a, veRepeat = 0} diff --git a/bittide/tests/Tests/StabilityChecker.hs b/bittide/tests/Tests/StabilityChecker.hs deleted file mode 100644 index 239b9a8dc..000000000 --- a/bittide/tests/Tests/StabilityChecker.hs +++ /dev/null @@ -1,81 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE OverloadedStrings #-} - -module Tests.StabilityChecker where - -import Clash.Prelude hiding (someNatVal, (^)) -import Prelude ((^)) - -import Clash.Hedgehog.Sized.Signed (genSigned) -import Hedgehog -import Test.Tasty -import Test.Tasty.Hedgehog - -import Bittide.ClockControl -import Bittide.ClockControl.StabilityChecker -import Tests.Shared - -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range - -tests :: TestTree -tests = - testGroup - "Tests.StabilityChecker" - [ testPropertyNamed - "stabilityCheckerTest behaves the same as its golden reference" - "stabilityCheckerTest" - stabilityCheckerTest - ] - -stabilityCheckerTest :: Property -stabilityCheckerTest = property $ do - dataCountBits <- forAll $ Gen.integral $ Range.linear 2 128 - cyclesStable <- forAll $ Gen.integral $ Range.linear 1 128 - margin <- forAll $ Gen.integral $ Range.linear 0 (2 ^ dataCountBits - 1) - - -- Convert generated variables to type level ones - case ( someSNat dataCountBits - , someSNat (cyclesStable - 1) - , someSNat margin - ) of - (SomeSNat sDataCountBits, SomeSNat sCyclesStable, SomeSNat sMargin) -> - prop sDataCountBits (succSNat sCyclesStable) sMargin - where - -- Property locked to arbitrary type level values - prop :: - forall cyclesStable dataCountBits margin. - (1 <= cyclesStable) => - SNat dataCountBits -> - SNat cyclesStable -> - SNat margin -> - PropertyT IO () - prop SNat sCyclesStable@SNat sMargin@SNat = do - simLength <- forAll $ Gen.integral (Range.linear 4 1024) - dataCounts <- - forAll - $ Gen.list - (Range.singleton simLength) - (genSigned @_ @dataCountBits Range.constantBounded) - let - topEntity = wcre $ fmap stable . stabilityChecker @System sMargin sCyclesStable - simOut = simulateN simLength topEntity dataCounts - - simOut === golden (snatToNum sMargin) (snatToNum sCyclesStable) dataCounts - - -- 'stabilityChecker' reference design - golden :: forall n. (KnownNat n) => Integer -> Integer -> [RelDataCount n] -> [Bool] - golden margin cyclesStable dataCounts = - f - (0, fromIntegral (targetDataCount :: RelDataCount n)) - (fmap fromIntegral dataCounts) - where - f _ [] = [] - f (!cnt, target) (x : xs) - | inMargin && cnt >= cyclesStable = True : f (cnt + 1, target) xs - | inMargin = False : f (cnt + 1, target) xs - | otherwise = False : f (0, x) xs - where - inMargin = abs (x - target) <= margin diff --git a/bittide/tests/Tests/Switch.hs b/bittide/tests/Tests/Switch.hs deleted file mode 100644 index fb75301eb..000000000 --- a/bittide/tests/Tests/Switch.hs +++ /dev/null @@ -1,112 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GADTs #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=5 #-} - -module Tests.Switch (tests) where - -import Clash.Prelude - -import Clash.Hedgehog.Sized.Index -import Clash.Hedgehog.Sized.Vector -import Clash.Sized.Vector (unsafeFromList) -import Data.String -import Hedgehog -import Protocols.Wishbone -import Test.Tasty -import Test.Tasty.Hedgehog - -import Bittide.Calendar -import Bittide.Switch -import Tests.Calendar hiding (tests) -import Tests.Shared - -import qualified Data.Sequence as Seq -import qualified GHC.TypeNats as TN -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range -import qualified Prelude as P - -tests :: TestTree -tests = - testGroup - "Tests.Switch" - [testPropertyNamed "Routing works" "switchFrameRoutingWorks" switchFrameRoutingWorks] - -data SwitchTestConfig nBytes addrW where - SwitchTestConfig :: - (KnownNat links, 1 <= nBytes) => - CalendarConfig nBytes addrW (CalendarEntry links) -> - SwitchTestConfig nBytes addrW - -deriving instance Show (SwitchTestConfig nBytes addrW) - --- This generator can generate a calendar entry for a switch given the amount of links. -genSwitchEntry :: - forall links. - SNat links -> - Gen (ValidEntry (CalendarEntry links) 0) -genSwitchEntry SNat = genValidEntry SNat (genVec (genIndex Range.constantBounded)) - -{- | This generator can generate a calendar for the bittide switch, knowing the -amount of bytes and address width of the wishbone bus, and given the amount of links and -calendar depth of the switch. --} -genSwitchCalendar :: - forall nBytes addrW. - (KnownNat nBytes, 1 <= nBytes, KnownNat addrW) => - Natural -> - Natural -> - Gen (SwitchTestConfig nBytes addrW) -genSwitchCalendar links calDepth = do - case TN.someNatVal links of - (SomeNat (snatProxy -> l)) -> do - testCal <- genCalendarConfig calDepth $ genSwitchEntry l - return $ SwitchTestConfig testCal - --- | This test checks that for any switch calendar all outputs select the correct frame. -switchFrameRoutingWorks :: Property -switchFrameRoutingWorks = property $ do - links <- forAll $ Gen.int (Range.constant 1 15) - calDepth <- forAll $ Gen.enum 2 8 - switchCal <- forAll $ genSwitchCalendar @4 @32 (fromIntegral links) calDepth - case switchCal of - SwitchTestConfig calConfig@(CalendarConfig _ (toList . fmap (toList . veEntry) -> cal) _) -> do - simLength <- forAll $ Gen.enum 1 (2 * fromIntegral calDepth) - let - genFrame = Just <$> genDefinedBitVector @64 - allLinks = Gen.list (Range.singleton links) genFrame - topEntityInput <- forAll $ Gen.list (Range.singleton simLength) allLinks - let - topEntity streamsIn = - withClockResetEnable clockGen resetGen enableGen - $ bundle - $ fst - $ switch - calConfig - (pure emptyWishboneM2S) - $ unbundle streamsIn - simOut = simulateN @System simLength topEntity $ fmap unsafeFromList topEntityInput - let - expectedFrames = P.replicate links Nothing : topEntityInput - expectedOutput = - P.take simLength $ P.replicate links Nothing - : P.zipWith selectAllOutputs expectedFrames (cycle cal) - footnote . fromString $ "expected:" <> showX expectedOutput - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "input: " <> showX topEntityInput - fmap toList simOut === expectedOutput - -selectAllOutputs :: - (KnownNat l) => - [Maybe a] -> - [Index (l + 1)] -> - [Maybe a] -selectAllOutputs incomingFrames = fmap (selectionFunc . fromEnum) - where - allFrames = Nothing Seq.<| Seq.fromList incomingFrames - selectionFunc = (allFrames `Seq.index`) diff --git a/bittide/tests/Tests/Transceiver.hs b/bittide/tests/Tests/Transceiver.hs deleted file mode 100644 index 044b4863d..000000000 --- a/bittide/tests/Tests/Transceiver.hs +++ /dev/null @@ -1,461 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -Wno-orphans #-} - -module Tests.Transceiver where - -import Clash.Explicit.Prelude hiding (PeriodToCycles) -import Clash.Prelude (withClock) -import Hedgehog - -import Bittide.Arithmetic.Time (PeriodToCycles) -import Bittide.SharedTypes (Bytes) -import Clash.Annotations.Primitive (dontTranslate) -import Clash.Cores.Xilinx.GTH (GthCore) -import Clash.Hedgehog.Sized.Index (genIndex) -import Clash.Signal.Internal (Signal ((:-))) -import Data.Maybe (fromMaybe) -import Data.Proxy (Proxy (Proxy)) -import Data.Sequence (Seq ((:<|), (:|>))) -import Test.Tasty (TestTree, adjustOption, testGroup) -import Test.Tasty.Hedgehog (HedgehogTestLimit (HedgehogTestLimit), testPropertyNamed) - -import qualified Bittide.Transceiver as Transceiver -import qualified Bittide.Transceiver.ResetManager as ResetManager -import qualified Bittide.Transceiver.WordAlign as WordAlign -import qualified Data.List as List -import qualified Data.Sequence as Seq -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range - -createDomain vSystem{vName = "RefIsUnused"} - --- Note that these domains are a factor of ~5_000 slower than we use in practise. We --- do this because timeouts in the transceivers are specified in milliseconds, which --- makes the simulation simply do "nothing" for a while. Also note the frequencies --- of domain A and B are slightly different - which is a realistic property of --- clocks - though the order of difference is very much exaggerated. -createDomain vSystem{vName = "A", vResetKind = Synchronous, vPeriod = hzToPeriod 60e3} -createDomain vSystem{vName = "B", vResetKind = Synchronous, vPeriod = hzToPeriod 61e3} - -{- | The free domain should be able to run at any speed, so we specify one that is -much slower than the A/B domains, one that is approximately the same, and one -that is much faster. --} -createDomain - vSystem{vName = "FreeSlow", vResetKind = Synchronous, vPeriod = hzToPeriod 10e3} -createDomain vSystem{vName = "Free", vResetKind = Synchronous, vPeriod = hzToPeriod 60e3} -createDomain - vSystem{vName = "FreeFast", vResetKind = Synchronous, vPeriod = hzToPeriod 120e3} - -{- | A signal that changes every cycle. It is used to simulate the RX's data when -it's receiving nothing. In theory this is white noise, in practise its a -rotating bit pattern. --} -noise :: (KnownDomain dom) => Clock dom -> Signal dom (BitVector 64) -noise clk = - let c = register clk noReset enableGen 0xdead_beef_ca55_e77e (rotateL <$> c <*> 1) - in c - --- | Non-translatable, quick-to-simulate chain of \"dflipflops\"s -delaySeqN :: Natural -> a -> Signal dom a -> Signal dom a -delaySeqN 0 _dflt = id -delaySeqN 1 dflt = (dflt :-) -delaySeqN n dflt = go (Seq.fromFunction (fromIntegral n) (const dflt)) - where - go :: Seq.Seq a -> Signal dom a -> Signal dom a - go (l :<| rest) (s :- ss) = l :- go (rest :|> s) ss - go _ _ = error "delaySeqN: impossible" -{-# ANN delaySeqN dontTranslate #-} -{-# OPAQUE delaySeqN #-} - -{- | Very simple model of a GTH core. Is modelled as if the serial domains and -the word domains run at the same frequency. I.e.., it doesn't serialize at -all. --} -gthCoreMock :: - String -> - -- | Number of cycles it takes for RX's domain to be stable - Natural -> - -- | Number of cycles it takes for TX's domain to be stable - Natural -> - -- | Number of cycles it takes for TX to be out of reset - Natural -> - -- Offset - Index 8 -> - GthCore tx rx ref free tx rx (Maybe (BitVector 64)) -gthCoreMock - _name - nRxResetCycles - nTxResetCycles - nTxDoneCycles - offset - _channelName - _clockPath - rxSerial - _rxSerial - freeClk - rstAll - rstRx - txWord - _txCtrl - _refClk = - ( txSerial - , txSerial - , txClk - , rxClk - , rxWord - , pack <$> txDone - , pack <$> rxDone - , pack <$> txActive - , 0 - , 0 - , 0 - , 0 - ) - where - rxWord = - withClock rxClk - $ WordAlign.aligner WordAlign.dealignLsbFirst (pure False) (pure offset) - $ fromMaybe - <$> noise rxClk - <*> rxSerial - - txSerial = Just <$> txWord - - registerRx = register rxClk rxRstRx enableGen - registerTx = register txClk txRstAll enableGen - - rxResetCounter = registerRx nRxResetCycles (predSatZeroNatural <$> rxResetCounter) - txResetCounter = registerTx nTxResetCycles (predSatZeroNatural <$> txResetCounter) - txDoneCounter = registerTx (nTxResetCycles + nTxDoneCycles) (predSatZeroNatural <$> txDoneCounter) - - rxDone = rxResetCounter .==. 0 - txActive = txResetCounter .==. 0 - txDone = txDoneCounter .==. 0 - - txClk = clockGen - rxClk = clockGen - - txRstAll = unsafeFromActiveHigh (unsafeSynchronizer freeClk txClk (unsafeToActiveHigh rstAll)) - rxRstAll = unsafeFromActiveHigh (unsafeSynchronizer freeClk rxClk (unsafeToActiveHigh rstAll)) - rxRstRx = - unsafeOrReset - rxRstAll - (unsafeFromActiveHigh (unsafeSynchronizer freeClk rxClk (unsafeToActiveHigh rstRx))) - - predSatZeroNatural :: Natural -> Natural - predSatZeroNatural 0 = 0 - predSatZeroNatural n = n - 1 - -data Input tx rx = Input - { dat :: Signal tx (BitVector 64) - , txReady :: Signal tx Bool - , rxReady :: Signal rx Bool - } - -dut :: - forall freeA freeB txA txB ref n. - ( KnownDomain ref - , HasSynchronousReset txA - , HasDefinedInitialValues txA - , HasSynchronousReset txB - , HasDefinedInitialValues txB - , HasSynchronousReset freeA - , HasDefinedInitialValues freeA - , HasSynchronousReset freeB - , HasDefinedInitialValues freeB - ) => - -- | Number of word clock cycles delay from A -> B - Natural -> - -- | Number of word clock cycles delay from B -> A - Natural -> - ResetManager.Config -> - GthCore txA txB ref freeA txA txB (Maybe (Bytes n)) -> - GthCore txB txA ref freeB txB txA (Maybe (Bytes n)) -> - Clock freeA -> - Reset freeA -> - Clock freeB -> - Reset freeB -> - Input txA txB -> - Input txB txA -> - ( Transceiver.Output txA txB txA freeA (Maybe (Bytes n)) - , Transceiver.Output txB txA txB freeB (Maybe (Bytes n)) - ) -dut - abDelay - baDelay - resetManagerConfig - gthCoreA - gthCoreB - freeClkA - freeRstA - freeClkB - freeRstB - inputA - inputB = (outputA, outputB) - where - outputA = - Transceiver.transceiverPrbsWith - gthCoreA - Transceiver.defConfig{Transceiver.resetManagerConfig} - Transceiver.Input - { clock = freeClkA - , reset = freeRstA - , refClock = error "A: refClock not used in simulation" - , transceiverIndex = 0 - , channelName = "A" - , clockPath = "clkA" - , rxN = delaySeqN baDelay Nothing outputB.txN - , rxP = error "A: rxP not used in simulation" - , txData = inputA.dat - , txReady = inputA.txReady - , rxReady = inputA.rxReady - } - - outputB = - Transceiver.transceiverPrbsWith - gthCoreB - Transceiver.defConfig{Transceiver.resetManagerConfig} - Transceiver.Input - { clock = freeClkB - , reset = freeRstB - , refClock = error "B: refClock not used in simulation" - , transceiverIndex = 1 - , channelName = "B" - , clockPath = "clkB" - , rxN = delaySeqN abDelay Nothing outputA.txN - , rxP = error "B: rxP not used in simulation" - , txData = inputB.dat - , txReady = inputB.txReady - , rxReady = inputB.rxReady - } - -type DutTestFunc txA txB free = - Transceiver.Output txA txB txA free (Maybe (BitVector 64)) -> - Transceiver.Output txB txA txB free (Maybe (BitVector 64)) -> - PropertyT IO () - -type InputFunc txA txB free = - Transceiver.Output txA txB txA free (Maybe (BitVector 64)) -> - Input txA txB - -dutRandomized :: - forall txA txB free. - ( HasSynchronousReset txA - , HasDefinedInitialValues txA - , HasSynchronousReset txB - , HasDefinedInitialValues txB - , HasSynchronousReset free - , HasDefinedInitialValues free - ) => - DutTestFunc txA txB free -> - InputFunc txA txB free -> - InputFunc txB txA free -> - -- Input txA txB -> - -- Input txB txA -> - Proxy (free :: Domain) -> - Property -dutRandomized f inputA inputB Proxy = property $ do - -- Note that the maximum timeout should be below 'ResetManager.txTimeoutMs' and - -- 'ResetManager.rxTimeoutMs'. - let genTimeoutMax = Gen.word64 (Range.linear 0 (natToNum @(PeriodToCycles A (Microseconds 500)))) - rxStableA <- fromIntegral <$> forAll genTimeoutMax - txStableA <- fromIntegral <$> forAll genTimeoutMax - txDoneA <- fromIntegral <$> forAll genTimeoutMax - rxStableB <- fromIntegral <$> forAll genTimeoutMax - txStableB <- fromIntegral <$> forAll genTimeoutMax - txDoneB <- fromIntegral <$> forAll genTimeoutMax - - -- XXX: Note that a single, static offset is generated. This is not realistic: - -- in practise, the offset is random, and "determined" after resetting the - -- rx subsystem. We currently don't rely on this behavior, due to the logic - -- in "Bittide.Transceiver.WordAlign". - aOffset <- forAll (genIndex Range.constantBounded) - bOffset <- forAll (genIndex Range.constantBounded) - - -- A cable of 1km "stores" 42 words of 64 bits. In theory these links can be - -- assymetric, although they rarely are in practise. - abDelay <- fromIntegral <$> forAll (Gen.word64 (Range.linear 0 42)) - baDelay <- fromIntegral <$> forAll (Gen.word64 (Range.linear 0 42)) - - let - -- XXX: Randomization of the reset manager is possible, but greatly slows down - -- simulation if done naively, as the two transceiver's reset managers can - -- end up in a "dance" where they keep resetting each other. We should - -- investigate how to randomize. - resetManagerConfig = ResetManager.defConfig - - gthCoreA = gthCoreMock "A" rxStableA txStableA txDoneA aOffset - gthCoreB = gthCoreMock "B" rxStableB txStableB txDoneB bOffset - - let - (outputA, outputB) = - dut - @free - @free - @txA - @txB - @RefIsUnused - @8 - abDelay - baDelay - resetManagerConfig - gthCoreA - gthCoreB - (clockGen @free) - (resetGen @free) - (clockGen @free) - (resetGen @free) - (inputA outputA) - (inputB outputB) - - f outputA outputB - --- | Tests whether the link is up within 500 milliseconds -testUp500ms :: DutTestFunc txA txB free -testUp500ms outputA outputB = - case sampledAfterStable of - [] -> failure - (List.map snd -> ups) -> do - let n = 100 - List.take n ups === List.replicate n True - where - linksUp = outputA.linkUp .&&. outputB.linkUp - nCycles = fromIntegral (maxBound :: Index (PeriodToCycles Free (Milliseconds 500))) - sampledLinksUp = sampleN nCycles linksUp - sampledAfterStable = List.dropWhile (not . snd) (List.zip [(0 :: Int) ..] sampledLinksUp) - --- | Test whether the link is never up within 500 milliseconds -testNeitherUp500ms :: DutTestFunc txA txB free -testNeitherUp500ms outputA outputB = - False === or (sampleN nCycles linksUp) - where - linksUp = outputA.linkUp .||. outputB.linkUp - nCycles = fromIntegral (maxBound :: Index (PeriodToCycles Free (Milliseconds 500))) - --- | Test whether the link is never up within 500 milliseconds -testNotBothUp500ms :: DutTestFunc txA txB free -testNotBothUp500ms outputA outputB = - False === or (sampleN nCycles linksUp) - where - linksUp = (outputA.linkUp .==. pure True) .&&. (outputB.linkUp .==. pure True) - nCycles = fromIntegral (maxBound :: Index (PeriodToCycles Free (Milliseconds 500))) - --- Input that applies no (back)pressure on the link -noPressureInput :: InputFunc txA txB free -noPressureInput _ = Input{dat = complement <$> 0, txReady = pure True, rxReady = pure True} - --- Input that never sets 'txReady' to 'True' -noTxReadyInput :: InputFunc txA txB free -noTxReadyInput i = (noPressureInput i){txReady = pure False} - --- Input that never sets 'rxReady' to 'True' -noRxReadyInput :: InputFunc txA txB free -noRxReadyInput i = (noPressureInput i){rxReady = pure False} - -{- | Check whether handshake works when there is no pressure on the link, -specialized to 'A', 'A', 'FreeSlow'. --} -prop_noPressure_A_A_FreeSlow :: Property -prop_noPressure_A_A_FreeSlow = - dutRandomized @A @A @FreeSlow testUp500ms noPressureInput noPressureInput Proxy - -{- | Check whether handshake works when there is no pressure on the link, -specialized to 'A', 'A', 'Free'. --} -prop_noPressure_A_A_Free :: Property -prop_noPressure_A_A_Free = - dutRandomized @A @A @Free testUp500ms noPressureInput noPressureInput Proxy - -{- | Check whether handshake works when there is no pressure on the link, -specialized to 'A', 'A', 'FreeFast'. --} -prop_noPressure_A_A_FreeFast :: Property -prop_noPressure_A_A_FreeFast = - dutRandomized @A @A @FreeFast testUp500ms noPressureInput noPressureInput Proxy - -{- | Check whether handshake works when there is no pressure on the link, -specialized to 'A', 'B', 'Free'. --} -prop_noPressure_A_B_Free :: Property -prop_noPressure_A_B_Free = - dutRandomized @A @B @Free testUp500ms noPressureInput noPressureInput Proxy - -{- | Check whether handshake works when there is no pressure on the link, -specialized to 'B', 'A', 'Free'. --} -prop_noPressure_B_A_Free :: Property -prop_noPressure_B_A_Free = - dutRandomized @B @A @Free testUp500ms noPressureInput noPressureInput Proxy - -{- | Check whether neither handshake works when one of the transceivers never -indicates it's ready to the other. --} -prop_noTxReady :: Property -prop_noTxReady = - dutRandomized @A @A @Free testNeitherUp500ms noPressureInput noTxReadyInput Proxy - --- | Same as 'prop_noTxReady', but with the transceivers flipped -prop_noTxReadyFlipped :: Property -prop_noTxReadyFlipped = - dutRandomized @A @A @Free testNeitherUp500ms noTxReadyInput noPressureInput Proxy - -{- | Check whether neither node indicates it's up, when it never transitions to -sending user data. --} -prop_noRxReady :: Property -prop_noRxReady = - dutRandomized @A @A @Free testNeitherUp500ms noRxReadyInput noRxReadyInput Proxy - --- TODO: Add tests for actual data transmission. This currently only happens in --- hardware, as we currently don't have the infrastructure to memory-efficiently --- test multi-domain systems in Clash.. - -tests :: TestTree -tests = - -- XXX: The number of tests we run is very low, due to the time it takes to - -- execute them. - testGroup - "Transceiver" - [ adjustOption (\_ -> HedgehogTestLimit (Just 100)) - $ testGroup - "Slow tests" - [ testPropertyNamed - "prop_noPressure_A_A_FreeSlow" - "prop_noPressure_A_A_FreeSlow" - prop_noPressure_A_A_FreeSlow - , testPropertyNamed - "prop_noPressure_A_A_Free" - "prop_noPressure_A_A_Free" - prop_noPressure_A_A_Free - , testPropertyNamed - "prop_noPressure_A_A_FreeFast" - "prop_noPressure_A_A_FreeFast" - prop_noPressure_A_A_FreeFast - , testPropertyNamed - "prop_noPressure_A_B_Free" - "prop_noPressure_A_B_Free" - prop_noPressure_A_B_Free - , testPropertyNamed - "prop_noPressure_B_A_Free" - "prop_noPressure_B_A_Free" - prop_noPressure_B_A_Free - ] - , adjustOption (\_ -> HedgehogTestLimit (Just 10)) - $ testGroup - "Very slow tests" - -- These tests are "very slow" because they cannot exit early on some - -- condition. Instead, they just wait for some deadline, and if they don't - -- see an errornous condition by then, they pass. - [ testPropertyNamed "prop_noTxReady" "prop_noTxReady" prop_noTxReady - , testPropertyNamed "prop_noTxReadyFlipped" "prop_noTxReadyFlipped" prop_noTxReadyFlipped - , testPropertyNamed "prop_noRxReady" "prop_noRxReady" prop_noRxReady - ] - ] diff --git a/bittide/tests/Tests/Transceiver/Prbs.hs b/bittide/tests/Tests/Transceiver/Prbs.hs deleted file mode 100644 index 63d1d8979..000000000 --- a/bittide/tests/Tests/Transceiver/Prbs.hs +++ /dev/null @@ -1,134 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GADTs #-} -{-# LANGUAGE OverloadedStrings #-} - -module Tests.Transceiver.Prbs where - -import Clash.Explicit.Prelude -import Clash.Prelude (withClock) - -import Clash.Hedgehog.Sized.Index (genIndex) - -import Hedgehog -import Test.Tasty -import Test.Tasty.Hedgehog -import Tests.Shared - -import qualified Bittide.Transceiver.Prbs as Prbs -import qualified Bittide.Transceiver.WordAlign as WordAlign -import qualified Data.List as L -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range - -data SomePrbsConfig where - SomePrbsConfig :: - (KnownNat nBytes) => - Prbs.Config polyLength polyTap (8 * nBytes) -> - SomePrbsConfig - -instance Show SomePrbsConfig where - show (SomePrbsConfig (Prbs.Config :: Prbs.Config polyLength polyTap (8 * nBytes))) = - "SomePrbsConfig (Prbs.Config" - <> "{polyLength=" - <> show (snatToNatural (SNat :: SNat polyLength)) - <> ", polyTap=" - <> show (snatToNatural (SNat :: SNat polyTap)) - <> ", nBytes=" - <> show (snatToNatural (SNat :: SNat nBytes)) - <> "})" - -{- | Generate a 'SomePrbsConfig' such that: - -* 1 <= nBits <= (n + 1) -* 1 <= polyTap <= (n + 1) -* (polyTap + 1) <= polyLength <= (polyTap + 1 + n + 1) --} -genSomePrbsConfig :: Natural -> Gen SomePrbsConfig -genSomePrbsConfig n = do - n0 <- Gen.integral (Range.linear 0 n) - n1 <- Gen.integral (Range.linear 0 n) - n2 <- Gen.integral (Range.linear 0 n) - - case (someSNat n0, someSNat n1, someSNat n2) of - (SomeSNat sn0, SomeSNat sn1, SomeSNat sn2) -> - let - nBytes = sn0 `addSNat` d1 - polyTap = sn1 `addSNat` d1 - polyLength = polyTap `addSNat` d1 `addSNat` sn2 - in - case (nBytes, polyTap, polyLength) of - (SNat :: SNat nBytes, SNat :: SNat polyTap, SNat :: SNat polyLength) -> - pure $ SomePrbsConfig (Prbs.Config @polyLength @polyTap @(8 * nBytes)) - --- | Number of cycles to check for PRBS errors after the PRBS checker is in sync -checkOk :: Int -checkOk = 8 - -{- | Connect a PRBS generator to a PRBS checker and check that no errors are -detected after the expected time it takes for the checker to align with the -generator. --} -prop_happy :: Property -prop_happy = property $ do - SomePrbsConfig config@Prbs.Config{} <- forAll (genSomePrbsConfig 100) - case config of - (Prbs.Config :: Prbs.Config polyLength polyTap nBits) -> do - offset <- forAll (genIndex Range.constantBounded) - let - resetCycle = 1 - alignCycle = 1 -- if offset == 0 then 0 else 1 - stateCycles = natToNum @(polyLength `DivRU` nBits) - expectAlignmentAfterNCycles = stateCycles + resetCycle + alignCycle - - nNoiseCycles <- forAll (Gen.integral (Range.linear 0 (10 * expectAlignmentAfterNCycles))) - noise <- forAll (Gen.list (Range.singleton nNoiseCycles) genDefinedBitVector) - - let - clk = clockGen @XilinxSystem - rst = noReset - ena = enableGen - - prbs = Prbs.generator clk rst ena config - noiseCounter = register clk rst ena (0 :: Int) (noiseCounter + 1) - sendNoise = noiseCounter .<. pure nNoiseCycles - noiseOrPrbs = mux sendNoise (fromList (noise <> L.repeat 0)) prbs - noiseOrPrbsDealigned = - withClock clk - $ WordAlign.aligner WordAlign.dealignLsbFirst (pure False) (pure offset) noiseOrPrbs - errors = Prbs.checker clk rst ena config noiseOrPrbsDealigned - - okAfter = fromIntegral (nNoiseCycles + expectAlignmentAfterNCycles) - (notOk, ok) = L.splitAt okAfter (sample errors) - - -- Note the first three cycles are always reported as errornous due to - -- start up behavior - anyNotOk = L.any (/= 0) (L.drop 3 notOk) - allOk = not anyNotOk - - L.take checkOk ok === L.replicate checkOk 0 - - -- Statistics checks - cover - 20 - "nNoiseCycles > expectAlignmentAfterNCycles" - (nNoiseCycles > expectAlignmentAfterNCycles) - - cover - 20 - "nNoiseCycles <= expectAlignmentAfterNCycles" - (nNoiseCycles <= expectAlignmentAfterNCycles) - - cover 70 "any error detected after 3 cycles" anyNotOk - cover 2 "no errors detected after 3 cycles" allOk -- detect "always pass" - -tests :: TestTree -tests = - testGroup - "Prbs" - [ testPropertyNamed "prop_happy" "prop_happy" prop_happy - ] - -main :: IO () -main = defaultMain tests diff --git a/bittide/tests/Tests/Transceiver/WordAlign.hs b/bittide/tests/Tests/Transceiver/WordAlign.hs deleted file mode 100644 index 8de972322..000000000 --- a/bittide/tests/Tests/Transceiver/WordAlign.hs +++ /dev/null @@ -1,235 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE UndecidableInstances #-} - -module Tests.Transceiver.WordAlign where - -import Clash.Prelude hiding (someNatVal, withSomeSNat, words) - -import Bittide.SharedTypes (Byte) -import Clash.Hedgehog.Sized.BitVector (genDefinedBitVector) -import Clash.Hedgehog.Sized.Index (genIndex) -import Data.Proxy (Proxy (..)) -import GHC.TypeNats (someNatVal) -import Hedgehog -import Numeric (showHex) -import Test.Tasty -import Test.Tasty.HUnit -import Test.Tasty.Hedgehog - -import qualified Bittide.Transceiver.WordAlign as WordAlign -import qualified Clash.Explicit.Prelude as E -import qualified Data.Char as Char -import qualified Data.List as L -import qualified Data.List.Extra as L -import qualified Hedgehog.Gen as Gen -import qualified Hedgehog.Range as Range - -newtype HexByte = HexByte Byte - deriving (Eq, Ord, Generic) - deriving newtype (Num, BitPack, NFDataX) - -instance Show HexByte where - show (HexByte b) = "0x" <> L.map Char.toUpper (showHex b "") - -withSomeSNat :: Natural -> (forall (n :: Nat). SNat n -> r) -> r -withSomeSNat n f = case someNatVal n of - SomeNat (_ :: Proxy n) -> f (SNat @n) - -word1 :: (HexByte, HexByte, HexByte, HexByte) -word1 = (HexByte 0x0, HexByte 0x1, HexByte 0x2, HexByte 0x3) - -word2 :: (HexByte, HexByte, HexByte, HexByte) -word2 = (HexByte 0xA, HexByte 0xB, HexByte 0xC, HexByte 0xD) - -case_dealignLsbFirst :: Assertion -case_dealignLsbFirst = do - (0x0, 0x1, 0x2, 0x3) @=? go 0 - (0xD, 0x0, 0x1, 0x2) @=? go 1 - (0xC, 0xD, 0x0, 0x1) @=? go 2 - (0xB, 0xC, 0xD, 0x0) @=? go 3 - where - go :: Index 4 -> (HexByte, HexByte, HexByte, HexByte) - go offset = unpack (WordAlign.dealignLsbFirst offset (pack word1) (pack word2)) - -case_alignLsbFirst :: Assertion -case_alignLsbFirst = do - (0xA, 0xB, 0xC, 0xD) @=? go 0 - (0xB, 0xC, 0xD, 0x0) @=? go 1 - (0xC, 0xD, 0x0, 0x1) @=? go 2 - (0xD, 0x0, 0x1, 0x2) @=? go 3 - where - go :: Index 4 -> (HexByte, HexByte, HexByte, HexByte) - go offset = unpack (WordAlign.alignLsbFirst offset (pack word1) (pack word2)) - -case_dealignMsbFirst :: Assertion -case_dealignMsbFirst = do - (0xA, 0xB, 0xC, 0xD) @=? go 0 - (0x3, 0xA, 0xB, 0xC) @=? go 1 - (0x2, 0x3, 0xA, 0xB) @=? go 2 - (0x1, 0x2, 0x3, 0xA) @=? go 3 - where - go :: Index 4 -> (HexByte, HexByte, HexByte, HexByte) - go offset = unpack (WordAlign.dealignMsbFirst offset (pack word1) (pack word2)) - -case_alignMsbFirst :: Assertion -case_alignMsbFirst = do - (0x0, 0x1, 0x2, 0x3) @=? go 0 - (0x1, 0x2, 0x3, 0xA) @=? go 1 - (0x2, 0x3, 0xA, 0xB) @=? go 2 - (0x3, 0xA, 0xB, 0xC) @=? go 3 - where - go :: Index 4 -> (HexByte, HexByte, HexByte, HexByte) - go offset = unpack (WordAlign.alignMsbFirst offset (pack word1) (pack word2)) - -prop_alignDealign :: - (forall n. WordAlign.AlignmentFn n) -> - (forall n. WordAlign.AlignmentFn n) -> - Property -prop_alignDealign alignFn dealignFn = property $ do - nMinusOne <- forAll $ Gen.integral (Range.linear 0 16) - withSomeSNat nMinusOne $ \(succSNat -> SNat :: SNat n) -> do - offset <- forAll $ genIndex @_ @n Range.constantBounded - nCycles <- forAll $ Gen.integral (Range.linear 0 64) - input <- forAll $ Gen.list (Range.singleton nCycles) (genDefinedBitVector @_ @(8 * n)) - - withClock @XilinxSystem clockGen $ do - let - inputSignal = E.fromList (input <> L.repeat 0) - dealigned = WordAlign.aligner dealignFn (pure False) (pure offset) inputSignal - aligned = WordAlign.aligner alignFn (pure False) (pure offset) dealigned - output = E.sampleN nCycles aligned - - pipelineCycles = 1 - startUpCycles = 1 - - expected = L.dropEnd pipelineCycles (L.drop startUpCycles input) - actual = L.drop (startUpCycles + pipelineCycles) output - - expected === actual - --- | 'prop_alignDealign' with LSB-first transmission -prop_alignDealignLsb :: Property -prop_alignDealignLsb = prop_alignDealign WordAlign.alignLsbFirst WordAlign.dealignLsbFirst - --- | 'prop_alignDealign' with MSB-first transmission -prop_alignDealignMsb :: Property -prop_alignDealignMsb = prop_alignDealign WordAlign.alignMsbFirst WordAlign.dealignMsbFirst - --- Should fail: --- prop_alignDealignMsbLsb :: Property --- prop_alignDealignMsbLsb = prop_alignDealign WordAlign.alignMsbFirst WordAlign.dealignLsbFirst - --- prop_alignDealignLsbMsb :: Property --- prop_alignDealignLsbMsb = prop_alignDealign WordAlign.alignLsbFirst WordAlign.dealignMsbFirst - --- | 'prop_wordAlignFromMsbs' with LSB-first transmission -prop_wordAlignFromMsbsLsbFirst :: Property -prop_wordAlignFromMsbsLsbFirst = prop_wordAlignFromMsbs WordAlign.alignLsbFirst WordAlign.dealignLsbFirst - --- | 'prop_wordAlignFromMsbs' with MSB-first transmission -prop_wordAlignFromMsbsMsbFirst :: Property -prop_wordAlignFromMsbsMsbFirst = prop_wordAlignFromMsbs WordAlign.alignMsbFirst WordAlign.dealignMsbFirst - -{- | Make sure we can recover "user data" after sending alignment bits and freezing -the offset/alignment. --} -prop_wordAlignFromMsbs :: - (forall n. WordAlign.AlignmentFn n) -> - (forall n. WordAlign.AlignmentFn n) -> - Property -prop_wordAlignFromMsbs alignFn dealignFn = property $ do - -- Generate type level constructs for worker function - nBytesMinusOne <- forAll $ Gen.integral (Range.linear 0 16) - withSomeSNat nBytesMinusOne (go . succSNat) - where - -- Worker function that only deals with term level naturals - go :: forall nBytes. (1 <= nBytes) => SNat nBytes -> PropertyT IO () - go SNat = leToPlus @1 @nBytes $ do - -- How much offset is "inserted" by the "transceiver"s - byteOffset <- forAll $ genIndex Range.constantBounded - - -- Number of cycles before sending alignment bits - nPreAlignCycles <- forAll $ Gen.integral (Range.linear 0 16) - preAlignWords <- forAll $ Gen.list (Range.singleton nPreAlignCycles) genDefinedBitVector - - -- Number of cycles where we transmit words with alignment bits. We will - -- always generate valid alignments in each test case to simplify the - -- implemenation. As noted in the documentation of 'WordAlign.aligner', we - -- generate at least 2 cycles with alignment bits, to ensure proper decoding - -- of the words thereafter. - nAlignCycles <- forAll $ Gen.integral (Range.linear 2 16) - alignWords <- - fmap (WordAlign.joinMsbs @nBytes @8 WordAlign.alignSymbol) - <$> forAll (Gen.list (Range.singleton nAlignCycles) genDefinedBitVector) - - -- Number of cycles to produce data with (potentially) invalid alignment - -- bits. We expect to be able to recover these words, because the aligner - -- should have used the last valid alignment bits. - nPostAlignCycles <- forAll $ Gen.integral (Range.linear 0 16) - postAlignWords <- forAll $ Gen.list (Range.singleton nPostAlignCycles) genDefinedBitVector - - withClock @XilinxSystem clockGen $ do - let - nCycles = nPreAlignCycles + nAlignCycles + nPostAlignCycles - allWords = E.fromList (preAlignWords <> alignWords <> postAlignWords <> L.repeat 0) - allDealignedWords = WordAlign.aligner dealignFn (pure False) (pure byteOffset) allWords - - pipelineDepth = 1 - - freeze = - L.replicate nPreAlignCycles False - <> L.replicate nAlignCycles False - <> L.replicate nPostAlignCycles True - - sampled = - E.sampleN (nCycles + pipelineDepth) - $ WordAlign.alignBytesFromMsbs @nBytes alignFn (E.fromList (freeze <> L.repeat True)) - $ allDealignedWords - - actual = - L.take nPostAlignCycles - $ L.drop (nPreAlignCycles + nAlignCycles + pipelineDepth) - $ sampled - - footnote $ "preAlignWords: " <> show preAlignWords - footnote $ "alignWords: " <> show alignWords - footnote $ "postAlignWords: " <> show postAlignWords - footnote $ "sampled: " <> show sampled - footnote - $ "allDealignedWords: " - <> show (sampleN (nCycles + pipelineDepth) allDealignedWords) - footnote $ "freeze: " <> show freeze - - postAlignWords === actual - -tests :: TestTree -tests = - testGroup - "WordAlign" - [ testCase "case_dealignLsbFirst" case_dealignLsbFirst - , testCase "case_dealignMsbFirst" case_dealignMsbFirst - , testCase "case_alignLsbFirst" case_alignLsbFirst - , testCase "case_alignMsbFirst" case_alignMsbFirst - , testPropertyNamed "prop_alignDealignLsb" "prop_alignDealignLsb" prop_alignDealignLsb - , testPropertyNamed "prop_alignDealignMsb" "prop_alignDealignMsb" prop_alignDealignMsb - , testPropertyNamed - "prop_wordAlignFromMsbsLsbFirst" - "prop_wordAlignFromMsbsLsbFirst" - prop_wordAlignFromMsbsLsbFirst - , testPropertyNamed - "prop_wordAlignFromMsbsMsbFirst" - "prop_wordAlignFromMsbsMsbFirst" - prop_wordAlignFromMsbsMsbFirst - -- While this works, it also prints the error which is very confusing :-( - -- , expectFail $ testPropertyNamed "prop_alignDealignMsbLsb" "prop_alignDealignMsbLsb" prop_alignDealignMsbLsb - -- , expectFail $ testPropertyNamed "prop_alignDealignLsbMsb" "prop_alignDealignLsbMsb" prop_alignDealignLsbMsb - ] - -main :: IO () -main = - defaultMain - $ adjustOption (const (HedgehogTestLimit (Just 1000))) tests diff --git a/bittide/tests/Tests/Wishbone.hs b/bittide/tests/Tests/Wishbone.hs deleted file mode 100644 index c712422f5..000000000 --- a/bittide/tests/Tests/Wishbone.hs +++ /dev/null @@ -1,373 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PartialTypeSignatures #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -fconstraint-solver-iterations=5 #-} -{-# OPTIONS_GHC -fplugin Protocols.Plugin #-} - -module Tests.Wishbone (tests) where - -import Clash.Prelude hiding (sample) - -import Clash.Hedgehog.Sized.Vector -import Clash.Sized.Vector (unsafeFromList) -import Data.Bifunctor -import Data.Constraint (Dict (Dict)) -import Data.Constraint.Nat.Extra (cancelMulDiv, divWithRemainder) -import Data.String -import Hedgehog -import Hedgehog.Range as Range -import Protocols -import Protocols.Df (Data (..)) -import Protocols.Hedgehog -import Protocols.Wishbone -import Protocols.Wishbone.Standard.Hedgehog (validatorCircuit) -import Test.Tasty -import Test.Tasty.Hedgehog - -import Bittide.SharedTypes -import Bittide.Wishbone -import Tests.Shared - -import qualified Data.List as L -import qualified GHC.TypeNats as TN -import qualified Hedgehog.Gen as Gen - -tests :: TestTree -tests = - testGroup - "Tests.Wishbone" - [ testPropertyNamed "Reading readData from slaves." "readingSlaves" readingSlaves - , testPropertyNamed "Writing and reading from slaves." "writingSlaves" writingSlaves - , testPropertyNamed - "Send and receive bytes via uartWb" - "uartWbCircuitTest" - uartWbCircuitTest - ] - -data UartMachineState = ReadStatus | ReadByte | WriteByte | OutputByte (BitVector 8) - deriving (Generic, NFDataX, Show) - -{- | A `Circuit` that transforms incoming `Df` transactions into `Wishbone` transactions -that are compatible with `Bittide.Wishbone.wbToDf`. --} - --- .It implements the following state machine: --- 1. Check if there is incoming data available in the `wbToDf` receive fifo, if so, skip to 4. --- 2. Check if there is space in the `wbToDf` transmit fifo. If so, skip to 6. --- 3. Go to 1. --- 4. Read in the incoming byte and send it to the outgoing Df interface. --- 5. Go to 1 --- 6. If there is no data available at the incoming Df interface, go to 1. --- 7. Write data from incoming Df interface to `wbToDf`. -uartMachine :: - forall dom addrW. - ( HiddenClockResetEnable dom - , KnownNat addrW - ) => - Circuit - (Df dom (BitVector 8)) - (Wishbone dom 'Standard addrW Byte, Df dom (BitVector 8)) -uartMachine = Circuit (second unbundle . mealyB go ReadStatus . second bundle) - where - go ReadStatus (_, ~(WishboneS2M{..}, _)) = (nextState, (Ack False, (wbOut, NoData))) - where - (rxEmpty, txFull) = unpack (resize readData) - nextState = case (acknowledge, err, (rxEmpty, txFull)) of - (True, False, (False, _)) -> ReadByte - (True, False, (True, False)) -> WriteByte - _ -> ReadStatus - wbOut = (emptyWishboneM2S @30){addr = 1, busCycle = True, strobe = True} - go ReadByte (_, ~(WishboneS2M{..}, _)) = (nextState, (Ack False, (wbOut, NoData))) - where - nextState = case (acknowledge, err) of - (True, False) -> OutputByte (resize readData) - _ -> ReadByte - wbOut = (emptyWishboneM2S @30){addr = 0, busCycle = True, strobe = True} - go (OutputByte byte) (_, ~(_, Ack dfAck)) = - (nextState, (Ack False, (emptyWishboneM2S, Data byte))) - where - nextState = if dfAck then ReadStatus else (OutputByte byte) - go (WriteByte) (Data dfData, ~(WishboneS2M{..}, _)) = (nextState, (Ack dfAck, (wbOut, NoData))) - where - (nextState, dfAck) = if acknowledge && not err then (ReadStatus, True) else (WriteByte, False) - wbOut = - (emptyWishboneM2S @30 @()) - { addr = 0 - , busCycle = True - , strobe = True - , writeEnable = True - , busSelect = 1 - , writeData = resize dfData - } - go WriteByte (NoData, _) = (ReadStatus, (Ack False, (emptyWishboneM2S, NoData))) - --- | Check if we can combine `uartWb` in loopback mode and `uartMachine` to create `id`. -uartWbCircuitTest :: Property -uartWbCircuitTest = do - let - dataGen = Gen.list (Range.linear 0 32) $ genDefinedBitVector @8 - dut :: (HiddenClockResetEnable System) => Circuit (Df System Byte) (Df System Byte) - dut = circuit $ \dfIn -> do - (wb, dfOut) <- uartMachine -< dfIn - (uartTx, _status) <- uartWb @System @32 d2 d2 (SNat @6250000) -< (wb, uartTx) - idC -< dfOut - expectOptions = - defExpectOptions - { eoResetCycles = 15 - , eoDriveEarly = True - } - idWithModel expectOptions dataGen id (wcre dut) - -{- | Generates a 'MemoryMap' for 'singleMasterInterconnect' for a specific number -of slaves. --} -genConfig :: - forall nSlaves. - (1 <= nSlaves) => - SNat nSlaves -> - Gen (MemoryMap nSlaves) -genConfig nSlaves@SNat = - unsafeFromList . L.take (snatToNum nSlaves) <$> Gen.shuffle [0 ..] - -{- | Creates a memory map with 'simpleSlave' devices and a list of read addresses and checks -if the correct 'simpleSlave' responds to the read operation. Reading outside of a 'simpleSlave' its -range should return an error. --} -readingSlaves :: Property -readingSlaves = property $ do - devices <- forAll $ Gen.enum 2 16 - case TN.someNatVal (devices - 1) of - SomeNat (succSNat . snatProxy -> devices0@SNat) -> - case compareSNat (clogBaseSNat d2 devices0) d30 of - SNatLE -> runTest devices0 - _ -> errorX "readingSlaves: number of devices can't be represented with 30 bits." - where - runTest devices = do - config <- forAll $ genConfig @_ devices - nrOfReads <- forAll $ Gen.enum 1 32 - let nrOfReadsRange = Range.singleton nrOfReads - readAddresses <- forAll $ Gen.list nrOfReadsRange (genDefinedBitVector @30) - ranges <- forAll $ genVec genDefinedBitVector - let - topEntityInput = (wbRead <$> readAddresses) <> [emptyWishboneM2S] - simOut = simulateN (L.length topEntityInput) (topEntity config ranges) topEntityInput - realTransactions = wbToTransaction topEntityInput simOut - expectedOutput = fmap (getExpected config ranges) topEntityInput - expectedTransactions = wbToTransaction topEntityInput expectedOutput - footnote . fromString $ "expectedTransactions: " <> showX expectedTransactions - footnote . fromString $ "realTransactions: " <> showX realTransactions - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "simIn: " <> showX topEntityInput - footnote . fromString $ "reads: " <> show readAddresses - footnote . fromString $ "ranges: " <> show ranges - footnote . fromString $ "config: " <> show config - realTransactions === expectedTransactions - - topEntity config ranges masterIn = toMaster - where - slaves = - withClockResetEnable @System clockGen resetGen enableGen - $ simpleSlave - <$> ranges - <*> config - <*> unbundle toSlaves - (toMaster, toSlaves) = - withClockResetEnable - clockGen - resetGen - enableGen - singleMasterInterconnect' - config - masterIn - $ bundle slaves - - getExpected :: - forall nSlaves. - ( KnownNat nSlaves - , 1 <= nSlaves - , BitSize (Unsigned (CLog 2 nSlaves)) <= 30 - , BitSize (Unsigned (CLog 2 nSlaves)) <= 30 - ) => - Vec nSlaves (Unsigned (CLog 2 nSlaves)) -> - Vec nSlaves (BitVector (30 - BitSize (Unsigned (CLog 2 nSlaves)))) -> - WishboneM2S 30 (Regs (Unsigned (CLog 2 nSlaves)) 8) (Unsigned (CLog 2 nSlaves)) -> - WishboneS2M (Unsigned (CLog 2 nSlaves)) - getExpected config ranges WishboneM2S{..} - | not commAttempt = emptyWishboneS2M - | Nothing <- maybeIndex = emptyWishboneS2M{err = True} - | Just index <- maybeIndex, not (inRange index) = emptyWishboneS2M{err = True} - | otherwise = - (emptyWishboneS2M @(Unsigned (CLog 2 nSlaves))) - { acknowledge = True - , readData = unpack indexBV - } - where - commAttempt = busCycle && strobe - maybeIndex = elemIndex (unpack indexBV) config - (indexBV :: BitVector (CLog 2 nSlaves), restAddr) = split addr - inRange index = restAddr <= (ranges !! index) - -{- | Creates a memory map with 'simpleSlave' devices and a list of write addresses and checks -that if we 'simpleSlave' responds to the read operation. Reading outside of a 'simpleSlave' its -range should return an err. --} -writingSlaves :: Property -writingSlaves = property $ do - devices <- forAll $ Gen.enum 1 16 - case TN.someNatVal (devices - 1) of - SomeNat (succSNat . snatProxy -> devices0) -> - case compareSNat (clogBaseSNat d2 devices0) d30 of - SNatLE -> runTest devices0 - _ -> errorX "readingSlaves: number of devices can't be represented with 30 bits." - where - runTest devices = do - config <- forAll $ genConfig @_ devices - nrOfWrites <- forAll $ Gen.enum 1 32 - let nrOfWritesRange = Range.singleton nrOfWrites - writeAddresses <- forAll $ Gen.list nrOfWritesRange genDefinedBitVector - ranges <- forAll $ genVec genDefinedBitVector - let - topEntityInput = L.concatMap wbWriteThenRead writeAddresses <> [emptyWishboneM2S] - simLength = L.length topEntityInput - simOut = simulateN simLength (topEntity config ranges) topEntityInput - realTransactions = wbToTransaction topEntityInput simOut - expectedOutput = fmap (getExpected config ranges) topEntityInput - expectedTransactions = wbToTransaction topEntityInput expectedOutput - footnote . fromString $ "expectedTransactions: " <> showX expectedTransactions - footnote . fromString $ "realTransactions: " <> showX realTransactions - footnote . fromString $ "simOut: " <> showX simOut - footnote . fromString $ "simIn: " <> showX topEntityInput - footnote . fromString $ "writes: " <> show writeAddresses - footnote . fromString $ "ranges: " <> show ranges - footnote . fromString $ "config: " <> show config - realTransactions === expectedTransactions - - topEntity config ranges masterIn = toMaster - where - slaves = - withClockResetEnable @System clockGen resetGen enableGen - $ simpleSlave - <$> ranges - <*> ranges - <*> unbundle toSlaves - (toMaster, toSlaves) = - withClockResetEnable - clockGen - resetGen - enableGen - singleMasterInterconnect' - config - masterIn - $ bundle slaves - - wbWriteThenRead a = [wbWrite a (resize a), wbRead a] - - getExpected :: - forall nSlaves. - ( KnownNat nSlaves - , 1 <= nSlaves - , BitSize (Unsigned (CLog 2 nSlaves)) <= 30 - , BitSize (Unsigned (CLog 2 nSlaves)) <= 30 - ) => - Vec nSlaves (Unsigned (CLog 2 nSlaves)) -> - Vec nSlaves (BitVector (30 - BitSize (Unsigned (CLog 2 nSlaves)))) -> - WishboneM2S - 30 - (DivRU (30 - BitSize (Unsigned (CLog 2 nSlaves))) 8) - (BitVector (30 - BitSize (Unsigned (CLog 2 nSlaves)))) -> - WishboneS2M (BitVector (30 - BitSize (Unsigned (CLog 2 nSlaves)))) - getExpected config ranges WishboneM2S{..} - | not commAttempt = emptyWishboneS2M - | Nothing <- maybeIndex = emptyWishboneS2M{err = True} - | Just index <- maybeIndex, not (inRange index) = emptyWishboneS2M{err = True} - | writeEnable = emptyWishboneS2M{acknowledge = True} - | otherwise = - (emptyWishboneS2M @(Unsigned (CLog 2 nSlaves))) - { acknowledge = True - , readData = restAddr - } - where - commAttempt = busCycle && strobe - maybeIndex = elemIndex (unpack indexBV) config - (indexBV :: BitVector (CLog 2 nSlaves), restAddr) = split addr - inRange index = restAddr <= (ranges !! index) - --- | transforms an address to a 'WishboneM2S' read operation. -wbRead :: - forall addressWidth a. - (KnownNat addressWidth, NFDataX a, KnownNat (BitSize a)) => - BitVector addressWidth -> - WishboneM2S addressWidth (Regs a 8) a -wbRead address = case cancelMulDiv @(Regs a 8) @8 of - Dict -> - (emptyWishboneM2S @addressWidth) - { addr = address - , strobe = True - , busCycle = True - , busSelect = maxBound - } - -{- | transforms an address to a 'WishboneM2S' write operation that writes the given address -to the given address. --} -wbWrite :: - forall addressWidth a. - (KnownNat addressWidth, NFDataX a, KnownNat (BitSize a)) => - BitVector addressWidth -> - a -> - WishboneM2S addressWidth (Regs a 8) a -wbWrite address a = - (emptyWishboneM2S @addressWidth @a) - { addr = address - , strobe = True - , busCycle = True - , writeData = a - , writeEnable = True - , busSelect = maxBound - } - -simpleSlave' :: - forall dom aw a. - (HiddenClockResetEnable dom, KnownNat aw, NFDataX a) => - BitVector aw -> - a -> - Circuit (Wishbone dom 'Standard aw a) () -simpleSlave' range readDataInit = - Circuit $ \(wbIn, ()) -> (mealy go readDataInit wbIn, ()) - where - go readData1 WishboneM2S{..} = - (readData2, (emptyWishboneS2M @a){readData, acknowledge, err}) - where - masterActive = busCycle && strobe - addrInRange = addr <= range - acknowledge = masterActive && addrInRange - err = masterActive && not addrInRange - writeOp = acknowledge && writeEnable - readData2 - | writeOp = writeData - | otherwise = readData1 - readData - | writeOp = writeData - | otherwise = readData1 - -{- | Simple wishbone slave that responds to addresses [0..range], it responds by returning -a stored value (initialized by readData0), which can be overwritten by the wishbone bus. -any read/write attempt to an address outside of the supplied range sets the err signal. --} -simpleSlave :: - forall dom aw a. - (HiddenClockResetEnable dom, KnownNat aw, BitPack a, NFDataX a, ShowX a) => - BitVector aw -> - a -> - Signal dom (WishboneM2S aw (Regs a 8) a) -> - Signal dom (WishboneS2M a) -simpleSlave range readDataInit wbIn = - case divWithRemainder @(Regs a 8) @8 @7 of - Dict -> fst $ toSignals slaveCircuit (wbIn, ()) - where - slaveCircuit = validatorCircuit |> simpleSlave' range readDataInit diff --git a/bittide/tests/UnitTests.hs b/bittide/tests/UnitTests.hs deleted file mode 100644 index e4fa57950..000000000 --- a/bittide/tests/UnitTests.hs +++ /dev/null @@ -1,71 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import Prelude - -import Test.Tasty -import Test.Tasty.Hedgehog - -import qualified Tests.Axi4 -import qualified Tests.Axi4.Generators -import qualified Tests.Axi4.Properties -import qualified Tests.Calendar -import qualified Tests.ClockControl.Si539xSpi -import qualified Tests.DoubleBufferedRam -import qualified Tests.ElasticBuffer -import qualified Tests.Haxioms -import qualified Tests.ProcessingElement.ReadElf -import qualified Tests.ScatterGather -import qualified Tests.StabilityChecker -import qualified Tests.Switch -import qualified Tests.Transceiver -import qualified Tests.Transceiver.Prbs -import qualified Tests.Transceiver.WordAlign -import qualified Tests.Wishbone - -tests :: TestTree -tests = - testGroup - "Unittests" - [ Tests.Axi4.tests - , Tests.Calendar.tests - , Tests.ClockControl.Si539xSpi.tests - , Tests.DoubleBufferedRam.tests - , Tests.ElasticBuffer.tests - , Tests.ProcessingElement.ReadElf.tests - , Tests.ScatterGather.tests - , Tests.StabilityChecker.tests - , Tests.Switch.tests - , Tests.Transceiver.tests - , Tests.Transceiver.Prbs.tests - , Tests.Transceiver.WordAlign.tests - , Tests.Wishbone.tests - , Tests.Axi4.Generators.tests - , Tests.Axi4.Properties.tests - , Tests.Haxioms.tests - ] - -{- | Default number of tests is 100, which is too low for our (complicated) -state machinery. --} -setDefaultHedgehogTestLimit :: HedgehogTestLimit -> HedgehogTestLimit -setDefaultHedgehogTestLimit (HedgehogTestLimit Nothing) = HedgehogTestLimit (Just 1000) -setDefaultHedgehogTestLimit opt = opt - -{- | Hedgehog seemingly gets stuck in an infinite loop when shrinking - probably -due to the way our generators depend on each other. We limit the number of -shrinks to 100. --} -setDefaultHedgehogShrinkLimit :: HedgehogShrinkLimit -> HedgehogShrinkLimit -setDefaultHedgehogShrinkLimit (HedgehogShrinkLimit Nothing) = HedgehogShrinkLimit (Just 100) -setDefaultHedgehogShrinkLimit opt = opt - -main :: IO () -main = - defaultMain $ - adjustOption setDefaultHedgehogTestLimit $ - adjustOption setDefaultHedgehogShrinkLimit $ - tests diff --git a/bittide/tests/doctests.hs b/bittide/tests/doctests.hs deleted file mode 100644 index 17e8f5f57..000000000 --- a/bittide/tests/doctests.hs +++ /dev/null @@ -1,14 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import System.Environment (getArgs) -import Test.DocTest (mainFromCabal) - -main :: IO () -main = do - -- We use Nix to setup tooling, not to provide GHC packages so we need to set --no-nix - args <- getArgs - mainFromCabal "bittide" ("--no-nix" : args) diff --git a/cabal.project b/cabal.project index 88e56f6b3..7a41efc35 100644 --- a/cabal.project +++ b/cabal.project @@ -1,175 +1,53 @@ -- SPDX-FileCopyrightText: 2022 Google LLC -- -- SPDX-License-Identifier: CC0-1.0 + packages: - bittide-experiments/ - bittide-extra/ - bittide-instances/ - bittide-shake/ - bittide-tools/ - bittide/ - clash-vexriscv/clash-vexriscv/ - vivado-hs/ + clash-vexriscv/ + clash-vexriscv-sim/ write-ghc-environment-files: always --- index state, to go along with the cabal.project.freeze file. update the index --- state by running `cabal update` twice and looking at the index state it --- displays to you (as the second update will be a no-op) -index-state: 2024-08-07T03:18:13Z -with-compiler: ghc-9.6.5 tests: True -package bittide - -- Tweak haddock stylesheet to enable word wrapping of types. - -- We specify the default Linuwial theme as an alternate - -- so we're able to import its css file from the custom theme. - haddock-options: --theme=linuwial-wrap-types.css --theme=Linuwial - -package zlib - flags: +pkg-config package clash-prelude flags: -multiple-hidden - ghc-options: - +RTS - -qn8 - -A64M - -RTS - -j8 - -package clash-lib - ghc-options: - +RTS - -qn8 - -A64M - -RTS - -j8 - -package clash-ghc - ghc-options: - +RTS - -qn8 - -A64M - -RTS - -j8 - -package bittide - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package bittide-experiments - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package bittide-extra - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package bittide-instances - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package bittide-shake - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package bittide-tools - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package clash-vexriscv - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package clash-vexriscv-sim - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 - -package vivado-hs - ghc-options: - +RTS - -xp - -qn8 - -A64M - -RTS - -j8 -- index state, to go along with the cabal.project.freeze file. update the index -- state by running `cabal update` twice and looking at the index state it -- displays to you (as the second update will be a no-op) -index-state: 2023-12-05T05:33:28Z +index-state: 2023-09-28T08:48:26Z --- We need an up-to-date Clash and libraries. Among other features, this adds --- support for dynamic clocks and Xilinx primitive support. +-- Needed to simulate dynamic clocks. source-repository-package type: git location: https://github.com/clash-lang/clash-compiler.git - tag: 20c17495e821faa2b8969db5cb3b3ebdc322b7f7 + tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a subdir: clash-prelude source-repository-package type: git location: https://github.com/clash-lang/clash-compiler.git - tag: 20c17495e821faa2b8969db5cb3b3ebdc322b7f7 + tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a subdir: clash-ghc source-repository-package type: git location: https://github.com/clash-lang/clash-compiler.git - tag: 20c17495e821faa2b8969db5cb3b3ebdc322b7f7 + tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a subdir: clash-lib source-repository-package type: git location: https://github.com/clash-lang/clash-compiler.git - tag: 20c17495e821faa2b8969db5cb3b3ebdc322b7f7 - subdir: clash-prelude-hedgehog + tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a + subdir: clash-cores source-repository-package type: git location: https://github.com/clash-lang/clash-compiler.git - tag: 20c17495e821faa2b8969db5cb3b3ebdc322b7f7 - subdir: clash-cores + tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a + subdir: clash-prelude-hedgehog source-repository-package type: git @@ -187,20 +65,3 @@ source-repository-package type: git location: https://github.com/cchalmers/circuit-notation.git tag: 19b386c4aa3ff690758ae089c7754303f3500cc9 - -source-repository-package - type: git - location: https://github.com/martijnbastiaan/haskell-hedgehog.git - tag: ddfa34501d16b12fb621efa72bed892910cb5475 - subdir: hedgehog - --- TODO: remove once the fixed version has been released on hackage -source-repository-package - type: git - location: https://github.com/clash-lang/ghc-typelits-knownnat - tag: 30b72d5bd01b497ef4465ff51205e1ebe068e4f9 - -source-repository-package - type: git - location: https://github.com/clash-lang/ghc-typelits-extra - tag: 1eed8d3de637a115dad12e2cb722cb0a3bc761ea diff --git a/cabal.project.freeze b/cabal.project.freeze deleted file mode 100644 index 7d13b456e..000000000 --- a/cabal.project.freeze +++ /dev/null @@ -1,329 +0,0 @@ -active-repositories: hackage.haskell.org:merge -constraints: any.Cabal ==3.10.3.0, - any.Cabal-syntax ==3.10.3.0, - any.Glob ==0.10.2, - any.HUnit ==1.6.2.0, - any.MissingH ==1.6.0.1, - MissingH +network--ge-3_0_0, - any.OneTuple ==0.4.1.1, - any.Only ==0.1, - any.QuickCheck ==2.14.3, - QuickCheck -old-random +templatehaskell, - any.StateVar ==1.2.2, - any.Stream ==0.4.7.2, - any.adjunctions ==4.4.2, - any.aeson ==2.2.1.0, - aeson +ordered-keymap, - any.aeson-pretty ==0.8.10, - aeson-pretty -lib-only, - any.ansi-terminal ==1.0, - ansi-terminal -example, - any.ansi-terminal-types ==0.11.5, - any.appar ==0.1.8, - any.array ==0.5.6.0, - any.arrows ==0.4.4.2, - any.asn1-encoding ==0.9.6, - any.asn1-parse ==0.9.5, - any.asn1-types ==0.3.4, - any.assoc ==1.1, - assoc +tagged, - any.async ==2.2.5, - async -bench, - any.attoparsec ==0.14.4, - attoparsec -developer, - any.attoparsec-aeson ==2.2.0.1, - any.barbies ==2.0.5.0, - any.base ==4.18.2.1, - any.base-compat ==0.13.1, - any.base-orphans ==0.9.1, - any.base16-bytestring ==1.0.2.0, - any.base64-bytestring ==1.2.1.0, - any.basement ==0.0.16, - any.bifunctors ==5.6.1, - bifunctors +tagged, - any.binary ==0.8.9.1, - any.bitvec ==1.1.5.0, - bitvec +simd, - any.blaze-builder ==0.4.2.3, - any.blaze-html ==0.9.1.2, - any.blaze-markup ==0.8.3.0, - any.boring ==0.2.1, - boring +tagged, - any.byteorder ==1.0.4, - any.bytestring ==0.11.5.3, - any.cabal-doctest ==1.0.9, - any.call-stack ==0.4.0, - any.case-insensitive ==1.2.1.0, - any.cassava ==0.5.3.0, - cassava -bytestring--lt-0_10_4, - any.cassava-conduit ==0.6.5, - cassava-conduit +small_base, - any.cereal ==0.5.8.3, - cereal -bytestring-builder, - any.charset ==0.3.10, - any.circuit-notation ==0.1.0.0, - any.clash-cores ==1.9.0, - clash-cores +doctests -haddock -nix +unittests, - any.clash-ghc ==1.9.0, - clash-ghc -dynamic -use-ghc-paths -workaround-ghc-mmap-crash, - any.clash-lib ==1.9.0, - clash-lib -debug +doctests +unittests -workaround-ghc-mmap-crash, - any.clash-prelude ==1.9.0, - clash-prelude +benchmarks +doctests -large-tuples -strict-mapsignal -super-strict +unittests -workaround-ghc-mmap-crash, - any.clash-prelude-hedgehog ==1.9.0, - any.clash-protocols ==0.1, - clash-protocols -ci -large-tuples, - any.clash-protocols-base ==0.1, - clash-protocols-base -ci -large-tuples, - any.clock ==0.8.4, - clock -llvm, - any.cmdargs ==0.10.22, - cmdargs +quotation -testprog, - any.code-page ==0.2.1, - any.colour ==2.3.6, - any.comonad ==5.0.8, - comonad +containers +distributive +indexed-traversable, - any.concurrent-output ==1.10.20, - any.concurrent-supply ==0.1.8, - concurrent-supply +test-properties, - any.conduit ==1.3.5, - any.conduit-extra ==1.3.6, - any.constraints ==0.14, - any.constraints-extras ==0.4.0.0, - constraints-extras +build-readme, - any.containers ==0.6.7, - any.contravariant ==1.5.5, - contravariant +semigroups +statevar +tagged, - any.cookie ==0.4.6, - any.cryptohash-sha256 ==0.11.102.1, - cryptohash-sha256 -exe +use-cbits, - any.crypton ==0.34, - crypton -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq +support_pclmuldq +support_rdrand -support_sse +use_target_attributes, - any.crypton-connection ==0.3.1, - any.crypton-x509 ==1.7.6, - any.crypton-x509-store ==1.6.9, - any.crypton-x509-system ==1.6.7, - any.crypton-x509-validation ==1.6.12, - any.data-binary-ieee754 ==0.4.4, - any.data-default ==0.7.1.1, - any.data-default-class ==0.1.2.0, - any.data-default-instances-containers ==0.0.1, - any.data-default-instances-dlist ==0.0.1, - any.data-default-instances-old-locale ==0.0.1, - any.data-fix ==0.3.2, - any.deepseq ==1.4.8.1, - any.dependent-sum ==0.7.2.0, - any.directory ==1.3.8.4, - any.distributive ==0.6.2.1, - distributive +semigroups +tagged, - any.dlist ==1.0, - dlist -werror, - any.doctest-parallel ==0.3.1, - any.elf ==0.31, - any.erf ==2.0.0.0, - any.exceptions ==0.10.7, - any.extra ==1.7.14, - any.filepath ==1.4.300.1, - any.filepattern ==0.1.3, - any.fingertree ==0.1.5.0, - any.first-class-families ==0.8.0.1, - any.free ==5.2, - any.generically ==0.1.1, - any.ghc ==9.6.5, - any.ghc-bignum ==1.3, - any.ghc-boot ==9.6.5, - any.ghc-boot-th ==9.6.5, - any.ghc-heap ==9.6.5, - any.ghc-paths ==0.1.0.12, - any.ghc-prim ==0.10.0, - any.ghc-tcplugins-extra ==0.4.5, - ghc-tcplugins-extra -deverror, - any.ghc-typelits-extra ==0.4.7, - ghc-typelits-extra -deverror, - any.ghc-typelits-knownnat ==0.7.11, - ghc-typelits-knownnat -deverror, - any.ghc-typelits-natnormalise ==0.7.9, - ghc-typelits-natnormalise -deverror, - any.ghci ==9.6.5, - any.half ==0.3.1, - any.happy ==1.20.1.1, - any.happy-dot ==1.0.0.0, - any.hashable ==1.4.3.0, - hashable +integer-gmp -random-initial-seed, - any.haskeline ==0.8.2.1, - any.haskell-lexer ==1.1.1, - any.haskell-src-exts ==1.23.1, - any.haskell-src-meta ==0.8.13, - any.heaps ==0.4, - any.hedgehog ==1.4, - any.hint ==0.9.0.8, - any.hourglass ==0.2.12, - any.hpc ==0.6.2.0, - any.hsc2hs ==0.68.10, - hsc2hs -in-ghc-tree, - any.hslogger ==1.3.1.0, - hslogger +network--gt-3_0_0, - any.http-client ==0.7.15, - http-client +network-uri, - any.http-client-tls ==0.3.6.3, - any.http-conduit ==2.3.8.3, - http-conduit +aeson, - any.http-types ==0.12.4, - any.indexed-traversable ==0.1.3, - any.indexed-traversable-instances ==0.1.1.2, - any.infinite-list ==0.1, - any.integer-conversion ==0.1.0.1, - any.integer-gmp ==1.1, - any.integer-logarithms ==1.0.3.1, - integer-logarithms -check-bounds +integer-gmp, - any.invariant ==0.6.2, - any.iproute ==1.7.12, - any.js-dgtable ==0.5.2, - any.js-flot ==0.8.3, - any.js-jquery ==3.3.1, - any.kan-extensions ==5.2.5, - any.lazysmallcheck ==0.6, - any.lens ==5.2.3, - lens -benchmark-uniplate -dump-splices +inlining -j +test-hunit +test-properties +test-templates +trustworthy, - any.libyaml ==0.1.2, - libyaml -no-unicode -system-libyaml, - any.lift-type ==0.1.1.1, - any.lifted-async ==0.10.2.5, - any.lifted-base ==0.2.3.12, - any.matplotlib ==0.7.7, - any.memory ==0.18.0, - memory +support_bytestring +support_deepseq, - any.mime-types ==0.1.2.0, - any.mmorph ==1.2.0, - any.monad-control ==1.0.3.1, - any.mono-traversable ==1.0.15.3, - any.mtl ==2.3.1, - any.network ==3.1.4.0, - network -devel, - any.network-bsd ==2.8.1.0, - any.network-uri ==2.6.4.2, - any.old-locale ==1.0.0.7, - any.old-time ==1.1.0.3, - any.optparse-applicative ==0.18.1.0, - optparse-applicative +process, - any.ordered-containers ==0.2.3, - any.parallel ==3.2.2.0, - any.parsec ==3.1.16.1, - any.parsers ==0.12.11, - parsers +attoparsec +binary +parsec, - any.pem ==0.2.4, - any.pretty ==1.1.3.6, - any.pretty-show ==1.10, - any.pretty-simple ==4.1.2.0, - pretty-simple -buildexample +buildexe, - any.prettyprinter ==1.7.1, - prettyprinter -buildreadme +text, - any.prettyprinter-ansi-terminal ==1.1.3, - any.prettyprinter-interp ==0.2.0.0, - any.primitive ==0.8.0.0, - any.process ==1.6.19.0, - any.profunctors ==5.6.2, - any.random ==1.2.1.1, - any.recursion-schemes ==5.2.2.5, - recursion-schemes +template-haskell, - any.reducers ==3.12.4, - any.reflection ==2.1.7, - reflection -slow +template-haskell, - any.regex-base ==0.94.0.2, - any.regex-compat ==0.95.2.1, - any.regex-posix ==0.96.0.1, - regex-posix -_regex-posix-clib, - any.resourcet ==1.3.0, - any.rts ==1.0.2, - any.safe ==0.3.19, - any.safe-exceptions ==0.1.7.4, - any.scientific ==0.3.7.0, - scientific -bytestring-builder -integer-simple, - any.semialign ==1.3, - semialign +semigroupoids, - any.semigroupoids ==6.0.0.1, - semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers, - any.semigroups ==0.20, - semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers, - any.shake ==0.19.7, - shake -cloud -embed-files -portable, - any.singletons ==3.0.2, - any.socks ==0.6.1, - any.some ==1.0.6, - some +newtype-unsafe, - any.split ==0.2.4, - any.splitmix ==0.1.0.5, - splitmix -optimised-mixer, - any.stm ==2.5.1.0, - any.streaming-commons ==0.2.2.6, - streaming-commons -use-bytestring-builder, - any.strict ==0.5, - any.strict-tuple ==0.1.5.3, - any.string-interpolate ==0.3.2.1, - string-interpolate -bytestring-builder -extended-benchmarks -text-builder, - any.syb ==0.7.2.4, - any.tagged ==0.8.8, - tagged +deepseq +transformers, - any.tasty ==1.4.3, - tasty +unix, - any.tasty-expected-failure ==0.12.3, - any.tasty-golden ==2.3.5, - tasty-golden -build-example, - any.tasty-hedgehog ==1.4.0.2, - any.tasty-hunit ==0.10.1, - any.tasty-th ==0.1.7, - any.template-haskell ==2.20.0.0, - any.temporary ==1.3, - any.terminal-size ==0.3.4, - any.terminfo ==0.4.1.6, - any.text ==2.0.2, - any.text-conversions ==0.3.1.1, - any.text-iso8601 ==0.1, - any.text-short ==0.1.5, - text-short -asserts, - any.th-abstraction ==0.6.0.0, - any.th-compat ==0.1.4, - any.th-expand-syns ==0.4.11.0, - any.th-lift ==0.8.4, - any.th-orphans ==0.13.14, - any.th-reify-many ==0.1.10, - any.these ==1.2, - any.time ==1.12.2, - any.time-compat ==1.9.6.1, - time-compat -old-locale, - any.tls ==1.9.0, - tls +compat -hans +network, - any.transformers ==0.6.1.0, - any.transformers-base ==0.4.6, - transformers-base +orphaninstances, - any.transformers-compat ==0.7.2, - transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, - any.trifecta ==2.1.3, - any.type-equality ==1, - any.type-errors ==0.2.0.2, - any.typed-process ==0.2.11.1, - any.typelits-witnesses ==0.4.0.1, - any.unbounded-delays ==0.1.1.1, - any.uniplate ==1.6.13, - any.unix ==2.8.4.0, - any.unix-time ==0.4.11, - any.unliftio-core ==0.2.1.0, - any.unordered-containers ==0.2.19.1, - unordered-containers -debug, - any.utf8-string ==1.0.2, - any.uuid-types ==1.0.5.1, - any.vector ==0.13.1.0, - vector +boundschecks -internalchecks -unsafechecks -wall, - any.vector-algorithms ==0.9.0.1, - vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks, - any.vector-binary-instances ==0.2.5.2, - any.vector-stream ==0.1.0.0, - any.void ==0.7.3, - void -safe, - any.witherable ==0.4.2, - any.wl-pprint-annotated ==0.1.0.1, - any.yaml ==0.11.11.2, - yaml +no-examples +no-exe, - any.zlib ==0.6.3.0, - zlib -bundled-c-zlib -non-blocking-ffi +pkg-config -index-state: hackage.haskell.org 2023-12-05T05:33:28Z diff --git a/cargo.sh b/cargo.sh deleted file mode 100755 index bd968bd7f..000000000 --- a/cargo.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - - -# Because our Rust code is split between different Cargo workspaces -# for Reasons TM, cargo commands can't be run directly. -# -# This script runs any command in *all* the workspaces. - - -# Echoes the given command to stderr, then executes it. -function run() { - # https://stackoverflow.com/a/76153233/14637 - echo -n '>' >&2 - for arg in "$@"; do - printf " %q" "$arg" >&2 - done - echo >&2 - "$@" -} - -cd firmware-support; - printf "firmware-support " >&2; - run cargo "$@"; - fs_res="$?" -cd .. - -cd firmware-binaries; - printf "firmware-binaries " >&2; - run cargo "$@"; - fb_res="$?" -cd .. - -cd host-tools; - printf "host-tools " >&2; - run cargo "$@"; - ht_res="$?" -cd .. - -if [[ fs_res -ne 0 ]]; then - echo "firmware-support failure!" -fi - -if [[ fb_res -ne 0 ]]; then - echo "firmware-binaries failure!" -fi - -if [[ ht_res -ne 0 ]]; then - echo "host-tools failure!" -fi - -if [[ fs_res -ne 0 ]] || [[ fb_res -ne 0 ]] || [[ ht_res -ne 0 ]]; then - exit 1 -fi diff --git a/clash-vexriscv/clash-vexriscv-sim/.gitignore b/clash-vexriscv-sim/.gitignore similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/.gitignore rename to clash-vexriscv-sim/.gitignore diff --git a/bittide-experiments/LICENSE b/clash-vexriscv-sim/LICENSE similarity index 100% rename from bittide-experiments/LICENSE rename to clash-vexriscv-sim/LICENSE diff --git a/clash-vexriscv/clash-vexriscv-sim/README.md b/clash-vexriscv-sim/README.md similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/README.md rename to clash-vexriscv-sim/README.md diff --git a/clash-vexriscv/clash-vexriscv-sim/app/HdlTest.hs b/clash-vexriscv-sim/app/HdlTest.hs similarity index 79% rename from clash-vexriscv/clash-vexriscv-sim/app/HdlTest.hs rename to clash-vexriscv-sim/app/HdlTest.hs index 491e2ac45..73b058681 100644 --- a/clash-vexriscv/clash-vexriscv-sim/app/HdlTest.hs +++ b/clash-vexriscv-sim/app/HdlTest.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2023 Google LLC +-- SPDX-FileCopyrightText: 2023-2024 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 diff --git a/clash-vexriscv-sim/app/VexRiscvChainSimulation.hs b/clash-vexriscv-sim/app/VexRiscvChainSimulation.hs new file mode 100644 index 000000000..232d0fb92 --- /dev/null +++ b/clash-vexriscv-sim/app/VexRiscvChainSimulation.hs @@ -0,0 +1,307 @@ +-- SPDX-FileCopyrightText: 2022-2024 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 +{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE RecordWildCards #-} + +import Clash.Prelude + +import Control.Monad (forM_, when) +import GHC.Char (chr) +import GHC.IO.Handle (hFlush, Handle, hPutStr) +import Options.Applicative (Parser, strOption, short, long, help, info, helper, fullDesc, progDesc, header, execParser) +import Protocols.Wishbone +import System.Exit (exitFailure) +import System.IO (openFile, IOMode (WriteMode), hPutStrLn, hPutChar) +import Text.Printf (printf, hPrintf) + +import Utils.DebugConfig (DebugConfiguration (..)) +import Utils.Cpu (cpu) +import Utils.ProgramLoad (loadProgramDmem) +import VexRiscv (JtagIn (JtagIn), JtagOut (JtagOut), CpuOut (dBusWbM2S, iBusWbM2S), DumpVcd(NoDumpVcd)) +import VexRiscv.JtagTcpBridge (vexrJtagBridge) + + +import qualified Data.List as L + +-- change this variable to the configuration you want to use + +debugConfig :: DebugConfiguration +debugConfig = + -- InspectWrites + RunCharacterDevice + +-- +{- + InspectBusses + 0 + 0 + (Just 100) + True + True +-- -} + +-------------------------------------- + +data RunOpts = RunOpts + { execPathA :: FilePath + , execPathB :: FilePath + , logPathA :: FilePath + , logPathB :: FilePath + } + +getRunOpts :: Parser RunOpts +getRunOpts = RunOpts + <$> strOption + ( short 'a' + <> long "a-exec" + <> help "Path to the executable for CPU A" + ) + <*> strOption + ( short 'b' + <> long "b-exec" + <> help "Path to the executable for CPU B" + ) + <*> strOption + ( short 'A' + <> long "a-log" + <> help "Path to the log file for CPU A" + ) + <*> strOption + ( short 'B' + <> long "b-log" + <> help "Path to the log file for CPU B" + ) + +jtagDaisyChain :: JtagIn -> JtagOut -> JtagIn +jtagDaisyChain (JtagIn tc ms _) (JtagOut to _) = JtagIn tc ms to + +type CpuSignals = + ( CpuOut + , JtagOut + , Maybe (BitVector 32, BitVector 32) + , WishboneS2M (BitVector 32) + , WishboneS2M (BitVector 32) + ) + +main :: IO () +main = do + RunOpts {..} <- execParser opts + + (iMemA, dMemA) <- + withClockResetEnable @System clockGen resetGen enableGen $ + loadProgramDmem @System execPathA + + (iMemB, dMemB) <- + withClockResetEnable @System clockGen resetGen enableGen $ + loadProgramDmem @System execPathB + + logFileA <- openFile logPathA WriteMode + logFileB <- openFile logPathB WriteMode + + let + jtagInA = vexrJtagBridge 7894 jtagOutB + cpuOutA@(unbundle -> (_circuitA, jtagOutA, _, _iBusA, _dBusA)) = + withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $ + let + (circ, jto, writes1, iBus, dBus) = cpu NoDumpVcd (Just jtagInA) iMemA dMemA + dBus' = register emptyWishboneS2M dBus + in bundle (circ, jto, writes1, iBus, dBus') + + jtagInB = liftA2 jtagDaisyChain jtagInA jtagOutA + cpuOutB@(unbundle -> (_circuitB, jtagOutB, _, _iBusB, _dBusB)) = + withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $ + let + (circ, jto, writes1, iBus, dBus) = cpu NoDumpVcd (Just jtagInB) iMemB dMemB + dBus' = register emptyWishboneS2M dBus + in bundle (circ, jto, writes1, iBus, dBus') + + _jtagReset = L.foldl (liftA2 go1) (pure False) [jtagOutA, jtagOutB] + where + go1 acc (JtagOut _ tr) = acc || bitToBool tr + + cpuOut = bundle (cpuOutA, cpuOutB) + + runSampling + debugConfig + (logFileA, logFileB) + cpuOut + where + opts = info (getRunOpts <**> helper) + ( fullDesc + <> progDesc "Run binaries on two Vex RISC-V CPUs linked with JTAG chaining" + <> header "vex-riscv-chain-simulation - a test for JTAG chaining" + ) + +runSampling + :: DebugConfiguration + -> (Handle, Handle) + -> Signal System (CpuSignals, CpuSignals) + -> IO () +runSampling dbg (handleA, handleB) cpusOutputs = do + case dbg of + RunCharacterDevice -> + forM_ + (sample_lazy @System (bundle (register @System (unpack 0) cpusOutputs, cpusOutputs))) + $ \((a1, b1), (a0, b0)) -> do + runCharacterDevice handleA a1 a0 + runCharacterDevice handleB b1 b0 + InspectBusses initCycles uninteresting interesting iEnabled dEnabled -> do + let + skipTotal = initCycles + uninteresting + sampled = case interesting of + Nothing -> L.zip [0 ..] $ sample_lazy @System cpusOutputs + Just nInteresting -> + let total = initCycles + uninteresting + nInteresting in + L.zip [0 ..] $ L.take total $ sample_lazy @System cpusOutputs + forM_ sampled $ \(i, (cpuOutA, cpuOutB)) -> do + runInspectBusses handleA skipTotal iEnabled dEnabled i cpuOutA + runInspectBusses handleB skipTotal iEnabled dEnabled i cpuOutB + InspectWrites -> + forM_ + (sample_lazy @System cpusOutputs) + $ \((_, _, writesA, _, _), (_, _, writesB, _, _)) -> do + case (writesA, writesB) of + (Just (aA, vA), Just (aB, vB)) -> do + hPrintf handleA "W: % 8X\n" (toInteger aA) (toInteger vA) + hPrintf handleB "W: % 8X\n" (toInteger aB) (toInteger vB) + (Just (a, v), Nothing) -> hPrintf handleA "W: % 8X\n" (toInteger a) (toInteger v) + (Nothing, Just (a, v)) -> hPrintf handleB "W: % 8X\n" (toInteger a) (toInteger v) + _ -> pure () + +runCharacterDevice + :: Handle + -> CpuSignals + -> CpuSignals + -> IO () +runCharacterDevice logFile (_, _, write, dS2M, iS2M) (out1, _, _, _, _) = do + when (err dS2M) $ do + let dBusM2S = dBusWbM2S out1 + let dAddr = toInteger (addr dBusM2S) -- `shiftL` 2 + hPrintf + logFile + "D-bus ERR reply % 8X (% 8X)\n" + (toInteger $ dAddr `shiftL` 2) + (toInteger dAddr) + exitFailure + + when (err iS2M) $ do + let iBusM2S = iBusWbM2S out1 + let iAddr = toInteger (addr iBusM2S) -- `shiftL` 2 + hPrintf + logFile + "I-bus ERR reply % 8X (% 8X)\n" + (toInteger $ iAddr `shiftL` 2) + (toInteger iAddr) + hPrintf logFile "%s\n" (show iBusM2S) + exitFailure + + case write of + Just (address, value) | address == 0x0000_1000 -> do + let + (_ :: BitVector 24, b :: BitVector 8) = unpack value + char = chr (fromEnum b) + hPutChar logFile char + when (char == '\n') (hFlush logFile) + _ -> pure () + +runInspectBusses + :: Handle + -> Int + -> Bool + -> Bool + -> Int + -> CpuSignals + -> IO () +runInspectBusses + logFile + skipTotal + iEnabled + dEnabled + i + (out, _, _, iBusS2M, dBusS2M) + = do + let doPrint = i >= skipTotal + + when (doPrint && iEnabled) $ do + let + iBusM2S = iBusWbM2S out + iAddr = toInteger (addr iBusM2S) -- `shiftL` 2 + cyc = if busCycle iBusM2S then "CYC" else " " + stb = if strobe iBusM2S then "STB" else " " + iResp = + if + | acknowledge iBusS2M -> "ACK " + | err iBusS2M -> "ERR " + | retry iBusS2M -> "RETRY" + | otherwise -> "NONE " + iRespData = + if acknowledge iBusS2M + then printf "% 8X" (toInteger $ readData iBusS2M) + else "" + + hPutStr logFile $ + "iM2S: (" + <> (cyc <> " " <> stb) + <> ") (" + <> showX (busSelect iBusM2S) + <> ") " + <> printf "% 8X" iAddr + <> " (" + <> printf "%X" (iAddr `shiftL` 2) + <> ")" + hPutStrLn logFile $ "iS2M: " <> iResp <> " - " <> iRespData + + when (err iBusS2M) + exitFailure + + when (doPrint && dEnabled) $ do + let + dBusM2S = dBusWbM2S out + dAddr = toInteger (addr dBusM2S) -- `shiftL 2` + dWrite = writeEnable dBusM2S + cyc = if busCycle dBusM2S then "CYC" else " " + stb = if strobe dBusM2S then "STB" else " " + dValid = busCycle dBusM2S && strobe dBusM2S + dActive = busCycle dBusM2S + mode = if dWrite then "W" else "R" + dResp = + if + | acknowledge dBusS2M -> "ACK " + | err dBusS2M -> "ERR " + | retry dBusS2M -> "RETRY" + | otherwise -> "NONE " + dRespData + | acknowledge dBusS2M && hasUndefined (readData dBusS2M) && not dWrite = printf " - undefined!!" + | acknowledge dBusS2M && not dWrite = printf " - % 8X" (toInteger $ readData dBusS2M) + | not dWrite = " - " + | otherwise = "" + writeDat = + if dValid && dWrite + then printf "% 8X" (toInteger $ writeData dBusM2S) + else " no data" + + when (dActive || hasTerminateFlag dBusS2M) $ do + hPutStr logFile $ + "dM2S: " + <> mode + <> " (" + <> (cyc <> " " <> stb) + <> ") (" + <> showX (busSelect dBusM2S) + <> ") " + <> printf "% 8X" dAddr + <> " (" + <> printf "% 8X" (dAddr `shiftL` 2) + <> ") <" + <> writeDat + <> "> - " + hPutStrLn logFile $ + "dS2M: " + <> dResp + <> dRespData + + when (err dBusS2M) + exitFailure diff --git a/clash-vexriscv/clash-vexriscv-sim/app/VexRiscvSimulation.hs b/clash-vexriscv-sim/app/VexRiscvSimulation.hs similarity index 80% rename from clash-vexriscv/clash-vexriscv-sim/app/VexRiscvSimulation.hs rename to clash-vexriscv-sim/app/VexRiscvSimulation.hs index 06e5be10a..81c0c8123 100644 --- a/clash-vexriscv/clash-vexriscv-sim/app/VexRiscvSimulation.hs +++ b/clash-vexriscv-sim/app/VexRiscvSimulation.hs @@ -1,16 +1,15 @@ --- SPDX-FileCopyrightText: 2022 Google LLC +-- SPDX-FileCopyrightText: 2022-2024 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE GADTs #-} {-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} import Clash.Prelude import Protocols.Wishbone -import VexRiscv (CpuOut(iBusWbM2S, dBusWbM2S)) +import VexRiscv (CpuOut(iBusWbM2S, dBusWbM2S), DumpVcd(NoDumpVcd)) import qualified Data.List as L @@ -21,36 +20,11 @@ import System.Environment (getArgs) import System.IO (putChar, hFlush, stdout) import Text.Printf (printf) - -import Utils.ProgramLoad (loadProgramDmem) +import Utils.DebugConfig (DebugConfiguration (..)) import Utils.Cpu (cpu) +import Utils.ProgramLoad (loadProgramDmem) import System.Exit (exitFailure) - --------------------------------------- --- --- Debugging configuration --- --------------------------------------- - -data DebugConfiguration where - -- | Run a program and only output what the program writes to the - -- character-device - RunCharacterDevice :: DebugConfiguration - -- | Run a program and print detailed information of CPU bus interactions - InspectBusses :: - -- | # of cycles the program takes to initialise the instruction memory - Int -> - -- | # of "uninteresting" cycles to skip, such as runtime setup code - Int -> - -- | # of "interesting" cycles to inspect - Maybe Int -> - -- | inspect instruct-bus interactions - Bool -> - -- | inspect data-bus interactions - Bool -> - DebugConfiguration - -- | Run a program and output all the write operations - InspectWrites :: DebugConfiguration +import VexRiscv.JtagTcpBridge (vexrJtagBridge) -- change this variable to the configuration you want to use @@ -80,16 +54,18 @@ main = do withClockResetEnable @System clockGen resetGen enableGen $ loadProgramDmem @System elfFile - let cpuOut@(unbundle -> (_circuit, writes, _iBus, _dBus)) = - withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $ - let (circ, writes1, iBus, dBus) = cpu (Just 7894) iMem dMem - dBus' = register emptyWishboneS2M dBus - in bundle (circ, writes1, iBus, dBus') + let + jtagPort = vexrJtagBridge 7894 jtagOut + cpuOut@(unbundle -> (_circuit, jtagOut, writes, _iBus, _dBus)) = + withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $ + let (circ, jto, writes1, iBus, dBus) = cpu NoDumpVcd (Just jtagPort) iMem dMem + dBus' = register emptyWishboneS2M dBus + in bundle (circ, jto, writes1, iBus, dBus') case debugConfig of RunCharacterDevice -> forM_ (sample_lazy @System (bundle (register @System (unpack 0) cpuOut, cpuOut))) $ - \((_out, write, dS2M, iS2M), (out1, _write, _dS2M, _iS2M)) -> do + \((_out, _, write, dS2M, iS2M), (out1, _, _write, _dS2M, _iS2M)) -> do when (err dS2M) $ do let dBusM2S = dBusWbM2S out1 @@ -122,7 +98,7 @@ main = do let total = initCycles + uninteresting + nInteresting in L.zip [0 ..] $ L.take total $ sample_lazy @System cpuOut - forM_ sampled $ \(i, (out, _, iBusS2M, dBusS2M)) -> do + forM_ sampled $ \(i, (out, _, _, iBusS2M, dBusS2M)) -> do let doPrint = i >= skipTotal diff --git a/clash-vexriscv/clash-vexriscv-sim/bundle_test_binaries.sh b/clash-vexriscv-sim/bundle_test_binaries.sh similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/bundle_test_binaries.sh rename to clash-vexriscv-sim/bundle_test_binaries.sh diff --git a/clash-vexriscv/clash-vexriscv-sim/clash-vexriscv-sim.cabal b/clash-vexriscv-sim/clash-vexriscv-sim.cabal similarity index 85% rename from clash-vexriscv/clash-vexriscv-sim/clash-vexriscv-sim.cabal rename to clash-vexriscv-sim/clash-vexriscv-sim.cabal index 499bcf1e3..c60046ce1 100644 --- a/clash-vexriscv/clash-vexriscv-sim/clash-vexriscv-sim.cabal +++ b/clash-vexriscv-sim/clash-vexriscv-sim.cabal @@ -83,6 +83,8 @@ library default-language: Haskell2010 exposed-modules: Utils.Cpu + Utils.DebugConfig + Utils.FilePath Utils.Instance Utils.Interconnect Utils.ProgramLoad @@ -94,7 +96,10 @@ library clash-prelude, clash-protocols, clash-vexriscv, + directory, elf >= 0.31 && < 0.32, + filepath, + template-haskell, -- XXX: Doesn't really belong in clash-vexriscv-SIM executable hdl-test @@ -121,6 +126,24 @@ executable clash-vexriscv-bin containers, directory, +executable clash-vexriscv-chain-bin + import: common-options + main-is: VexRiscvChainSimulation.hs + hs-source-dirs: app + default-language: Haskell2010 + ghc-options: -threaded -rtsopts "-with-rtsopts=-M400M" + build-depends: + base, + clash-prelude, + clash-protocols, + clash-vexriscv, + clash-vexriscv-sim, + bytestring, + containers, + directory, + filepath, + optparse-applicative, + test-suite unittests import: common-options default-language: Haskell2010 @@ -128,7 +151,8 @@ test-suite unittests type: exitcode-stdio-1.0 ghc-options: -threaded -rtsopts -with-rtsopts=-N build-tool-depends: - clash-vexriscv-sim:clash-vexriscv-bin + clash-vexriscv-sim:clash-vexriscv-bin, + clash-vexriscv-sim:clash-vexriscv-chain-bin autogen-modules: Paths_clash_vexriscv_sim ghc-options: -threaded @@ -136,6 +160,7 @@ test-suite unittests other-modules: Paths_clash_vexriscv_sim Tests.Jtag + Tests.JtagChain build-depends: async, base, @@ -149,6 +174,7 @@ test-suite unittests extra, filepath, process, + streaming, tasty >= 1.2 && < 1.6, tasty-hunit >= 0.10 && < 0.11, temporary >=1.1 && <1.4, diff --git a/clash-vexriscv/clash-vexriscv-sim/clash-vexriscv-sim.cabal.license b/clash-vexriscv-sim/clash-vexriscv-sim.cabal.license similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/clash-vexriscv-sim.cabal.license rename to clash-vexriscv-sim/clash-vexriscv-sim.cabal.license diff --git a/clash-vexriscv/clash-vexriscv-sim/data/vexriscv_gdb.cfg b/clash-vexriscv-sim/data/vexriscv_chain_gdba.cfg similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/data/vexriscv_gdb.cfg rename to clash-vexriscv-sim/data/vexriscv_chain_gdba.cfg diff --git a/clash-vexriscv-sim/data/vexriscv_chain_gdbb.cfg b/clash-vexriscv-sim/data/vexriscv_chain_gdbb.cfg new file mode 100644 index 000000000..5de8010a4 --- /dev/null +++ b/clash-vexriscv-sim/data/vexriscv_chain_gdbb.cfg @@ -0,0 +1,45 @@ +# Execute using: +# +# gdb --command vexriscv_gdb.cfg +# + +# SPDX-FileCopyrightText: 2024 Google LLC +# +# SPDX-License-Identifier: CC0-1.0 + +# Assume "print_a" is running on the CPU +file "target/riscv32imc-unknown-none-elf/debug/print_b" + +# Work around issues where simulation is too slow to respond to keep-alive messages, +# confusing either OpenOCD or GDB. Note that it will still complain about "missed" +# deadlines, but it won't fail.. +set remotetimeout unlimited + +# Connect to OpenOCD +target extended-remote :3334 + +# List registers +i r + +# break on main function entrance +break main + +# Jump to start address, should run until it hits main +jump _start + +# Run until we hit function "done", meaning it should have printed "a" +disable 1 +break print_b::done +continue +disable 2 + +# Load program +file "target/riscv32imc-unknown-none-elf/debug/print_a" +load + +# Jump to start address. Should now output "b". +break print_a::done +jump _start + +# Stop running GDB +quit diff --git a/clash-vexriscv-sim/data/vexriscv_chain_sim.cfg b/clash-vexriscv-sim/data/vexriscv_chain_sim.cfg new file mode 100644 index 000000000..102e49a58 --- /dev/null +++ b/clash-vexriscv-sim/data/vexriscv_chain_sim.cfg @@ -0,0 +1,47 @@ +# Execute using: +# +# openocd-vexriscv -f vexriscv_sim_chain.cfg +# + +# SPDX-FileCopyrightText: 2024 Google LLC +# +# SPDX-License-Identifier: CC0-1.0 + +# See vexriscv_sim.cfg for more information on each step taken. + +adapter driver jtag_tcp +adapter speed 64000 +transport select jtag + + +set _ENDIAN little +set _TAP_TYPE 1234 + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x10001fff +} + +set _CHIPNAME vexrisc_ocd + +jtag newtap $_CHIPNAME chain0 -expected-id $_CPUTAPID -irlen 4 -ircapture 0x1 -irmask 0x0F +jtag newtap $_CHIPNAME chain1 -expected-id $_CPUTAPID -irlen 4 -ircapture 0x1 -irmask 0x03 + +target create $_CHIPNAME.cpu0 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.chain1 +vexriscv readWaitCycles 10 +vexriscv cpuConfigFile clash-vexriscv/example-cpu/ExampleCpu.yaml + +target create $_CHIPNAME.cpu1 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.chain0 +vexriscv readWaitCycles 10 +vexriscv cpuConfigFile clash-vexriscv/example-cpu/ExampleCpu.yaml + +poll_period 50 + +init + +echo "Halting processor" + +halt + +sleep 1000 diff --git a/clash-vexriscv-sim/data/vexriscv_gdb.cfg b/clash-vexriscv-sim/data/vexriscv_gdb.cfg new file mode 100644 index 000000000..362d7c758 --- /dev/null +++ b/clash-vexriscv-sim/data/vexriscv_gdb.cfg @@ -0,0 +1,45 @@ +# Execute using: +# +# gdb --command vexriscv_gdb.cfg +# + +# SPDX-FileCopyrightText: 2024 Google LLC +# +# SPDX-License-Identifier: CC0-1.0 + +# Assume "print_a" is running on the CPU +file "target/riscv32imc-unknown-none-elf/debug/print_a" + +# Work around issues where simulation is too slow to respond to keep-alive messages, +# confusing either OpenOCD or GDB. Note that it will still complain about "missed" +# deadlines, but it won't fail.. +set remotetimeout unlimited + +# Connect to OpenOCD +target extended-remote :3333 + +# List registers +i r + +# break on main function entrance +break main + +# Jump to start address, should run until it hits main +jump _start + +# Run until we hit function "done", meaning it should have printed "a" +disable 1 +break print_a::done +continue +disable 2 + +# Load program +file "target/riscv32imc-unknown-none-elf/debug/print_b" +load + +# Jump to start address. Should now output "b". +break print_b::done +jump _start + +# Stop running GDB +quit diff --git a/clash-vexriscv/clash-vexriscv-sim/data/vexriscv_sim.cfg b/clash-vexriscv-sim/data/vexriscv_sim.cfg similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/data/vexriscv_sim.cfg rename to clash-vexriscv-sim/data/vexriscv_sim.cfg diff --git a/clash-vexriscv/clash-vexriscv-sim/src/Utils/Cpu.hs b/clash-vexriscv-sim/src/Utils/Cpu.hs similarity index 88% rename from clash-vexriscv/clash-vexriscv-sim/src/Utils/Cpu.hs rename to clash-vexriscv-sim/src/Utils/Cpu.hs index 447921712..c9dc1bcbf 100644 --- a/clash-vexriscv/clash-vexriscv-sim/src/Utils/Cpu.hs +++ b/clash-vexriscv-sim/src/Utils/Cpu.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Google LLC +-- SPDX-FileCopyrightText: 2022-2023 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 @@ -22,6 +22,7 @@ import GHC.Stack (HasCallStack) import Utils.ProgramLoad (Memory, DMemory) import Utils.Interconnect (interconnectTwo) import Clash.Explicit.Prelude (unsafeOrReset) +import Data.Maybe (fromMaybe) createDomain vXilinxSystem{vName="Basic50", vPeriod= hzToPeriod 50_000_000} @@ -39,33 +40,34 @@ cpu :: -- convenient it is to use this within a design with synchronous resets. -- , HasAsynchronousReset dom ) => - Maybe Integer -> + DumpVcd -> + Maybe (Signal dom JtagIn) -> DMemory dom -> DMemory dom -> - ( Signal dom CpuOut, - -- writes - Signal dom (Maybe (BitVector 32, BitVector 32)), - -- iBus responses - Signal dom (WishboneS2M (BitVector 32)), - -- dBus responses + ( Signal dom CpuOut + , Signal dom JtagOut + , -- writes + Signal dom (Maybe (BitVector 32, BitVector 32)) + , -- iBus responses + Signal dom (WishboneS2M (BitVector 32)) + , -- dBus responses Signal dom (WishboneS2M (BitVector 32)) ) -cpu jtagPort bootIMem bootDMem = - ( output +cpu dumpVcd jtagIn0 bootIMem bootDMem = + ( cpuOut + , jtagOut , writes , iS2M , dS2M ) where - (output, jtagOut) = vexRiscv hasClock (hasReset `unsafeOrReset` jtagReset) input jtagIn + (cpuOut, jtagOut) = vexRiscv dumpVcd hasClock (hasReset `unsafeOrReset` jtagReset) input jtagIn1 jtagReset = unsafeFromActiveHigh $ register False $ bitToBool . debugReset <$> jtagOut - jtagIn = case jtagPort of - Just port -> vexrJtagBridge (fromInteger port) jtagOut - Nothing -> pure JTag.defaultIn + jtagIn1 = fromMaybe (pure JTag.defaultIn) jtagIn0 {- 00000000 - dummy area @@ -75,7 +77,7 @@ cpu jtagPort bootIMem bootDMem = -- The I-bus is only split once, for the D-mem and everything below. (iS2M, vecToTuple . unbundle -> (iMemIM2S, dMemIM2S)) = interconnectTwo - (unBusAddr . iBusWbM2S <$> output) + (unBusAddr . iBusWbM2S <$> cpuOut) ((0x0000_0000, iMemIS2M) :> (0x4000_0000, dMemIS2M) :> Nil) -- Because the dummy region should never be accessed by the instruction bus @@ -83,7 +85,7 @@ cpu jtagPort bootIMem bootDMem = (iMemIS2M, iMemDS2M) = bootIMem (mapAddr (\x -> complement 0x2000_0000 .&. x) <$> iMemIM2S) iMemDM2S -- needed for 'writes' below - dM2S = dBusWbM2S <$> output + dM2S = dBusWbM2S <$> cpuOut -- because of the memory map having the dummy at 0x0.., then instructions -- and then data memory, the D-bus is split in an "upper" and "lower" region, diff --git a/clash-vexriscv-sim/src/Utils/DebugConfig.hs b/clash-vexriscv-sim/src/Utils/DebugConfig.hs new file mode 100644 index 000000000..df051d1e0 --- /dev/null +++ b/clash-vexriscv-sim/src/Utils/DebugConfig.hs @@ -0,0 +1,34 @@ +-- SPDX-FileCopyrightText: 2022-2024 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE GADTs #-} + +module Utils.DebugConfig where + +import Prelude + +-------------------------------------- +-- +-- Debugging configuration +-- +-------------------------------------- + +data DebugConfiguration where + -- | Run a program and only output what the program writes to the + -- character-device + RunCharacterDevice :: DebugConfiguration + -- | Run a program and print detailed information of CPU bus interactions + InspectBusses :: + -- | # of cycles the program takes to initialise the instruction memory + Int -> + -- | # of "uninteresting" cycles to skip, such as runtime setup code + Int -> + -- | # of "interesting" cycles to inspect + Maybe Int -> + -- | inspect instruct-bus interactions + Bool -> + -- | inspect data-bus interactions + Bool -> + DebugConfiguration + -- | Run a program and output all the write operations + InspectWrites :: DebugConfiguration diff --git a/clash-vexriscv-sim/src/Utils/FilePath.hs b/clash-vexriscv-sim/src/Utils/FilePath.hs new file mode 100644 index 000000000..3a4c6e4b5 --- /dev/null +++ b/clash-vexriscv-sim/src/Utils/FilePath.hs @@ -0,0 +1,38 @@ +-- SPDX-FileCopyrightText: 2022-2024 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 + +module Utils.FilePath where + +import Prelude + +import System.Directory (getCurrentDirectory, doesFileExist) +import System.FilePath (isDrive, takeDirectory, ()) +import Control.Exception (throwIO) + +buildDir :: FilePath -> FilePath +buildDir baseDir = baseDir "target" + +cabalProject :: String +cabalProject = "cabal.project" + +data BuildType = Debug | Release deriving (Eq) + +instance Show BuildType where + show buildType = case buildType of + Release -> "release" + Debug -> "debug" + +rustBinsDir :: FilePath -> String -> BuildType -> FilePath +rustBinsDir baseDir rsTargetArch buildType = buildDir baseDir rsTargetArch show buildType + +findParentContaining :: String -> IO FilePath +findParentContaining filename = getCurrentDirectory >>= findParentContaining' + where + findParentContaining' dir + | isDrive dir = throwIO $ userError $ "Could not find " <> filename + | otherwise = do + exists <- doesFileExist (dir filename) + if exists + then return dir + else findParentContaining' $ takeDirectory dir diff --git a/clash-vexriscv/clash-vexriscv-sim/src/Utils/Instance.hs b/clash-vexriscv-sim/src/Utils/Instance.hs similarity index 93% rename from clash-vexriscv/clash-vexriscv-sim/src/Utils/Instance.hs rename to clash-vexriscv-sim/src/Utils/Instance.hs index 0ba606cd5..d534bf87d 100644 --- a/clash-vexriscv/clash-vexriscv-sim/src/Utils/Instance.hs +++ b/clash-vexriscv-sim/src/Utils/Instance.hs @@ -20,6 +20,6 @@ circuit :: ( "CPU_OUTPUT" ::: Signal System CpuOut , "JTAG_OUT" ::: Signal System JtagOut ) circuit clk rst input jtagIn = - vexRiscv clk rst input jtagIn + vexRiscv NoDumpVcd clk rst input jtagIn {-# CLASH_OPAQUE circuit #-} makeTopEntity 'circuit diff --git a/clash-vexriscv/clash-vexriscv-sim/src/Utils/Interconnect.hs b/clash-vexriscv-sim/src/Utils/Interconnect.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/src/Utils/Interconnect.hs rename to clash-vexriscv-sim/src/Utils/Interconnect.hs diff --git a/clash-vexriscv/clash-vexriscv-sim/src/Utils/ProgramLoad.hs b/clash-vexriscv-sim/src/Utils/ProgramLoad.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/src/Utils/ProgramLoad.hs rename to clash-vexriscv-sim/src/Utils/ProgramLoad.hs diff --git a/clash-vexriscv/clash-vexriscv-sim/src/Utils/ReadElf.hs b/clash-vexriscv-sim/src/Utils/ReadElf.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/src/Utils/ReadElf.hs rename to clash-vexriscv-sim/src/Utils/ReadElf.hs diff --git a/clash-vexriscv/clash-vexriscv-sim/src/Utils/Storage.hs b/clash-vexriscv-sim/src/Utils/Storage.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/src/Utils/Storage.hs rename to clash-vexriscv-sim/src/Utils/Storage.hs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/Cargo.lock b/clash-vexriscv-sim/test-programs/Cargo.lock similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/Cargo.lock rename to clash-vexriscv-sim/test-programs/Cargo.lock diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/Cargo.lock.license b/clash-vexriscv-sim/test-programs/Cargo.lock.license similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/Cargo.lock.license rename to clash-vexriscv-sim/test-programs/Cargo.lock.license diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/Cargo.toml b/clash-vexriscv-sim/test-programs/Cargo.toml similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/Cargo.toml rename to clash-vexriscv-sim/test-programs/Cargo.toml diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/build.rs b/clash-vexriscv-sim/test-programs/build.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/build.rs rename to clash-vexriscv-sim/test-programs/build.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/memory.x b/clash-vexriscv-sim/test-programs/memory.x similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/memory.x rename to clash-vexriscv-sim/test-programs/memory.x diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.expected b/clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.expected rename to clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.rs b/clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.rs rename to clash-vexriscv-sim/test-programs/src/bin/dyn_dispatch.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.expected b/clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.expected rename to clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.rs b/clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.rs rename to clash-vexriscv-sim/test-programs/src/bin/ebreak_exception.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/fpu_test.expected b/clash-vexriscv-sim/test-programs/src/bin/fpu_test.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/fpu_test.expected rename to clash-vexriscv-sim/test-programs/src/bin/fpu_test.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/fpu_test.rs b/clash-vexriscv-sim/test-programs/src/bin/fpu_test.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/fpu_test.rs rename to clash-vexriscv-sim/test-programs/src/bin/fpu_test.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/hello_world.expected b/clash-vexriscv-sim/test-programs/src/bin/hello_world.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/hello_world.expected rename to clash-vexriscv-sim/test-programs/src/bin/hello_world.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/hello_world.rs b/clash-vexriscv-sim/test-programs/src/bin/hello_world.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/hello_world.rs rename to clash-vexriscv-sim/test-programs/src/bin/hello_world.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/long_string.expected b/clash-vexriscv-sim/test-programs/src/bin/long_string.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/long_string.expected rename to clash-vexriscv-sim/test-programs/src/bin/long_string.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/long_string.rs b/clash-vexriscv-sim/test-programs/src/bin/long_string.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/long_string.rs rename to clash-vexriscv-sim/test-programs/src/bin/long_string.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/loop_write.expected b/clash-vexriscv-sim/test-programs/src/bin/loop_write.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/loop_write.expected rename to clash-vexriscv-sim/test-programs/src/bin/loop_write.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/loop_write.rs b/clash-vexriscv-sim/test-programs/src/bin/loop_write.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/loop_write.rs rename to clash-vexriscv-sim/test-programs/src/bin/loop_write.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/many_calls.expected b/clash-vexriscv-sim/test-programs/src/bin/many_calls.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/many_calls.expected rename to clash-vexriscv-sim/test-programs/src/bin/many_calls.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/many_calls.rs b/clash-vexriscv-sim/test-programs/src/bin/many_calls.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/many_calls.rs rename to clash-vexriscv-sim/test-programs/src/bin/many_calls.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_a.expected b/clash-vexriscv-sim/test-programs/src/bin/print_a.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_a.expected rename to clash-vexriscv-sim/test-programs/src/bin/print_a.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_a.rs b/clash-vexriscv-sim/test-programs/src/bin/print_a.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_a.rs rename to clash-vexriscv-sim/test-programs/src/bin/print_a.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_b.expected b/clash-vexriscv-sim/test-programs/src/bin/print_b.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_b.expected rename to clash-vexriscv-sim/test-programs/src/bin/print_b.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_b.rs b/clash-vexriscv-sim/test-programs/src/bin/print_b.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_b.rs rename to clash-vexriscv-sim/test-programs/src/bin/print_b.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_numbers.expected b/clash-vexriscv-sim/test-programs/src/bin/print_numbers.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_numbers.expected rename to clash-vexriscv-sim/test-programs/src/bin/print_numbers.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_numbers.rs b/clash-vexriscv-sim/test-programs/src/bin/print_numbers.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/print_numbers.rs rename to clash-vexriscv-sim/test-programs/src/bin/print_numbers.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/single_write.expected b/clash-vexriscv-sim/test-programs/src/bin/single_write.expected similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/single_write.expected rename to clash-vexriscv-sim/test-programs/src/bin/single_write.expected diff --git a/clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/single_write.rs b/clash-vexriscv-sim/test-programs/src/bin/single_write.rs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/test-programs/src/bin/single_write.rs rename to clash-vexriscv-sim/test-programs/src/bin/single_write.rs diff --git a/clash-vexriscv/clash-vexriscv-sim/tests/Tests/Jtag.hs b/clash-vexriscv-sim/tests/Tests/Jtag.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv-sim/tests/Tests/Jtag.hs rename to clash-vexriscv-sim/tests/Tests/Jtag.hs diff --git a/clash-vexriscv-sim/tests/Tests/JtagChain.hs b/clash-vexriscv-sim/tests/Tests/JtagChain.hs new file mode 100644 index 000000000..a972b2773 --- /dev/null +++ b/clash-vexriscv-sim/tests/Tests/JtagChain.hs @@ -0,0 +1,173 @@ +-- SPDX-FileCopyrightText: 2022-2024 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 + +module Tests.JtagChain where + +import Prelude + +import Test.Tasty.HUnit (Assertion, testCase, (@=?)) +import Tests.Jtag (cabalListBin, getGdb, JtagDebug (JtagDebug)) +import Utils.FilePath (findParentContaining, cabalProject, rustBinsDir, BuildType (Debug)) +import System.FilePath (()) +import System.Process +import Test.Tasty (TestTree, askOption, testGroup, defaultMainWithIngredients, includingOptions, defaultIngredients) +import Data.Data (Proxy (Proxy)) +import Test.Tasty.Options (OptionDescription(Option)) +import Control.Exception (ErrorCall (ErrorCallWithLocation)) + +import qualified Streaming.Prelude as SP +import System.IO (openFile, IOMode (ReadMode, WriteMode), hClose) +import GHC.Exception (throw, Exception (toException)) +import Data.Maybe (fromJust) +import System.Directory (doesPathExist, doesDirectoryExist) +import System.Exit (ExitCode(ExitSuccess)) +import Control.Monad (when) + +getSimulateExecPath :: IO FilePath +getSimulateExecPath = cabalListBin "clash-vexriscv-sim:clash-vexriscv-chain-bin" + +getProjectRoot :: IO FilePath +getProjectRoot = findParentContaining cabalProject + +test :: + -- | Print debug output of subprocesses + Bool -> + Assertion +test debug = do + simulateExecPath <- getSimulateExecPath + projectRoot <- getProjectRoot + let + rBD = rustBinsDir projectRoot "riscv32imc-unknown-none-elf" Debug + printAElfPath = rBD "print_a" + logAPath = projectRoot "cpu_a.log" + printBElfPath = rBD "print_b" + logBPath = projectRoot "cpu_b.log" + simDataDir = projectRoot "clash-vexriscv-sim" "data" + openocdCfgPath = simDataDir "vexriscv_chain_sim.cfg" + gdbCmdPathA = simDataDir "vexriscv_chain_gdba.cfg" + gdbCmdPathB = simDataDir "vexriscv_chain_gdbb.cfg" + gdb <- getGdb + + ensureExists logAPath 46 + ensureExists logBPath 47 + + let + vexRiscvProc = + ( proc + simulateExecPath + ["-a", printAElfPath, "-b", printBElfPath, "-A", logAPath, "-B", logBPath] + ) + { std_out = CreatePipe + , cwd = Just projectRoot + } + + openOcdProc = (proc "openocd-vexriscv" ["-f", openocdCfgPath]) + { std_err = CreatePipe + , cwd = Just projectRoot + } + + gdbProcA = (proc gdb ["--command", gdbCmdPathA]) + { std_out = CreatePipe + , cwd = Just projectRoot + } + + gdbProcB = (proc gdb ["--command", gdbCmdPathB]) + { std_out = CreatePipe + , cwd = Just projectRoot + } + + withCreateProcess vexRiscvProc $ \_ _ _ _ -> do + logAHandle <- openFile logAPath ReadMode + logBHandle <- openFile logBPath ReadMode + let + logA0 = SP.fromHandle logAHandle + logB0 = SP.fromHandle logBHandle + + logA1 <- expectLineFromStream debug logA0 "[CPU] a" 83 + logB1 <- expectLineFromStream debug logB0 "[CPU] b" 84 + + withCreateProcess openOcdProc $ \_ _ (fromJust -> openOcdStdErr) _ -> do + let openOcdStream = SP.fromHandle openOcdStdErr + _ <- waitForLineInStream debug openOcdStream "Halting processor" 88 + + withCreateProcess gdbProcA $ \_ _ _ gdbProcHandleA -> do + withCreateProcess gdbProcB $ \_ _ _ gdbProcHandleB -> do + _ <- expectLineFromStream debug logA1 "[CPU] a" 92 + _ <- expectLineFromStream debug logB1 "[CPU] b" 93 + _ <- expectLineFromStream debug logA1 "[CPU] b" 94 + _ <- expectLineFromStream debug logB1 "[CPU] a" 95 + + gdbAExitCode <- waitForProcess gdbProcHandleA + gdbBExitCode <- waitForProcess gdbProcHandleB + ExitSuccess @=? gdbAExitCode + ExitSuccess @=? gdbBExitCode + +ensureExists :: FilePath -> Int -> IO () +ensureExists file line = do + pathExists <- doesPathExist file + if not pathExists + then touchFile + else do + pathIsDir <- doesDirectoryExist file + if pathIsDir + then throw . toException + $ ErrorCallWithLocation + ("log file path `" <> file <> "` points to existing directory") + ("at line " <> show line) + else touchFile + where + touchFile = do + file' <- openFile file WriteMode + hClose file' + +expectLineFromStream + :: Bool + -> SP.Stream (SP.Of String) IO () + -> String + -> Int + -> IO (SP.Stream (SP.Of String) IO ()) +expectLineFromStream debug stream lookFor line = do + result <- SP.next stream + case result of + Right (out, next) -> do + when debug $ putStrLn $ "DBG(E): " <> out + if out == lookFor + then return next + else throw . toException $ errorHelper lookFor out line + Left _ -> expectLineFromStream debug stream lookFor line + +waitForLineInStream + :: Bool + -> SP.Stream (SP.Of String) IO () + -> String + -> Int + -> IO (SP.Stream (SP.Of String) IO ()) +waitForLineInStream debug stream lookFor line = do + result <- SP.next stream + case result of + Right (out, next) -> do + when debug $ putStrLn $ "DBG(W): " <> out + if out == lookFor + then return next + else waitForLineInStream debug next lookFor line + Left _ -> expectLineFromStream debug stream lookFor line + +errorHelper :: String -> String -> Int -> ErrorCall +errorHelper expected found loc = + ErrorCallWithLocation + ("expected `" <> expected <> "`, found `" <> found <> "`") + ("at line " <> show loc) + +tests :: TestTree +tests = askOption $ \(JtagDebug debug) -> + testGroup + "JTAG chaining" + [ testCase "Basic GDB commands, breakpoints, and program loading" (test debug) + ] + +main :: IO () +main = + defaultMainWithIngredients + (includingOptions [Option (Proxy :: Proxy JtagDebug)] : defaultIngredients) + tests diff --git a/clash-vexriscv/clash-vexriscv-sim/tests/tests.hs b/clash-vexriscv-sim/tests/tests.hs similarity index 92% rename from clash-vexriscv/clash-vexriscv-sim/tests/tests.hs rename to clash-vexriscv-sim/tests/tests.hs index fa7c7fcaf..b2e21b263 100644 --- a/clash-vexriscv/clash-vexriscv-sim/tests/tests.hs +++ b/clash-vexriscv-sim/tests/tests.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Google LLC +-- SPDX-FileCopyrightText: 2022-2024 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 {-# LANGUAGE NumericUnderscores #-} @@ -25,8 +25,10 @@ import Test.Tasty.Options import Utils.ProgramLoad (loadProgramDmem) import Utils.Cpu (cpu) +import VexRiscv (DumpVcd(NoDumpVcd)) import qualified Tests.Jtag +import qualified Tests.JtagChain runProgramExpect :: -- | action to copy ELF file @@ -41,9 +43,9 @@ runProgramExpect act n expected = withSystemTempFile "ELF" $ \fp _ -> do (iMem, dMem) <- withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $ loadProgramDmem fp - let _all@(unbundle -> (_circuit, writes, _iBus, _dBus)) = + let _all@(unbundle -> (_circuit, _, writes, _iBus, _dBus)) = withClockResetEnable @System clockGen (resetGenN (SNat @2)) enableGen $ - bundle (cpu Nothing iMem dMem) + bundle (cpu NoDumpVcd Nothing iMem dMem) let output = L.take (BS.length expected) $ flip mapMaybe (sampleN_lazy n writes) $ \case @@ -60,6 +62,7 @@ findTests :: IO [(String, FilePath, FilePath)] -- test name bin path expected-path findTests srcDir binDir = do + srcFiles <- listDirectory srcDir let expectFiles = L.filter (\p -> takeExtension p == ".expected") srcFiles @@ -102,7 +105,7 @@ runTest :: FilePath -> TestTree runTest name mode n elfPath expectPath = - testCase ("Integration test `" <> name <> "` (" <> mode <> ")") $ do + testCase ("Integration test " <> name <> " (" <> mode <> ")") $ do expected <- BS.readFile expectPath let act = copyFile elfPath @@ -134,6 +137,7 @@ main = do [ testGroup "Debug builds" debugTestCases , testGroup "Release builds" releaseTestCases , Tests.Jtag.tests + , Tests.JtagChain.tests ] defaultMainWithIngredients diff --git a/clash-vexriscv/.cargo/config.toml b/clash-vexriscv/.cargo/config.toml deleted file mode 100644 index be5e2d99f..000000000 --- a/clash-vexriscv/.cargo/config.toml +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[build] -target = "riscv32imc-unknown-none-elf" -rustflags = ["-C", "target-feature=+f"] diff --git a/clash-vexriscv/.envrc b/clash-vexriscv/.envrc deleted file mode 100644 index c551fc7b9..000000000 --- a/clash-vexriscv/.envrc +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -use_nix -j8 diff --git a/clash-vexriscv/.github/cabal.project b/clash-vexriscv/.github/cabal.project deleted file mode 100644 index 1d9dfa214..000000000 --- a/clash-vexriscv/.github/cabal.project +++ /dev/null @@ -1,8 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: CC0-1.0 -package clash-vexriscv - ghc-options: -Werror - -package clash-vexriscv-sim - ghc-options: -Werror diff --git a/clash-vexriscv/.github/docker/Dockerfile b/clash-vexriscv/.github/docker/Dockerfile deleted file mode 100644 index 754c775b1..000000000 --- a/clash-vexriscv/.github/docker/Dockerfile +++ /dev/null @@ -1,82 +0,0 @@ -# syntax=docker/dockerfile:1.2 - -# SPDX-FileCopyrightText: 2024 Google LLC - -# SPDX-License-Identifier: CC0-1.0 - -ARG UBUNTU_VERSION -FROM ubuntu:$UBUNTU_VERSION AS builder - -LABEL vendor="QBayLogic B.V." maintainer="devops@qbaylogic.com" -ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 LC_ALL=C.UTF-8 PREFIX=/opt - -ARG DEPS_COMMON="build-essential ca-certificates curl git locales ca-certificates" - -RUN apt-get update \ - && apt-get install -y --no-install-recommends $DEPS_COMMON \ - && locale-gen en_US.UTF-8 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -FROM builder AS build-openocd-vexriscv - -ARG DEPS_OPENOCD_VEXRISCV="autoconf automake libtool pkg-config libusb-1.0-0-dev libftdi-dev libhidapi-dev libusb-dev libyaml-dev" - -RUN apt-get update \ - && apt-get install -y --no-install-recommends $DEPS_OPENOCD_VEXRISCV \ - && git clone --recursive https://github.com/SpinalHDL/openocd_riscv.git \ - && cd openocd_riscv \ - && ./bootstrap \ - && ./configure --enable-ftdi --enable-dummy --prefix=/opt \ - && make -j$(nproc) \ - && make install - -FROM builder AS build-verilator - -ARG DEPS_VERILATOR="perl python3 make autoconf g++ flex bison ccache libgoogle-perftools-dev numactl perl-doc libfl2 libfl-dev zlib1g zlib1g-dev help2man" -RUN apt-get update \ - && apt-get install -y --no-install-recommends $DEPS_VERILATOR - -ARG verilator_version="v5.020" -RUN git clone https://github.com/verilator/verilator verilator \ - && cd verilator \ - && git checkout $verilator_version \ - && autoconf \ - && ./configure --prefix $PREFIX \ - && make PREFIX=$PREFIX -j$(nproc) \ - && make PREFIX=$PREFIX install \ - && cd ../.. \ - && rm -Rf verilator - -FROM builder AS build-ghc - -ARG ghcup_version="0.1.22.0" - -# Must be explicitly set -ARG ghc_version -ARG cabal_version - -RUN curl "https://downloads.haskell.org/~ghcup/$ghcup_version/x86_64-linux-ghcup-$ghcup_version" --output /usr/bin/ghcup \ - && chmod +x /usr/bin/ghcup \ - && ghcup install ghc $ghc_version --set \ - && ghcup install cabal $cabal_version --set - -FROM builder AS run - -LABEL vendor="QBayLogic B.V." maintainer="devops@qbaylogic.com" -ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 LC_ALL=C.UTF-8 PATH="$PATH:/opt/bin:/root/.ghcup/bin" - -ARG DEPS_RUNTIME="gnupg pkg-config openjdk-8-jdk gdb-multiarch picocom libtinfo5 libtinfo-dev build-essential curl libc6-dev libgmp10-dev python3 ccache libftdi1 libhidapi-hidraw0 libusb-1.0-0 libyaml-0-2" -RUN apt-get update \ - && apt-get install -y --no-install-recommends $DEPS_RUNTIME \ - && echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | tee /etc/apt/sources.list.d/sbt.list \ - && echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | tee /etc/apt/sources.list.d/sbt_old.list \ - && curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | apt-key add \ - && apt-get update \ - && apt-get install -y --no-install-recommends sbt \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -COPY --from=build-verilator /opt /opt -COPY --from=build-openocd-vexriscv /opt /opt -COPY --from=build-ghc /root/.ghcup /root/.ghcup diff --git a/clash-vexriscv/.github/workflows/ci.yml b/clash-vexriscv/.github/workflows/ci.yml deleted file mode 100644 index d94d4fbf4..000000000 --- a/clash-vexriscv/.github/workflows/ci.yml +++ /dev/null @@ -1,180 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -name: CI - -on: [push] - -# Updating Rust versions: -# -# When updating to a newer (or older) version of Rust for the main build process -# then the version should be updated in the /rust-toolchain.toml file too. -# That file determines which version gets used locally on developer machines. - -jobs: - license-check: - runs-on: ubuntu-22.04 - container: - image: ubuntu:22.04 - steps: - - uses: actions/checkout@v4 - - name: REUSE Compliance Check - uses: fsfe/reuse-action@v2 - - - rust-checks: - name: Rust checks - runs-on: ubuntu-22.04 - container: - image: ubuntu:22.04 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - apt-get update - apt-get install -y curl build-essential - - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.67 # See Note [Updating Rust versions] - profile: minimal - target: riscv32imc-unknown-none-elf - components: clippy, rustfmt - - - name: Rust formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --target riscv32imc-unknown-none-elf --all-features - - rust-build-programs: - name: Build Programs - runs-on: ubuntu-22.04 - container: - image: ubuntu:22.04 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - apt-get update - apt-get install -y curl build-essential - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.67.1 # See Note [Updating Rust versions] - profile: minimal - target: riscv32imc-unknown-none-elf - components: clippy, rustfmt - - - name: Caching - uses: Swatinem/rust-cache@v2 - - - name: Build release binaries - uses: actions-rs/cargo@v1 - with: - command: build - args: --release - - - name: Build debug binaries - uses: actions-rs/cargo@v1 - with: - command: build - - - name: Archive Integration Test Binaries - run: | - cd clash-vexriscv-sim; sh bundle_test_binaries.sh - - - name: Upload Integration Test Binaries - uses: actions/upload-artifact@v3 - with: - name: vexriscv-test-binaries - path: clash-vexriscv-sim/vexriscv-test-binaries.tar - - - vex-riscv: - name: VexRiscv integration - runs-on: ubuntu-22.04 - needs: [rust-build-programs] - - strategy: - fail-fast: false - matrix: - ghc: - - "9.0.2" - - "9.2.8" - - "9.4.8" - - "9.6.6" - - container: - image: ghcr.io/clash-lang/clash-vexriscv-ci:${{ matrix.ghc }}-20240823 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Update Cabal index info - run: | - cp .github/cabal.project cabal.project.local - cabal update - cabal freeze - - name: Cache - uses: actions/cache@v3 - with: - path: | - ~/.local/state/cabal/store/ - key: packages-cachebust-3-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze', 'cabal.project') }} - restore-keys: packages-cachebust-3-${{ matrix.ghc }} - - - name: Stash existing VexRiscv.v - run: | - cp clash-vexriscv/example-cpu/VexRiscv.v clash-vexriscv/example-cpu/VexRiscv.v.comitted - - name: Build clash-vexriscv - run: | - export PKG_CONFIG_PATH=/opt/share/pkgconfig/:${PKG_CONFIG_PATH} - cabal build clash-vexriscv - - name: Check whether committed VexRiscv.v corresponds to generated one - run: | - diff -u clash-vexriscv/example-cpu/VexRiscv.v clash-vexriscv/example-cpu/VexRiscv.v.comitted - - name: Build clash-vexriscv-sim - run: | - cabal build clash-vexriscv-sim - - - name: Download VexRiscv Integration Tests - uses: actions/download-artifact@v3 - with: - name: vexriscv-test-binaries - - - name: Work around dubious owner error - run: | - git config --global --add safe.directory "$(pwd)" - - - name: Extract VexRiscv Integration Tests - run: | - tar -x -f vexriscv-test-binaries.tar - - - name: OpenOCD bin symlink - run: | - ln -s /opt/bin/openocd /opt/bin/openocd-vexriscv - - - name: Run `clash-vexriscv` unittests - run: | - cabal run clash-vexriscv:unittests - - - name: Run `clash-vexriscv-sim` unittests - run: | - cabal run clash-vexriscv-sim:unittests -- -j2 - - - name: Run `clash-vexriscv-sim` HDL test - run: | - cabal run clash-vexriscv-sim:hdl-test diff --git a/clash-vexriscv/.gitignore b/clash-vexriscv/.gitignore index fc2b16ff9..f4fbe7e5e 100644 --- a/clash-vexriscv/.gitignore +++ b/clash-vexriscv/.gitignore @@ -2,72 +2,5 @@ # # SPDX-License-Identifier: CC0-1.0 -ignore -dist/ -build/ -dist-newstyle/ -.stack-work/ -stack.yaml.lock -cabal-dev -/cabal.project.local -.ghc.environment.* -a.out -*.o -*.o-boot -*.hi -*.hi-boot -*.po -*.po-boot -*.p_o -*.p_o-boot -*.chi -*.chs.h -*.dyn_o -*.dyn_o-boot -*.dyn_hi -*.dyn_hi-boot -*.local -.virtualenv -.hpc -.hsenv -.cabal-sandbox/ -cabal.sandbox.config -cabal.config -*.prof -*.aux -*.hp -*.bin -*.log -*.tar.gz - -*~ -*.DS_Store - -# IntelliJ -/.idea -*.iml - -# Nix output -result* - -# Sphinx output -_build/ - -# rewrite histories -*.dat - -# Rust build files -target/ - -# Vivado -vivado.jou -log -vivado_* -tight_setup_hold_pins.txt -.Xil - -# Verilator debug output -simulation_dump.vcd - -# Clash output -verilog +build_out_dir +vexriscv-test-binaries.tar diff --git a/clash-vexriscv/.reuse/dep5 b/clash-vexriscv/.reuse/dep5 deleted file mode 100644 index aacbba431..000000000 --- a/clash-vexriscv/.reuse/dep5 +++ /dev/null @@ -1,16 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: clash-vexrisc -Upstream-Contact: QBayLogic -Source: https://github.com/clash-lang/clash-vexrisc - - -# Sample paragraph, commented out: -# -# Files: src/* -# Copyright: $YEAR $NAME <$CONTACT> -# License: ... - -Files: clash-vexriscv-sim/test-programs/src/bin/*.expected -Copyright: 2022-2023 Google LLC -License: CC0-1.0 - diff --git a/clash-vexriscv/.vscode/settings.json b/clash-vexriscv/.vscode/settings.json deleted file mode 100644 index f5b064694..000000000 --- a/clash-vexriscv/.vscode/settings.json +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Google LLC - -// SPDX-License-Identifier: CC0-1.0 -{ - "files.exclude": { - "**/*.dyn_hi": true, - "**/*.dyn_o": true, - "**/*.hi": true, - "**/*.o": true, - "**/*.o-boot": true, - "**/*.hi-boot": true, - "**/dist-newstyle": true, - "**/.stack-work": true, - "**/.ghc.environment.*": true, - }, - "files.insertFinalNewline": true, - "files.trimFinalNewlines": true, - "files.trimTrailingWhitespace": true, - "editor.tabSize": 2, - "[rust]": { - "editor.tabSize": 4 - }, - "[haskell]": { - "editor.formatOnSave": false - }, -} diff --git a/clash-vexriscv/LICENSE b/clash-vexriscv/LICENSE index 261eeb9e9..d64569567 100644 --- a/clash-vexriscv/LICENSE +++ b/clash-vexriscv/LICENSE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/clash-vexriscv/LICENSES/Apache-2.0.txt b/clash-vexriscv/LICENSES/Apache-2.0.txt deleted file mode 100644 index 137069b82..000000000 --- a/clash-vexriscv/LICENSES/Apache-2.0.txt +++ /dev/null @@ -1,73 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/clash-vexriscv/LICENSES/CC0-1.0.txt b/clash-vexriscv/LICENSES/CC0-1.0.txt deleted file mode 100644 index 0e259d42c..000000000 --- a/clash-vexriscv/LICENSES/CC0-1.0.txt +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/clash-vexriscv/LICENSES/MIT.txt b/clash-vexriscv/LICENSES/MIT.txt deleted file mode 100644 index 2071b23b0..000000000 --- a/clash-vexriscv/LICENSES/MIT.txt +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/clash-vexriscv/clash-vexriscv/Makefile b/clash-vexriscv/Makefile similarity index 100% rename from clash-vexriscv/clash-vexriscv/Makefile rename to clash-vexriscv/Makefile diff --git a/clash-vexriscv/README.md b/clash-vexriscv/README.md index 5bcc2e1e5..7e205522b 100644 --- a/clash-vexriscv/README.md +++ b/clash-vexriscv/README.md @@ -1,39 +1,29 @@ -# VexRiscv core for Clash - -This repository contains a [VexRiscv](https://github.com/SpinalHDL/VexRiscv) based CPU core and -bindings for use in [Clash](https://clash-lang.org/). - -For now this repository only contains one -[example CPU configuration](clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala), -which implements a 32 bit IMC RISC-V core. - -This package includes simulation via [`verilator`](https://github.com/verilator/verilator) as -well as a black-box for Verilog synthesis. - -The core interfaces with other components via [Wishbone](https://cdn.opencores.org/downloads/wbspec_b4.pdf) -interfaces, using [`clash-protocols`](https://github.com/clash-lang/clash-protocols) types. +# VexRiscv integration ## Building -For building the CPU, the following software needs to be installed and available in the `PATH`: +For building the CPU, the following software needs to be installed and available +in the `PATH`: - a recent JDK installation - SBT, the scala build tool - a recent C compiler -- `verilator`, at least version 5.001 (development version at time of writing) +- `verilator` - `make` for building the verilated library and FFI code -## Notes for using the core +## Notes for using the core: -- VexRiscv has a "reset vector" for the instruction bus. This is the initial PC that gets fetched. - This address only gets presented to the IBUS after at least one cycle of RST being asserted. +- VexRiscv has a "reset vector" for the instruction bus. This is the initial + PC that gets fetched. This address only gets presented to the IBUS after at + least one cycle of RST being asserted. -- The contents of memories need to be stored in little endian. This means that for example the - contents of an ELF file need to be endian-swapped before being used as the contents of the - instruction storage. This applies to all storages. +- The contents of memories need to be stored in little endian. This means that + for example the contents of an ELF file need to be endian-swapped before being + used as the contents of the instruction storage. + This applies to all storages. diff --git a/clash-vexriscv/clash-vexriscv/Setup.hs b/clash-vexriscv/Setup.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/Setup.hs rename to clash-vexriscv/Setup.hs diff --git a/clash-vexriscv/cabal.project b/clash-vexriscv/cabal.project deleted file mode 100644 index 7dcd22a5a..000000000 --- a/clash-vexriscv/cabal.project +++ /dev/null @@ -1,60 +0,0 @@ --- SPDX-FileCopyrightText: 2022 Google LLC --- --- SPDX-License-Identifier: CC0-1.0 - -packages: - clash-vexriscv/ - clash-vexriscv-sim/ - -write-ghc-environment-files: always -tests: True - - -package clash-prelude - flags: -multiple-hidden - --- index state, to go along with the cabal.project.freeze file. update the index --- state by running `cabal update` twice and looking at the index state it --- displays to you (as the second update will be a no-op) -index-state: 2023-09-28T08:48:26Z - --- Needed to simulate dynamic clocks. -source-repository-package - type: git - location: https://github.com/clash-lang/clash-compiler.git - tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a - subdir: clash-prelude - -source-repository-package - type: git - location: https://github.com/clash-lang/clash-compiler.git - tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a - subdir: clash-ghc - -source-repository-package - type: git - location: https://github.com/clash-lang/clash-compiler.git - tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a - subdir: clash-lib - -source-repository-package - type: git - location: https://github.com/clash-lang/clash-compiler.git - tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a - subdir: clash-cores - -source-repository-package - type: git - location: https://github.com/clash-lang/clash-compiler.git - tag: a26bb9ef3cb57f24daec3da30db9fb54af91297a - subdir: clash-prelude-hedgehog - -source-repository-package - type: git - location: https://github.com/clash-lang/clash-protocols.git - tag: eb76cd1be746ae91beff60c0f16d8c1dd888662c - -source-repository-package - type: git - location: https://github.com/cchalmers/circuit-notation.git - tag: 19b386c4aa3ff690758ae089c7754303f3500cc9 diff --git a/clash-vexriscv/clash-vexriscv-sim/LICENSE b/clash-vexriscv/clash-vexriscv-sim/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/clash-vexriscv/clash-vexriscv-sim/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/clash-vexriscv/clash-vexriscv/clash-vexriscv.cabal b/clash-vexriscv/clash-vexriscv.cabal similarity index 100% rename from clash-vexriscv/clash-vexriscv/clash-vexriscv.cabal rename to clash-vexriscv/clash-vexriscv.cabal diff --git a/clash-vexriscv/clash-vexriscv/clash-vexriscv.cabal.license b/clash-vexriscv/clash-vexriscv.cabal.license similarity index 100% rename from clash-vexriscv/clash-vexriscv/clash-vexriscv.cabal.license rename to clash-vexriscv/clash-vexriscv.cabal.license diff --git a/clash-vexriscv/clash-vexriscv/.gitignore b/clash-vexriscv/clash-vexriscv/.gitignore deleted file mode 100644 index f4fbe7e5e..000000000 --- a/clash-vexriscv/clash-vexriscv/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -build_out_dir -vexriscv-test-binaries.tar diff --git a/clash-vexriscv/clash-vexriscv/LICENSE b/clash-vexriscv/clash-vexriscv/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/clash-vexriscv/clash-vexriscv/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/clash-vexriscv/clash-vexriscv/README.md b/clash-vexriscv/clash-vexriscv/README.md deleted file mode 100644 index 7e205522b..000000000 --- a/clash-vexriscv/clash-vexriscv/README.md +++ /dev/null @@ -1,29 +0,0 @@ - - -# VexRiscv integration - -## Building - -For building the CPU, the following software needs to be installed and available -in the `PATH`: - -- a recent JDK installation -- SBT, the scala build tool -- a recent C compiler -- `verilator` -- `make` for building the verilated library and FFI code - -## Notes for using the core: - -- VexRiscv has a "reset vector" for the instruction bus. This is the initial - PC that gets fetched. This address only gets presented to the IBUS after at - least one cycle of RST being asserted. - -- The contents of memories need to be stored in little endian. This means that - for example the contents of an ELF file need to be endian-swapped before being - used as the contents of the instruction storage. - This applies to all storages. diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/.gitignore b/clash-vexriscv/example-cpu/.gitignore similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/.gitignore rename to clash-vexriscv/example-cpu/.gitignore diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/ExampleCpu.yaml b/clash-vexriscv/example-cpu/ExampleCpu.yaml similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/ExampleCpu.yaml rename to clash-vexriscv/example-cpu/ExampleCpu.yaml diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/ExampleCpu.yaml.license b/clash-vexriscv/example-cpu/ExampleCpu.yaml.license similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/ExampleCpu.yaml.license rename to clash-vexriscv/example-cpu/ExampleCpu.yaml.license diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/VexRiscv.v b/clash-vexriscv/example-cpu/VexRiscv.v similarity index 99% rename from clash-vexriscv/clash-vexriscv/example-cpu/VexRiscv.v rename to clash-vexriscv/example-cpu/VexRiscv.v index 83ac4ad63..8e26ec68e 100644 --- a/clash-vexriscv/clash-vexriscv/example-cpu/VexRiscv.v +++ b/clash-vexriscv/example-cpu/VexRiscv.v @@ -5712,11 +5712,11 @@ module VexRiscv ( assign IBusSimplePlugin_rspJoin_rspBuffer_c_io_flush = 1'b0; always @(posedge clk or posedge reset) begin if(reset) begin - IBusSimplePlugin_fetchPc_pcReg <= 32'h80000000; + IBusSimplePlugin_fetchPc_pcReg <= 32'h20000000; IBusSimplePlugin_fetchPc_correctionReg <= 1'b0; IBusSimplePlugin_fetchPc_booted <= 1'b0; IBusSimplePlugin_fetchPc_inc <= 1'b0; - IBusSimplePlugin_decodePc_pcReg <= 32'h80000000; + IBusSimplePlugin_decodePc_pcReg <= 32'h20000000; _zz_IBusSimplePlugin_iBusRsp_stages_1_input_valid_1 <= 1'b0; IBusSimplePlugin_decompressor_bufferValid <= 1'b0; IBusSimplePlugin_decompressor_throw2BytesReg <= 1'b0; diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/VexRiscv.v.license b/clash-vexriscv/example-cpu/VexRiscv.v.license similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/VexRiscv.v.license rename to clash-vexriscv/example-cpu/VexRiscv.v.license diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/build.sbt b/clash-vexriscv/example-cpu/build.sbt similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/build.sbt rename to clash-vexriscv/example-cpu/build.sbt diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/lib/update-vexriscv.py b/clash-vexriscv/example-cpu/lib/update-vexriscv.py similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/lib/update-vexriscv.py rename to clash-vexriscv/example-cpu/lib/update-vexriscv.py diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar b/clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar rename to clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar.license b/clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar.license similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar.license rename to clash-vexriscv/example-cpu/lib/vexriscv_2.11-2.0.0-457ae5c7e5c8183f0ba7c51f7f0301d05eb8ced1.jar.license diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/project/Dependencies.scala b/clash-vexriscv/example-cpu/project/Dependencies.scala similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/project/Dependencies.scala rename to clash-vexriscv/example-cpu/project/Dependencies.scala diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/project/build.properties b/clash-vexriscv/example-cpu/project/build.properties similarity index 100% rename from clash-vexriscv/clash-vexriscv/example-cpu/project/build.properties rename to clash-vexriscv/example-cpu/project/build.properties diff --git a/clash-vexriscv/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala b/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala similarity index 97% rename from clash-vexriscv/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala rename to clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala index ce4358a44..35c6ca63a 100644 --- a/clash-vexriscv/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala +++ b/clash-vexriscv/example-cpu/src/main/scala/example/ExampleCpu.scala @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 Google LLC +// SPDX-FileCopyrightText: 2022-2024 Google LLC // // SPDX-License-Identifier: Apache-2.0 @@ -17,7 +17,7 @@ object ExampleCpu extends App { val config = VexRiscvConfig( plugins = List( new IBusSimplePlugin( - resetVector = 0x80000000l, + resetVector = 0x20000000l, cmdForkOnSecondStage = false, cmdForkPersistence = false, prediction = NONE, diff --git a/clash-vexriscv/nix/nixpkgs.nix b/clash-vexriscv/nix/nixpkgs.nix deleted file mode 100644 index 83112d245..000000000 --- a/clash-vexriscv/nix/nixpkgs.nix +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -{ sources ? import ./sources.nix }: - -let - rust_overlay = import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"); - overlay = _: nixpkgs: { - # Nix tooling - gitignore = import sources.gitignore { inherit (nixpkgs) lib; }; - openocd-vexriscv = import ./openocd-vexriscv.nix { inherit (nixpkgs) pkgs; }; - - # Haskell overrides - haskellPackages = nixpkgs.haskellPackages.override { - overrides = self: super: { - # Disables library profiling, documentation building, and building/running - # test suites to speed up compilation times. This should only be applied - # to local packages. Adding them to others will defeat caching. - applyPrefs = p: - (nixpkgs.haskell.lib.disableLibraryProfiling - (nixpkgs.haskell.lib.dontHaddock - (nixpkgs.haskell.lib.dontCheck p))); - - # External overrides - # .. no external overrides yet .. - - # Internal overrides - # .. no internal overrides yet .. - }; - }; - }; - -in import sources.nixpkgs { overlays = [ rust_overlay overlay ]; } diff --git a/clash-vexriscv/nix/openocd-vexriscv.nix b/clash-vexriscv/nix/openocd-vexriscv.nix deleted file mode 100644 index ff2d6b777..000000000 --- a/clash-vexriscv/nix/openocd-vexriscv.nix +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Google LLC - -# SPDX-License-Identifier: CC0-1.0 -{ pkgs ? import ./nixpkgs.nix {} }: - -pkgs.stdenv.mkDerivation rec { - name = "openocd-vexriscv"; - - buildInputs = [ - pkgs.autoconf - pkgs.automake - pkgs.coreutils - pkgs.git - pkgs.libtool - pkgs.libusb1 - pkgs.libyaml - pkgs.pkg-config - pkgs.texinfo - pkgs.which - ]; - - src = pkgs.fetchgit { - url = "https://github.com/SpinalHDL/openocd_riscv.git"; - rev = "058dfa50d625893bee9fecf8d604141911fac125"; - sha256 = "sha256-UuX4Zfr9DiJx60nvBAv+9xCbWXExrk5KNSC5V5e4rsw="; - fetchSubmodules = true; - deepClone = true; - postFetch = '' - # See: https://github.com/NixOS/nixpkgs/issues/8567#issuecomment-1846499599 - find "$out/" -type d -name '.git' | xargs rm -rf - ''; - }; - - installPhase = '' - SKIP_SUBMODULE=1 ./bootstrap - ./configure --enable-ftdi --enable-dummy --prefix=$out - make -j $(nproc) - make install - mv $out/bin/openocd $out/bin/openocd-vexriscv - ''; -} diff --git a/clash-vexriscv/nix/sources.json b/clash-vexriscv/nix/sources.json deleted file mode 100644 index 01e657fb9..000000000 --- a/clash-vexriscv/nix/sources.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "gitignore": { - "branch": "master", - "description": "Nix functions for filtering local git sources", - "homepage": "", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", - "sha256": "1rlja3ba9s1n0icy3aarwhx9hk1jyfgngzizbn1afwwdlpvdlqw0", - "type": "tarball", - "url": "https://github.com/hercules-ci/gitignore.nix/archive/43e1aa1308018f37118e34d3a9cb4f5e75dc11d5.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - }, - "nixpkgs": { - "branch": "nixos-23.11", - "description": "Nix Packages collection", - "homepage": "", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5bf1cadb72ab4e77cb0b700dab76bcdaf88f706b", - "sha256": "0zjwk71lsri9zmlmhwgz1ny9dv91kaznii2wjff4g2d3w47gy8nj", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/5bf1cadb72ab4e77cb0b700dab76bcdaf88f706b.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - } -} diff --git a/clash-vexriscv/nix/sources.nix b/clash-vexriscv/nix/sources.nix deleted file mode 100644 index 9a01c8acf..000000000 --- a/clash-vexriscv/nix/sources.nix +++ /dev/null @@ -1,194 +0,0 @@ -# This file has been generated by Niv. - -let - - # - # The fetchers. fetch_ fetches specs of type . - # - - fetch_file = pkgs: name: spec: - let - name' = sanitizeName name + "-src"; - in - if spec.builtin or true then - builtins_fetchurl { inherit (spec) url sha256; name = name'; } - else - pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; - - fetch_tarball = pkgs: name: spec: - let - name' = sanitizeName name + "-src"; - in - if spec.builtin or true then - builtins_fetchTarball { name = name'; inherit (spec) url sha256; } - else - pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; - - fetch_git = name: spec: - let - ref = - if spec ? ref then spec.ref else - if spec ? branch then "refs/heads/${spec.branch}" else - if spec ? tag then "refs/tags/${spec.tag}" else - abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"; - submodules = if spec ? submodules then spec.submodules else false; - submoduleArg = - let - nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0; - emptyArgWithWarning = - if submodules == true - then - builtins.trace - ( - "The niv input \"${name}\" uses submodules " - + "but your nix's (${builtins.nixVersion}) builtins.fetchGit " - + "does not support them" - ) - {} - else {}; - in - if nixSupportsSubmodules - then { inherit submodules; } - else emptyArgWithWarning; - in - builtins.fetchGit - ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg); - - fetch_local = spec: spec.path; - - fetch_builtin-tarball = name: throw - ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. - $ niv modify ${name} -a type=tarball -a builtin=true''; - - fetch_builtin-url = name: throw - ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. - $ niv modify ${name} -a type=file -a builtin=true''; - - # - # Various helpers - # - - # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 - sanitizeName = name: - ( - concatMapStrings (s: if builtins.isList s then "-" else s) - ( - builtins.split "[^[:alnum:]+._?=-]+" - ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) - ) - ); - - # The set of packages used when specs are fetched using non-builtins. - mkPkgs = sources: system: - let - sourcesNixpkgs = - import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; - hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; - hasThisAsNixpkgsPath = == ./.; - in - if builtins.hasAttr "nixpkgs" sources - then sourcesNixpkgs - else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then - import {} - else - abort - '' - Please specify either (through -I or NIX_PATH=nixpkgs=...) or - add a package called "nixpkgs" to your sources.json. - ''; - - # The actual fetching function. - fetch = pkgs: name: spec: - - if ! builtins.hasAttr "type" spec then - abort "ERROR: niv spec ${name} does not have a 'type' attribute" - else if spec.type == "file" then fetch_file pkgs name spec - else if spec.type == "tarball" then fetch_tarball pkgs name spec - else if spec.type == "git" then fetch_git name spec - else if spec.type == "local" then fetch_local spec - else if spec.type == "builtin-tarball" then fetch_builtin-tarball name - else if spec.type == "builtin-url" then fetch_builtin-url name - else - abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; - - # If the environment variable NIV_OVERRIDE_${name} is set, then use - # the path directly as opposed to the fetched source. - replace = name: drv: - let - saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name; - ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; - in - if ersatz == "" then drv else - # this turns the string into an actual Nix path (for both absolute and - # relative paths) - if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}"; - - # Ports of functions for older nix versions - - # a Nix version of mapAttrs if the built-in doesn't exist - mapAttrs = builtins.mapAttrs or ( - f: set: with builtins; - listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) - ); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 - range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 - stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 - stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); - concatMapStrings = f: list: concatStrings (map f list); - concatStrings = builtins.concatStringsSep ""; - - # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 - optionalAttrs = cond: as: if cond then as else {}; - - # fetchTarball version that is compatible between all the versions of Nix - builtins_fetchTarball = { url, name ? null, sha256 }@attrs: - let - inherit (builtins) lessThan nixVersion fetchTarball; - in - if lessThan nixVersion "1.12" then - fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) - else - fetchTarball attrs; - - # fetchurl version that is compatible between all the versions of Nix - builtins_fetchurl = { url, name ? null, sha256 }@attrs: - let - inherit (builtins) lessThan nixVersion fetchurl; - in - if lessThan nixVersion "1.12" then - fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) - else - fetchurl attrs; - - # Create the final "sources" from the config - mkSources = config: - mapAttrs ( - name: spec: - if builtins.hasAttr "outPath" spec - then abort - "The values in sources.json should not have an 'outPath' attribute" - else - spec // { outPath = replace name (fetch config.pkgs name spec); } - ) config.sources; - - # The "config" used by the fetchers - mkConfig = - { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null - , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile) - , system ? builtins.currentSystem - , pkgs ? mkPkgs sources system - }: rec { - # The sources, i.e. the attribute set of spec name to spec - inherit sources; - - # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers - inherit pkgs; - }; - -in -mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); } diff --git a/clash-vexriscv/rust-toolchain.toml b/clash-vexriscv/rust-toolchain.toml deleted file mode 100644 index 87201e728..000000000 --- a/clash-vexriscv/rust-toolchain.toml +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[toolchain] - -# !!!!! -# -# When the Rust version here gets updated, also update it in -# .github/workflows/ci.yml. The version is written out for multiple jobs, so -# please make sure that all jobs are updated. -# -# !!!!! - -channel = "1.67.1" -targets = [ "riscv32imc-unknown-none-elf", "x86_64-unknown-linux-gnu" ] -profile = "minimal" -components = [ "clippy", "rustfmt" ] diff --git a/clash-vexriscv/shell.nix b/clash-vexriscv/shell.nix deleted file mode 100644 index 714afdd50..000000000 --- a/clash-vexriscv/shell.nix +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -{ pkgs ? import nix/nixpkgs.nix {} }: -pkgs.mkShell { - name = "shell"; - buildInputs = - [ - pkgs.gcc - pkgs.pkg-config - pkgs.sbt - pkgs.scala - pkgs.verilator - - # Haskell toolchain - pkgs.cabal-install - # pkgs.haskell.compiler.ghc90 - # pkgs.haskell.compiler.ghc92 - pkgs.haskell.compiler.ghc94 - # pkgs.haskell.compiler.ghc96 - - (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml) - - # VexRiscV needs a special openocd - pkgs.openocd-vexriscv - pkgs.gdb - - # For Cabal to clone git repos - pkgs.git - - # For upgrading Nix env. To update dependencies (within bounds of the currently - # tracking NixOS version) use: - # - # niv update - # - # If you want to upgrade nixpkgs to another NixOS version, use: - # - # niv update nixpkgs -b nixos-23.11 - # - pkgs.niv - ] - ; - - shellHook = '' - # Prevents Perl warnings - export LC_ALL="C.UTF-8"; - - # Mixing Nix Cabal and non-Nix Cabal yields some weird linking errors. - export CABAL_DIR="$HOME/.cabal-nix"; - ''; -} diff --git a/clash-vexriscv/clash-vexriscv/src/VexRiscv.hs b/clash-vexriscv/src/VexRiscv.hs similarity index 92% rename from clash-vexriscv/clash-vexriscv/src/VexRiscv.hs rename to clash-vexriscv/src/VexRiscv.hs index 88ea12877..25e8338f7 100644 --- a/clash-vexriscv/clash-vexriscv/src/VexRiscv.hs +++ b/clash-vexriscv/src/VexRiscv.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Google LLC +-- SPDX-FileCopyrightText: 2022-2024 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 @@ -20,9 +20,10 @@ import Clash.Signal.Internal import Data.Bifunctor (first) import Data.String.Interpolate (__i) import Data.Word (Word64) -import Foreign (Ptr) +import Foreign (Ptr, nullPtr) import Foreign.Marshal (alloca) import Foreign.Storable +import Foreign.C.String (newCString) import GHC.IO (unsafePerformIO, unsafeInterleaveIO) import GHC.Stack (HasCallStack) import Language.Haskell.TH.Syntax @@ -66,6 +67,8 @@ data CpuOut = CpuOut } deriving (Generic, NFDataX, ShowX, Eq, BitPack) +data DumpVcd = DumpVcd FilePath | NoDumpVcd + data Jtag (dom :: Domain) @@ -81,6 +84,7 @@ vexRiscv :: forall dom . ( HasCallStack , KnownDomain dom) => + DumpVcd -> Clock dom -> Reset dom -> Signal dom CpuIn -> @@ -88,7 +92,7 @@ vexRiscv :: ( Signal dom CpuOut , Signal dom JtagOut ) -vexRiscv clk rst cpuInput jtagInput = +vexRiscv dumpVcd clk rst cpuInput jtagInput = ( CpuOut <$> (WishboneM2S <$> iBus_ADR @@ -161,7 +165,7 @@ vexRiscv clk rst cpuInput jtagInput = , dBus_BTE , debug_resetOut , jtag_TDO - ) = vexRiscv# sourcePath clk rst + ) = vexRiscv# dumpVcd sourcePath clk rst timerInterrupt externalInterrupt softwareInterrupt @@ -181,7 +185,8 @@ vexRiscv clk rst cpuInput jtagInput = vexRiscv# :: KnownDomain dom - => String + => DumpVcd + -> String -> Clock dom -> Reset dom -- input signals @@ -228,7 +233,7 @@ vexRiscv# , Signal dom Bit -- ^ debug_resetOut , Signal dom Bit -- ^ jtag_TDO ) -vexRiscv# !_sourcePath clk rst0 +vexRiscv# dumpVcd !_sourcePath clk rst0 timerInterrupt0 externalInterrupt0 softwareInterrupt0 @@ -243,7 +248,7 @@ vexRiscv# !_sourcePath clk rst0 jtag_TCK0 jtag_TMS0 jtag_TDI0 = unsafePerformIO $ do - (v, initStage1, initStage2, stepRising, stepFalling, _shutDown) <- vexCPU + (v, initStage1, initStage2, stepRising, stepFalling, _shutDown) <- vexCPU dumpVcd -- Make sure all the inputs are defined let @@ -366,7 +371,8 @@ vexRiscv# !_sourcePath clk rst0 ( -- ARGs - _ + _knownDomain + , _vcdPath , srcPath , clk , rst @@ -404,7 +410,7 @@ vexRiscv# !_sourcePath clk rst0 , jtag_TDO , cpu - ) = vecToTuple $ indicesI @35 + ) = vecToTuple $ indicesI @36 in InlineYamlPrimitive [Verilog] [__i| BlackBox: @@ -505,23 +511,30 @@ vexRiscv# !_sourcePath clk rst0 -- | Return a function that performs an execution step and a function to free -- the internal CPU state -vexCPU :: IO - ( Ptr VexRiscv - , Ptr VexRiscv -> NON_COMB_INPUT -> IO OUTPUT -- initStage1 - , Ptr VexRiscv -> COMB_INPUT -> IO () -- initStage2 - , Ptr VexRiscv -> Word64 -> NON_COMB_INPUT -> IO OUTPUT -- rising - , Ptr VexRiscv -> Word64 -> COMB_INPUT -> IO () -- falling - , Ptr VexRiscv -> IO () - ) -vexCPU = do +vexCPU :: + DumpVcd -> + IO + ( Ptr VexRiscv + , Ptr VexRiscv -> NON_COMB_INPUT -> IO OUTPUT -- initStage1 + , Ptr VexRiscv -> COMB_INPUT -> IO () -- initStage2 + , Ptr VexRiscv -> Word64 -> NON_COMB_INPUT -> IO OUTPUT -- rising + , Ptr VexRiscv -> Word64 -> COMB_INPUT -> IO () -- falling + , Ptr VexRiscv -> IO () + ) +vexCPU dumpVcd = do v <- vexrInit + vcd <- case dumpVcd of + NoDumpVcd -> pure nullPtr + DumpVcd path -> do + vcdPath <- newCString path + vexrInitVcd v vcdPath let {-# NOINLINE initStage1 #-} initStage1 vPtr nonCombInput = alloca $ \nonCombInputFFI -> alloca $ \outputFFI -> do poke nonCombInputFFI nonCombInput - vexrInitStage1 vPtr nonCombInputFFI outputFFI + vexrInitStage1 vcd vPtr nonCombInputFFI outputFFI peek outputFFI {-# NOINLINE initStage2 #-} @@ -534,14 +547,14 @@ vexCPU = do stepRising vPtr fsSinceLastEvent nonCombInput = alloca $ \nonCombInputFFI -> alloca $ \outputFFI -> do poke nonCombInputFFI nonCombInput - vexrStepRisingEdge vPtr fsSinceLastEvent nonCombInputFFI outputFFI + vexrStepRisingEdge vcd vPtr fsSinceLastEvent nonCombInputFFI outputFFI peek outputFFI {-# NOINLINE stepFalling #-} stepFalling vPtr fsSinceLastEvent combInput = alloca $ \combInputFFI -> do poke combInputFFI combInput - vexrStepFallingEdge vPtr fsSinceLastEvent combInputFFI + vexrStepFallingEdge vcd vPtr fsSinceLastEvent combInputFFI shutDown = vexrShutdown diff --git a/clash-vexriscv/clash-vexriscv/src/VexRiscv/ClockTicks.hs b/clash-vexriscv/src/VexRiscv/ClockTicks.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/src/VexRiscv/ClockTicks.hs rename to clash-vexriscv/src/VexRiscv/ClockTicks.hs diff --git a/clash-vexriscv/clash-vexriscv/src/VexRiscv/FFI.hsc b/clash-vexriscv/src/VexRiscv/FFI.hsc similarity index 94% rename from clash-vexriscv/clash-vexriscv/src/VexRiscv/FFI.hsc rename to clash-vexriscv/src/VexRiscv/FFI.hsc index a55bd5066..9fc929d6f 100644 --- a/clash-vexriscv/clash-vexriscv/src/VexRiscv/FFI.hsc +++ b/clash-vexriscv/src/VexRiscv/FFI.hsc @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Google LLC +-- SPDX-FileCopyrightText: 2022-2024 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 @@ -10,6 +10,7 @@ module VexRiscv.FFI where import Foreign.Storable import Foreign.Ptr +import Foreign.C (CString) import Prelude import Clash.Prelude import Data.Word @@ -18,15 +19,18 @@ import Data.Word data VexRiscv +data VerilatedVcdC + data VexRiscvJtagBridge foreign import ccall unsafe "vexr_init" vexrInit :: IO (Ptr VexRiscv) +foreign import ccall unsafe "vexr_init_vcd" vexrInitVcd :: Ptr VexRiscv -> CString -> IO (Ptr VerilatedVcdC) foreign import ccall unsafe "vexr_shutdown" vexrShutdown :: Ptr VexRiscv -> IO () -foreign import ccall unsafe "vexr_init_stage1" vexrInitStage1 :: Ptr VexRiscv -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO () +foreign import ccall unsafe "vexr_init_stage1" vexrInitStage1 :: Ptr VerilatedVcdC -> Ptr VexRiscv -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO () foreign import ccall unsafe "vexr_init_stage2" vexrInitStage2 :: Ptr VexRiscv -> Ptr COMB_INPUT -> IO () -foreign import ccall unsafe "vexr_step_rising_edge" vexrStepRisingEdge :: Ptr VexRiscv -> Word64 -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO () -foreign import ccall unsafe "vexr_step_falling_edge" vexrStepFallingEdge :: Ptr VexRiscv -> Word64 -> Ptr COMB_INPUT -> IO () +foreign import ccall unsafe "vexr_step_rising_edge" vexrStepRisingEdge :: Ptr VerilatedVcdC -> Ptr VexRiscv -> Word64 -> Ptr NON_COMB_INPUT -> Ptr OUTPUT -> IO () +foreign import ccall unsafe "vexr_step_falling_edge" vexrStepFallingEdge :: Ptr VerilatedVcdC -> Ptr VexRiscv -> Word64 -> Ptr COMB_INPUT -> IO () foreign import ccall unsafe "vexr_jtag_bridge_init" vexrJtagBridgeInit :: Word16 -> IO (Ptr VexRiscvJtagBridge) foreign import ccall unsafe "vexr_jtag_bridge_step" vexrJtagBridgeStep :: Ptr VexRiscvJtagBridge -> Ptr JTAG_OUTPUT -> Ptr JTAG_INPUT -> IO () diff --git a/clash-vexriscv/clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs b/clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs rename to clash-vexriscv/src/VexRiscv/JtagTcpBridge.hs diff --git a/clash-vexriscv/clash-vexriscv/src/VexRiscv/Random.hs b/clash-vexriscv/src/VexRiscv/Random.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/src/VexRiscv/Random.hs rename to clash-vexriscv/src/VexRiscv/Random.hs diff --git a/clash-vexriscv/clash-vexriscv/src/VexRiscv/TH.hs b/clash-vexriscv/src/VexRiscv/TH.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/src/VexRiscv/TH.hs rename to clash-vexriscv/src/VexRiscv/TH.hs diff --git a/clash-vexriscv/clash-vexriscv/src/VexRiscv/VecToTuple.hs b/clash-vexriscv/src/VexRiscv/VecToTuple.hs similarity index 99% rename from clash-vexriscv/clash-vexriscv/src/VexRiscv/VecToTuple.hs rename to clash-vexriscv/src/VexRiscv/VecToTuple.hs index e64aa4972..673d91b50 100644 --- a/clash-vexriscv/clash-vexriscv/src/VexRiscv/VecToTuple.hs +++ b/clash-vexriscv/src/VexRiscv/VecToTuple.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Google LLC +-- SPDX-FileCopyrightText: 2022-2023 Google LLC -- -- SPDX-License-Identifier: Apache-2.0 diff --git a/clash-vexriscv/clash-vexriscv/src/ffi/impl.cpp b/clash-vexriscv/src/ffi/impl.cpp similarity index 87% rename from clash-vexriscv/clash-vexriscv/src/ffi/impl.cpp rename to clash-vexriscv/src/ffi/impl.cpp index 7fe8daddb..88eda623d 100644 --- a/clash-vexriscv/clash-vexriscv/src/ffi/impl.cpp +++ b/clash-vexriscv/src/ffi/impl.cpp @@ -4,6 +4,7 @@ #include "VVexRiscv.h" #include "verilated.h" +#include #include "interface.h" #include @@ -31,12 +32,13 @@ typedef struct { extern "C" { VVexRiscv* vexr_init(); + VerilatedVcdC* vexr_init_vcd(VVexRiscv *top, const char* path); void vexr_shutdown(VVexRiscv *top); - void vexr_init_stage1(VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output); + void vexr_init_stage1(VerilatedVcdC *vcd, VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output); void vexr_init_stage2(VVexRiscv *top, const COMB_INPUT *input); - void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output); - void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input); + void vexr_step_rising_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output); + void vexr_step_falling_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input); vexr_jtag_bridge_data *vexr_jtag_bridge_init(uint16_t port); void vexr_jtag_bridge_step(vexr_jtag_bridge_data *d, const JTAG_OUTPUT *output, JTAG_INPUT *input); @@ -51,11 +53,21 @@ VVexRiscv* vexr_init() { contextp = new VerilatedContext; VVexRiscv *v = new VVexRiscv(contextp); - Verilated::traceEverOn(true); v->clk = false; return v; } +VerilatedVcdC* vexr_init_vcd(VVexRiscv *top, const char* path) +{ + VerilatedVcdC* vcd = new VerilatedVcdC; + Verilated::traceEverOn(true); + // Trace 99 levels of the hierarchy. We only have one level AFAIK, so this + // should be enough :-). + top->trace(vcd, 99); + vcd->open(path); + return vcd; +} + // Set all inputs that cannot combinationaly depend on outputs. I.e., all inputs // except the Wishbone buses. void set_non_comb_inputs(VVexRiscv *top, const NON_COMB_INPUT *input) @@ -106,7 +118,7 @@ void set_ouputs(VVexRiscv *top, OUTPUT *output) output->jtag_TDO = top->jtag_tdo; } -void vexr_init_stage1(VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output) +void vexr_init_stage1(VerilatedVcdC *vcd, VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *output) { // Set all inputs that cannot combinationaly depend on outputs. I.e., all inputs // except the Wishbone buses. @@ -114,6 +126,9 @@ void vexr_init_stage1(VVexRiscv *top, const NON_COMB_INPUT *input, OUTPUT *outpu // Combinatorially respond to the inputs top->eval(); + if (vcd != NULL) { + vcd->dump(contextp->time()); + } set_ouputs(top, output); // Advance time by 50 nanoseconds. This is an arbitrary value. Ideally, we would @@ -134,7 +149,7 @@ void vexr_shutdown(VVexRiscv *top) } -void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output) +void vexr_step_rising_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const NON_COMB_INPUT *input, OUTPUT *output) { // Advance time since last event. Note that this is 0 for the first call to // this function. To get a sensisble waveform, vexr_init has already advanced @@ -146,12 +161,15 @@ void vexr_step_rising_edge(VVexRiscv *top, uint64_t time_add, const NON_COMB_INP top->clk = true; top->eval(); + if (vcd != NULL) { + vcd->dump(contextp->time()); + } // Set all outputs set_ouputs(top, output); } -void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input) +void vexr_step_falling_edge(VerilatedVcdC *vcd, VVexRiscv *top, uint64_t time_add, const COMB_INPUT *input) { // advance time since last event contextp->timeInc(time_add); // time_add is in femtoseconds, timeinc expects picoseconds @@ -162,6 +180,9 @@ void vexr_step_falling_edge(VVexRiscv *top, uint64_t time_add, const COMB_INPUT // Evaluate the simulation top->eval(); + if (vcd != NULL) { + vcd->dump(contextp->time()); + } } vexr_jtag_bridge_data *vexr_jtag_bridge_init(uint16_t port) diff --git a/clash-vexriscv/clash-vexriscv/src/ffi/interface.h b/clash-vexriscv/src/ffi/interface.h similarity index 100% rename from clash-vexriscv/clash-vexriscv/src/ffi/interface.h rename to clash-vexriscv/src/ffi/interface.h diff --git a/clash-vexriscv/clash-vexriscv/tests/unittests/Tests/Extra.hs b/clash-vexriscv/tests/unittests/Tests/Extra.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/tests/unittests/Tests/Extra.hs rename to clash-vexriscv/tests/unittests/Tests/Extra.hs diff --git a/clash-vexriscv/clash-vexriscv/tests/unittests/Tests/VexRiscv/ClockTicks.hs b/clash-vexriscv/tests/unittests/Tests/VexRiscv/ClockTicks.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/tests/unittests/Tests/VexRiscv/ClockTicks.hs rename to clash-vexriscv/tests/unittests/Tests/VexRiscv/ClockTicks.hs diff --git a/clash-vexriscv/clash-vexriscv/tests/unittests/Tests/VexRiscv/Random.hs b/clash-vexriscv/tests/unittests/Tests/VexRiscv/Random.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/tests/unittests/Tests/VexRiscv/Random.hs rename to clash-vexriscv/tests/unittests/Tests/VexRiscv/Random.hs diff --git a/clash-vexriscv/clash-vexriscv/tests/unittests/main.hs b/clash-vexriscv/tests/unittests/main.hs similarity index 100% rename from clash-vexriscv/clash-vexriscv/tests/unittests/main.hs rename to clash-vexriscv/tests/unittests/main.hs diff --git a/default.nix b/default.nix deleted file mode 100644 index c390b48ec..000000000 --- a/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -{ nixpkgs ? import ./nix/nixpkgs.nix {} }: - -with nixpkgs.pkgs.haskellPackages; - -{ inherit bittide-extra bittide; } diff --git a/docs/code-of-conduct.md b/docs/code-of-conduct.md deleted file mode 100644 index 6a8d07056..000000000 --- a/docs/code-of-conduct.md +++ /dev/null @@ -1,117 +0,0 @@ - - -# Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our -project and our community a harassment-free experience for everyone, -regardless of age, body size, disability, ethnicity, gender identity -and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language - -* Being respectful of differing viewpoints and experiences - -* Gracefully accepting constructive criticism - -* Focusing on what is best for the community - -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual - attention or advances - -* Trolling, insulting/derogatory comments, and personal or political - attacks - -* Public or private harassment - -* Publishing others' private information, such as a physical or - electronic address, without explicit permission - -* Other conduct which could reasonably be considered inappropriate - in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of -acceptable behavior and are expected to take appropriate and fair -corrective action in response to any instances of unacceptable -behavior. - -Project maintainers have the right and responsibility to remove, edit, -or reject comments, commits, code, wiki edits, issues, and other -contributions that are not aligned to this Code of Conduct, or to ban -temporarily or permanently any contributor for other behaviors that -they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public -spaces when an individual is representing the project or its -community. Examples of representing a project or community include -using an official project e-mail address, posting via an official -social media account, or acting as an appointed representative at an -online or offline event. Representation of a project may be further -defined and clarified by project maintainers. - -This Code of Conduct also applies outside the project spaces when the -Project Steward has a reasonable belief that an individual's behavior -may have a negative impact on the project or its community. - -## Conflict Resolution - -We do not believe that all conflict is bad; healthy debate and -disagreement often yield positive results. However, it is never okay -to be disrespectful or to engage in behavior that violates the -project’s code of conduct. - -If you see someone violating the code of conduct, you are encouraged -to address the behavior directly with those involved. Many issues can -be resolved quickly and easily, and this gives people more control -over the outcome of their dispute. If you are unable to resolve the -matter for any reason, or if the behavior is threatening or harassing, -report it. We are dedicated to providing an environment where -participants feel welcome and safe. - -Reports should be directed to Calin Cascaval (cascaval@google.com), -Martin Izzard (izzard@google.com), and Tammo Spalink -(tammo@google.com), the Project Steward(s) for bittide. It is the -Project Steward’s duty to receive and address reported violations of -the code of conduct. They will then work with a committee consisting -of representatives from the Open Source Programs Office and the Google -Open Source Strategy team. If for any reason you are uncomfortable -reaching out to the Project Steward, please email -opensource@google.com. - -We will investigate every complaint, but you may not receive a direct -response. We will use our discretion in determining when and how to -follow up on reported incidents, which may range from not taking -action to permanent expulsion from the project and project-sponsored -spaces. We will notify the accused of the report and provide them an -opportunity to discuss it before any action is taken. The identity of -the reporter will be omitted from the details of the report supplied -to the accused. In potentially harmful situations, such as ongoing -harassment or threats to anyone's safety, we may take action without -notice. - -## Attribution - -This Code of Conduct is adapted from the Contributor Covenant, version -1.4, available at -https://www.contributor-covenant.org/version/1/4/code-of-conduct.html diff --git a/docs/contributing.md b/docs/contributing.md deleted file mode 100644 index 5549bc855..000000000 --- a/docs/contributing.md +++ /dev/null @@ -1,35 +0,0 @@ - - -# How to Contribute - -We'd love to accept your patches and contributions to this -project. There are just a few small guidelines you need to follow. - -## Contributor License Agreement - -Contributions to this project must be accompanied by a Contributor -License Agreement. You (or your employer) retain the copyright to your -contribution; this simply gives us permission to use and redistribute -your contributions as part of the project. Head over to - to see your current agreements on -file or to sign a new one. - -You generally only need to submit a CLA once, so if you've already -submitted one (even if it was for a different project), you probably -don't need to do it again. - -## Code reviews - -All submissions, including submissions by project members, require -review. We use GitHub pull requests for this purpose. Consult [GitHub -Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. - -## Community Guidelines - -This project follows [Google's Open Source Community -Guidelines](https://opensource.google/conduct/). diff --git a/firmware-binaries/.cargo/config.toml b/firmware-binaries/.cargo/config.toml deleted file mode 100644 index 3333601fa..000000000 --- a/firmware-binaries/.cargo/config.toml +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[build] -target = "riscv32imc-unknown-none-elf" -rustflags = ["-C", "target-feature=+f"] -target-dir = "../_build/cargo/firmware-binaries" - -[unstable] -build-std = ["core,panic_abort"] diff --git a/firmware-binaries/Cargo.lock b/firmware-binaries/Cargo.lock deleted file mode 100644 index a56054afa..000000000 --- a/firmware-binaries/Cargo.lock +++ /dev/null @@ -1,452 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "atomic-polyfill" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" -dependencies = [ - "critical-section", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "axi_stream_self_test" -version = "0.1.0" -dependencies = [ - "bittide-sys", - "riscv-rt", - "ufmt", -] - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bittide-sys" -version = "0.1.0" -dependencies = [ - "fdt", - "heapless 0.8.0", - "log", - "rand", - "smoltcp", - "ufmt", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clock-control" -version = "0.1.0" -dependencies = [ - "bittide-sys", - "riscv-rt", -] - -[[package]] -name = "clock-control-reg-cpy" -version = "0.1.0" -dependencies = [ - "bittide-sys", - "riscv-rt", - "ufmt", -] - -[[package]] -name = "critical-section" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" - -[[package]] -name = "dna_port_e2_test" -version = "0.1.0" -dependencies = [ - "bittide-sys", - "riscv-rt", - "ufmt", -] - -[[package]] -name = "embedded-hal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" -dependencies = [ - "nb 0.1.3", - "void", -] - -[[package]] -name = "fdt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" - -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.7.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" -dependencies = [ - "atomic-polyfill", - "hash32 0.2.1", - "rustc_version", - "spin", - "stable_deref_trait", - "ufmt-write", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32 0.3.1", - "stable_deref_trait", - "ufmt-write", -] - -[[package]] -name = "hello" -version = "0.1.0" -dependencies = [ - "bittide-sys", - "riscv-rt", - "ufmt", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "managed" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "nb" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" -dependencies = [ - "nb 1.1.0", -] - -[[package]] -name = "nb" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" - -[[package]] -name = "proc-macro2" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "processing-element-test" -version = "0.1.0" -dependencies = [ - "heapless 0.7.16", - "riscv-rt", - "ufmt", -] - -[[package]] -name = "quote" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r0" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "regex" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" - -[[package]] -name = "riscv" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa3145d2fae3778b1e31ec2e827b228bdc6abd9b74bb5705ba46dcb82069bc4f" -dependencies = [ - "bit_field", - "critical-section", - "embedded-hal", -] - -[[package]] -name = "riscv-rt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102c52c89defde24dedf9ac077cc69df77b85aa2400dd2d5aad6eea6a6a5c089" -dependencies = [ - "r0", - "riscv", - "riscv-rt-macros", - "riscv-target", -] - -[[package]] -name = "riscv-rt-macros" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38509d7b17c2f604ceab3e5ff8ac97bb8cd2f544688c512be75c715edaf4daf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "riscv-target" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - -[[package]] -name = "smoltcp" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" -dependencies = [ - "bitflags", - "byteorder", - "cfg-if", - "heapless 0.8.0", - "log", - "managed", -] - -[[package]] -name = "smoltcp_echo" -version = "0.1.0" -dependencies = [ - "bittide-sys", - "heapless 0.8.0", - "log", - "riscv", - "riscv-rt", - "smoltcp", - "ufmt", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "time_self_test" -version = "0.1.0" -dependencies = [ - "bittide-sys", - "riscv-rt", - "ufmt", -] - -[[package]] -name = "ufmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a64846ec02b57e9108d6469d98d1648782ad6bb150a95a9baac26900bbeab9d" -dependencies = [ - "ufmt-macros", - "ufmt-write", -] - -[[package]] -name = "ufmt-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ufmt-write" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" - -[[package]] -name = "unicode-ident" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/firmware-binaries/Cargo.lock.license b/firmware-binaries/Cargo.lock.license deleted file mode 100644 index 2b4d55897..000000000 --- a/firmware-binaries/Cargo.lock.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/firmware-binaries/Cargo.toml b/firmware-binaries/Cargo.toml deleted file mode 100644 index 8dd32695f..000000000 --- a/firmware-binaries/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - - -[profile.release] -# Optimize for size -opt-level = "z" -lto = true -panic = "abort" -incremental = false -codegen-units = 1 - -[workspace] -members = [ - "examples/hello", - "examples/smoltcp_echo", - - "test-cases/dna_port_e2_test", - "test-cases/time_self_test", - "test-cases/axi_stream_self_test", - - "clock-control-reg-cpy", - "clock-control", - "clock-control", - "processing-element-test", -] -resolver = "2" diff --git a/firmware-binaries/clock-control-reg-cpy/Cargo.toml b/firmware-binaries/clock-control-reg-cpy/Cargo.toml deleted file mode 100644 index 7c7284e7e..000000000 --- a/firmware-binaries/clock-control-reg-cpy/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "clock-control-reg-cpy" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -riscv-rt = "0.11.0" -bittide-sys = { path = "../../firmware-support/bittide-sys" } -ufmt = "0.2.0" diff --git a/firmware-binaries/clock-control-reg-cpy/build.rs b/firmware-binaries/clock-control-reg-cpy/build.rs deleted file mode 100644 index 0d6ad00a9..000000000 --- a/firmware-binaries/clock-control-reg-cpy/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/clock-control-reg-cpy/memory.x b/firmware-binaries/clock-control-reg-cpy/memory.x deleted file mode 100644 index 5c6f8a19c..000000000 --- a/firmware-binaries/clock-control-reg-cpy/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - IMEM : ORIGIN = 0x80000000, LENGTH = 64K - DMEM : ORIGIN = 0x40000000, LENGTH = 64K -} - -REGION_ALIAS("REGION_TEXT", IMEM); -REGION_ALIAS("REGION_RODATA", DMEM); -REGION_ALIAS("REGION_DATA", DMEM); -REGION_ALIAS("REGION_BSS", DMEM); -REGION_ALIAS("REGION_HEAP", DMEM); -REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/clock-control-reg-cpy/src/main.rs b/firmware-binaries/clock-control-reg-cpy/src/main.rs deleted file mode 100644 index fc680da77..000000000 --- a/firmware-binaries/clock-control-reg-cpy/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_std] -#![cfg_attr(not(test), no_main)] - -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use bittide_sys::clock_control::{ClockControl, SpeedChange}; - -#[cfg(not(test))] -use riscv_rt::entry; - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - let mut cc = unsafe { ClockControl::from_base_addr(0xC000_0000 as *const u32) }; - - let callisto_reg_addr = 0x0000_0004 as *const u32; - - loop { - let callisto_val = unsafe { callisto_reg_addr.read_volatile() }; - let change = match callisto_val { - 0 => SpeedChange::NoChange, - 1 => SpeedChange::SlowDown, - _ => SpeedChange::SpeedUp, - }; - - cc.change_speed(change); - } -} - -#[panic_handler] -fn panic_handler(_info: &core::panic::PanicInfo) -> ! { - loop { - continue; - } -} diff --git a/firmware-binaries/clock-control/Cargo.lock.license b/firmware-binaries/clock-control/Cargo.lock.license deleted file mode 100644 index 848612f0e..000000000 --- a/firmware-binaries/clock-control/Cargo.lock.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/firmware-binaries/clock-control/Cargo.toml b/firmware-binaries/clock-control/Cargo.toml deleted file mode 100644 index f6eb4f6ba..000000000 --- a/firmware-binaries/clock-control/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "clock-control" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -riscv-rt = "0.11.0" -bittide-sys = { path = "../../firmware-support/bittide-sys" } -# ufmt = "0.2.0" diff --git a/firmware-binaries/clock-control/build.rs b/firmware-binaries/clock-control/build.rs deleted file mode 100644 index 0d6ad00a9..000000000 --- a/firmware-binaries/clock-control/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/clock-control/memory.x b/firmware-binaries/clock-control/memory.x deleted file mode 100644 index 5c6f8a19c..000000000 --- a/firmware-binaries/clock-control/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - IMEM : ORIGIN = 0x80000000, LENGTH = 64K - DMEM : ORIGIN = 0x40000000, LENGTH = 64K -} - -REGION_ALIAS("REGION_TEXT", IMEM); -REGION_ALIAS("REGION_RODATA", DMEM); -REGION_ALIAS("REGION_DATA", DMEM); -REGION_ALIAS("REGION_BSS", DMEM); -REGION_ALIAS("REGION_HEAP", DMEM); -REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/clock-control/src/main.rs b/firmware-binaries/clock-control/src/main.rs deleted file mode 100644 index 2aeb07898..000000000 --- a/firmware-binaries/clock-control/src/main.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![no_std] -#![cfg_attr(not(test), no_main)] - -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use core::panic::PanicInfo; - -use bittide_sys::{ - callisto::{self, ControlConfig, ControlSt, ReframingState}, - clock_control::{ClockControl, SpeedChange}, -}; -#[cfg(not(test))] -use riscv_rt::entry; - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - #[allow(clippy::zero_ptr)] // we might want to change the address! - let mut cc = unsafe { ClockControl::from_base_addr(0xC000_0000 as *const u32) }; - - let config = ControlConfig { - target_count: 0, - wait_time: 0, - reframing_enabled: 0, - }; - let mut state = ControlSt { - z_k: 0, - b_k: SpeedChange::NoChange, - steady_state_target: 0.0f32, - rf_state: ReframingState::Detect, - }; - - loop { - let data_counts = cc.data_counts().map(|x| x as isize); - let links_stable = cc.links_stable(); - - callisto::callisto(&config, 0b0111_1111, links_stable, data_counts, &mut state); - - cc.change_speed(state.b_k); - } -} - -#[panic_handler] -fn panic_handler(_info: &PanicInfo) -> ! { - loop { - continue; - } -} diff --git a/firmware-binaries/examples/.cargo/config.toml b/firmware-binaries/examples/.cargo/config.toml deleted file mode 100644 index 80452197c..000000000 --- a/firmware-binaries/examples/.cargo/config.toml +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[build] -target = "riscv32imc-unknown-none-elf" - -[unstable] -build-std = ["core,panic_abort"] -build-std-features = ["panic_immediate_abort"] diff --git a/firmware-binaries/examples/hello/Cargo.toml b/firmware-binaries/examples/hello/Cargo.toml deleted file mode 100644 index a5a106b2d..000000000 --- a/firmware-binaries/examples/hello/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "hello" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -riscv-rt = "0.11.0" -bittide-sys = { path = "../../../firmware-support/bittide-sys" } -ufmt = "0.2.0" diff --git a/firmware-binaries/examples/hello/build.rs b/firmware-binaries/examples/hello/build.rs deleted file mode 100644 index 0d6ad00a9..000000000 --- a/firmware-binaries/examples/hello/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/examples/hello/memory.x b/firmware-binaries/examples/hello/memory.x deleted file mode 100644 index 5c6f8a19c..000000000 --- a/firmware-binaries/examples/hello/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - IMEM : ORIGIN = 0x80000000, LENGTH = 64K - DMEM : ORIGIN = 0x40000000, LENGTH = 64K -} - -REGION_ALIAS("REGION_TEXT", IMEM); -REGION_ALIAS("REGION_RODATA", DMEM); -REGION_ALIAS("REGION_DATA", DMEM); -REGION_ALIAS("REGION_BSS", DMEM); -REGION_ALIAS("REGION_HEAP", DMEM); -REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/examples/hello/src/main.rs b/firmware-binaries/examples/hello/src/main.rs deleted file mode 100644 index 6ea1b6a34..000000000 --- a/firmware-binaries/examples/hello/src/main.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![no_std] -#![cfg_attr(not(test), no_main)] - -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use ufmt::uwriteln; - -use bittide_sys::uart::Uart; - -#[cfg(not(test))] -use riscv_rt::entry; - -const STATUS_REG_ADDR: *mut u32 = 0xE000_0000 as *mut u32; -const UART_ADDR: *const () = 0xC000_0000 as *const (); - -fn test_success() { - unsafe { - STATUS_REG_ADDR.write_volatile(1); - } -} - -fn test_failure() { - unsafe { - STATUS_REG_ADDR.write_volatile(2); - } -} - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - let mut uart = unsafe { Uart::new(UART_ADDR) }; - - let names = ["Rust", "RISC-V", "Haskell"]; - for name in names { - uwriteln!(uart, "Hello from {}!", name).unwrap(); - } - uwriteln!(uart, "This can also do {} {:#x}", "debug prints", 42).unwrap(); - uwriteln!(uart, "Going in echo mode!").unwrap(); - - test_success(); - - loop { - let c = uart.receive(); - uart.send(c); - } -} - -#[panic_handler] -fn panic_handler(_info: &core::panic::PanicInfo) -> ! { - test_failure(); - loop { - continue; - } -} diff --git a/firmware-binaries/examples/smoltcp_echo/Cargo.toml b/firmware-binaries/examples/smoltcp_echo/Cargo.toml deleted file mode 100644 index b27d16a18..000000000 --- a/firmware-binaries/examples/smoltcp_echo/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "smoltcp_echo" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bittide-sys = { path = "../../../firmware-support/bittide-sys" } -riscv-rt = "0.11.0" -riscv = "^0.10" -heapless = { version = "0.8", default-features = false} -smoltcp = { version = "0.11", default-features = false, features = ["medium-ethernet", "proto-ipv4", "socket-tcp"] } -ufmt = "0.2.0" -log = {version = "0.4.21", features = ["release_max_level_trace"]} diff --git a/firmware-binaries/examples/smoltcp_echo/build.rs b/firmware-binaries/examples/smoltcp_echo/build.rs deleted file mode 100644 index cd062a257..000000000 --- a/firmware-binaries/examples/smoltcp_echo/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/examples/smoltcp_echo/memory.x b/firmware-binaries/examples/smoltcp_echo/memory.x deleted file mode 100644 index 3e6288629..000000000 --- a/firmware-binaries/examples/smoltcp_echo/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - IMEM : ORIGIN = 0x80000000, LENGTH = 256K - DMEM : ORIGIN = 0x10000000, LENGTH = 64K -} - -REGION_ALIAS("REGION_TEXT", IMEM); -REGION_ALIAS("REGION_RODATA", DMEM); -REGION_ALIAS("REGION_DATA", DMEM); -REGION_ALIAS("REGION_BSS", DMEM); -REGION_ALIAS("REGION_HEAP", DMEM); -REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/examples/smoltcp_echo/src/main.rs b/firmware-binaries/examples/smoltcp_echo/src/main.rs deleted file mode 100644 index 8a9650a00..000000000 --- a/firmware-binaries/examples/smoltcp_echo/src/main.rs +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_mut)] -#![allow(clippy::collapsible_if)] -#![no_main] - -use bittide_sys::axi::{AxiRx, AxiTx}; -use bittide_sys::dna_port_e2::{dna_to_u128, DnaValue}; -use bittide_sys::mac::MacStatus; -use bittide_sys::smoltcp::axi::AxiEthernet; -use bittide_sys::smoltcp::{set_local, set_unicast}; -use bittide_sys::time::{Clock, Duration}; -use bittide_sys::uart::Uart; -use log::{self, debug}; -#[cfg(not(test))] -use riscv_rt::entry; - -use core::cmp::min; -use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::Medium; -use smoltcp::socket::tcp::{Socket, SocketBuffer}; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use ufmt::uwriteln; - -const CLOCK_ADDR: *const () = (0b0011 << 28) as *const (); -const DNA_ADDR: *const DnaValue = (0b0111 << 28) as *const DnaValue; -const MAC_ADDR: *const MacStatus = (0b1001 << 28) as *const MacStatus; -const RX_AXI_ADDR: *const () = (0b0101 << 28) as *const (); -const TX_AXI_ADDR: *const () = (0b0110 << 28) as *const (); -const UART_ADDR: *const () = (0b0010 << 28) as *const (); - -const RX_BUFFER_SIZE: usize = 2048; -const ETH_MTU: usize = RX_BUFFER_SIZE; -const TCP_MTU: usize = ETH_MTU - 40; -const SOFT_BUFFER_SIZE: usize = 1024 * 8; - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - // Initialize and test UART. - let mut uart = unsafe { Uart::new(UART_ADDR) }; - - uwriteln!(uart, "Starting smoltcp-echo").unwrap(); - // Initialize and test clock - let mut clock = unsafe { Clock::new(CLOCK_ADDR) }; - - // Create interface - let dna = unsafe { dna_to_u128(*DNA_ADDR) }; - let mut eth_addr = EthernetAddress::from_bytes(&dna.to_le_bytes()[0..6]); - set_unicast(&mut eth_addr); - set_local(&mut eth_addr); - let mut config = Config::new(eth_addr.into()); - - let axi_tx = unsafe { AxiTx::new(TX_AXI_ADDR) }; - let axi_rx: AxiRx = unsafe { AxiRx::new(RX_AXI_ADDR) }; - let mut eth: AxiEthernet = AxiEthernet::new(Medium::Ethernet, axi_rx, axi_tx, Some(2)); - let now = clock.elapsed().into(); - let mut iface = Interface::new(config, &mut eth, now); - iface.update_ip_addrs(|ip_addrs| { - ip_addrs - .push(IpCidr::new(IpAddress::v4(10, 0, 0, 10), 8)) - .unwrap(); - }); - - // Create sockets - let server_socket = { - // It is not strictly necessary to use a `static mut` and unsafe code here, but - // on embedded systems that smoltcp targets it is far better to allocate the data - // statically to verify that it fits into RAM rather than get undefined behavior - // when stack overflows. - static mut TCP_SERVER_RX_DATA: [u8; SOFT_BUFFER_SIZE] = [0; SOFT_BUFFER_SIZE]; - static mut TCP_SERVER_TX_DATA: [u8; SOFT_BUFFER_SIZE] = [0; SOFT_BUFFER_SIZE]; - let tcp_rx_buffer = SocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); - let tcp_tx_buffer = SocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); - Socket::new(tcp_rx_buffer, tcp_tx_buffer) - }; - - let mut sockets: [_; 1] = Default::default(); - let mut sockets = SocketSet::new(&mut sockets[..]); - let server_handle = sockets.add(server_socket); - - let mut mac_status = unsafe { MAC_ADDR.read_volatile() }; - loop { - let elapsed = clock.elapsed().into(); - iface.poll(elapsed, &mut eth, &mut sockets); - - let mut socket = sockets.get_mut::(server_handle); - if !socket.is_active() && !socket.is_listening() { - mac_status = unsafe { MAC_ADDR.read_volatile() }; - uwriteln!(uart, "listening").unwrap(); - socket.listen(7).unwrap(); - } - if socket.is_open() && socket.may_send() && !socket.may_recv() { - socket.close(); - uwriteln!(uart, "DNA: {:X}", dna).unwrap(); - let new_mac_status = unsafe { MAC_ADDR.read_volatile() }; - uwriteln!(uart, "{:?}", new_mac_status - mac_status).unwrap(); - uwriteln!(uart, "Closing socket").unwrap(); - } - if socket.can_recv() { - let mut buf = [0; TCP_MTU]; - let mut slice: &[u8] = &[]; - socket - .recv(|buffer| { - let elements = min(TCP_MTU, buffer.len()); - buf[..elements].copy_from_slice(&buffer[..elements]); - slice = &buf[0..elements]; - (elements, ()) - }) - .unwrap(); - - debug!("Echoing {} bytes", slice.len()); - socket.send_slice(slice).unwrap(); - } - - match iface.poll_delay(clock.elapsed().into(), &sockets) { - Some(smoltcp::time::Duration::ZERO) => {} - Some(delay) => { - debug!("sleeping for {} ms", delay); - clock.wait(Duration::from(delay)); - } - None => {} - } - } -} - -#[export_name = "ExceptionHandler"] -fn exception_handler(_trap_frame: &riscv_rt::TrapFrame) -> ! { - let mut uart = unsafe { Uart::new(UART_ADDR) }; - riscv::interrupt::free(|| { - uwriteln!(uart, "... caught an exception. Looping forever now.\n").unwrap(); - }); - loop { - continue; - } -} - -#[panic_handler] -fn panic_handler(_info: &core::panic::PanicInfo) -> ! { - let mut uart = unsafe { Uart::new(UART_ADDR) }; - uwriteln!(uart, "Panicked, looping forever now").unwrap(); - loop { - continue; - } -} diff --git a/firmware-binaries/processing-element-test/Cargo.lock.license b/firmware-binaries/processing-element-test/Cargo.lock.license deleted file mode 100644 index 848612f0e..000000000 --- a/firmware-binaries/processing-element-test/Cargo.lock.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/firmware-binaries/processing-element-test/Cargo.toml b/firmware-binaries/processing-element-test/Cargo.toml deleted file mode 100644 index 8267de772..000000000 --- a/firmware-binaries/processing-element-test/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "processing-element-test" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -riscv-rt = "0.11.0" -heapless = { version = "0.7.16", default-features = false, features = ["ufmt-impl"] } -ufmt = "0.2.0" diff --git a/firmware-binaries/processing-element-test/build.rs b/firmware-binaries/processing-element-test/build.rs deleted file mode 100644 index 0d6ad00a9..000000000 --- a/firmware-binaries/processing-element-test/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/processing-element-test/memory.x b/firmware-binaries/processing-element-test/memory.x deleted file mode 100644 index 580bd71fb..000000000 --- a/firmware-binaries/processing-element-test/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2022 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - DATA : ORIGIN = 0x40000000, LENGTH = 64K - INSTR : ORIGIN = 0x80000000, LENGTH = 64K -} - -REGION_ALIAS("REGION_TEXT", INSTR); -REGION_ALIAS("REGION_RODATA", DATA); -REGION_ALIAS("REGION_DATA", DATA); -REGION_ALIAS("REGION_BSS", DATA); -REGION_ALIAS("REGION_HEAP", DATA); -REGION_ALIAS("REGION_STACK", DATA); diff --git a/firmware-binaries/processing-element-test/src/main.rs b/firmware-binaries/processing-element-test/src/main.rs deleted file mode 100644 index 5bd72f267..000000000 --- a/firmware-binaries/processing-element-test/src/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![no_std] -#![cfg_attr(not(test), no_main)] - -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use core::panic::PanicInfo; - -#[cfg(not(test))] -use riscv_rt::entry; - -use ufmt::uwrite; - -const STATUS_REG_ADDR: *mut u32 = 0xE000_0000 as *mut u32; - -fn test_success() { - unsafe { - STATUS_REG_ADDR.write_volatile(1); - } -} - -fn test_failure() { - unsafe { - STATUS_REG_ADDR.write_volatile(2); - } -} - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - #[allow(clippy::eq_op)] - { - assert_eq!(14 + 51, 65); - } - - for i in 0..5 { - let mut s = heapless::String::<32>::new(); - _ = uwrite!(s, "{}", i); - - let num = s.parse::().unwrap(); - assert_eq!(i, num); - } - - test_success(); - - loop { - continue; - } -} - -#[panic_handler] -fn panic_handler(_info: &PanicInfo) -> ! { - test_failure(); - loop { - continue; - } -} diff --git a/firmware-binaries/test-cases/axi_stream_self_test/Cargo.toml b/firmware-binaries/test-cases/axi_stream_self_test/Cargo.toml deleted file mode 100644 index e01415b97..000000000 --- a/firmware-binaries/test-cases/axi_stream_self_test/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "axi_stream_self_test" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -riscv-rt = "0.11.0" -bittide-sys = { path = "../../../firmware-support/bittide-sys" } -ufmt = "0.2.0" diff --git a/firmware-binaries/test-cases/axi_stream_self_test/build.rs b/firmware-binaries/test-cases/axi_stream_self_test/build.rs deleted file mode 100644 index cd062a257..000000000 --- a/firmware-binaries/test-cases/axi_stream_self_test/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/test-cases/axi_stream_self_test/memory.x b/firmware-binaries/test-cases/axi_stream_self_test/memory.x deleted file mode 100644 index 6a4288f8b..000000000 --- a/firmware-binaries/test-cases/axi_stream_self_test/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - IMEM : ORIGIN = 0x80000000, LENGTH = 64K - DMEM : ORIGIN = 0x20000000, LENGTH = 32K -} - -REGION_ALIAS("REGION_TEXT", IMEM); -REGION_ALIAS("REGION_RODATA", DMEM); -REGION_ALIAS("REGION_DATA", DMEM); -REGION_ALIAS("REGION_BSS", DMEM); -REGION_ALIAS("REGION_HEAP", DMEM); -REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/test-cases/axi_stream_self_test/rust-toolchain.toml b/firmware-binaries/test-cases/axi_stream_self_test/rust-toolchain.toml deleted file mode 100644 index f0242ef95..000000000 --- a/firmware-binaries/test-cases/axi_stream_self_test/rust-toolchain.toml +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[toolchain] -channel = "stable" -targets = [ "riscv32imc-unknown-none-elf" ] diff --git a/firmware-binaries/test-cases/axi_stream_self_test/src/main.rs b/firmware-binaries/test-cases/axi_stream_self_test/src/main.rs deleted file mode 100644 index 945899f02..000000000 --- a/firmware-binaries/test-cases/axi_stream_self_test/src/main.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![no_std] -#![cfg_attr(not(test), no_main)] - -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use ufmt::uwriteln; - -use bittide_sys::axi::self_test::self_test; -use bittide_sys::axi::{AxiRx, AxiTx}; -use bittide_sys::uart::Uart; -#[cfg(not(test))] -use riscv_rt::entry; - -const UART_ADDR: *const () = (2 << 29) as *const (); - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - // Initialize peripherals. - let uart = unsafe { Uart::new(UART_ADDR) }; - let tx = unsafe { AxiTx::new((3 << 29) as *const ()) }; - let rx: AxiRx<128> = unsafe { AxiRx::new((5 << 29) as *const ()) }; - self_test(uart, tx, rx); - loop { - continue; - } -} - -#[panic_handler] -fn panic_handler(_info: &core::panic::PanicInfo) -> ! { - let mut uart = unsafe { Uart::new(UART_ADDR) }; - uwriteln!(uart, "Woops, I panicked!").unwrap(); - loop { - continue; - } -} diff --git a/firmware-binaries/test-cases/dna_port_e2_test/Cargo.toml b/firmware-binaries/test-cases/dna_port_e2_test/Cargo.toml deleted file mode 100644 index 9861b9733..000000000 --- a/firmware-binaries/test-cases/dna_port_e2_test/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "dna_port_e2_test" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -riscv-rt = "0.11.0" -bittide-sys = { path = "../../../firmware-support/bittide-sys" } -ufmt = "0.2.0" diff --git a/firmware-binaries/test-cases/dna_port_e2_test/build.rs b/firmware-binaries/test-cases/dna_port_e2_test/build.rs deleted file mode 100644 index cd062a257..000000000 --- a/firmware-binaries/test-cases/dna_port_e2_test/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/test-cases/dna_port_e2_test/memory.x b/firmware-binaries/test-cases/dna_port_e2_test/memory.x deleted file mode 100644 index 5b0e902e8..000000000 --- a/firmware-binaries/test-cases/dna_port_e2_test/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - IMEM : ORIGIN = 0x80000000, LENGTH = 64K - DMEM : ORIGIN = 0x40000000, LENGTH = 32K -} - -REGION_ALIAS("REGION_TEXT", IMEM); -REGION_ALIAS("REGION_RODATA", DMEM); -REGION_ALIAS("REGION_DATA", DMEM); -REGION_ALIAS("REGION_BSS", DMEM); -REGION_ALIAS("REGION_HEAP", DMEM); -REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/test-cases/dna_port_e2_test/src/main.rs b/firmware-binaries/test-cases/dna_port_e2_test/src/main.rs deleted file mode 100644 index dcd47f22b..000000000 --- a/firmware-binaries/test-cases/dna_port_e2_test/src/main.rs +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -#![no_std] -#![cfg_attr(not(test), no_main)] - -use ufmt::uwriteln; - -use bittide_sys::dna_port_e2::{dna_to_u128, DnaValue}; -use bittide_sys::uart::Uart; -#[cfg(not(test))] -use riscv_rt::entry; - -const DNA_ADDR: *const DnaValue = 0xC000_0000 as *const DnaValue; - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - // Initialize peripherals. - let mut uart = unsafe { Uart::new(0x8000_0000 as *const ()) }; - let dna = dna_to_u128(unsafe { *DNA_ADDR }); - uwriteln!(uart, "{}", dna).unwrap(); - loop { - continue; - } -} - -#[panic_handler] -fn panic_handler(_info: &core::panic::PanicInfo) -> ! { - loop { - continue; - } -} diff --git a/firmware-binaries/test-cases/time_self_test/Cargo.toml b/firmware-binaries/test-cases/time_self_test/Cargo.toml deleted file mode 100644 index b31de80a6..000000000 --- a/firmware-binaries/test-cases/time_self_test/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "time_self_test" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -riscv-rt = "0.11.0" -bittide-sys = { path = "../../../firmware-support/bittide-sys" } -ufmt = "0.2.0" diff --git a/firmware-binaries/test-cases/time_self_test/build.rs b/firmware-binaries/test-cases/time_self_test/build.rs deleted file mode 100644 index cd062a257..000000000 --- a/firmware-binaries/test-cases/time_self_test/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::env; -use std::fs; -use std::path::Path; - -/// Put the linker script somewhere the linker can find it. -fn main() { - let out_dir = env::var("OUT_DIR").expect("No out dir"); - let dest_path = Path::new(&out_dir).join("memory.x"); - fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); - - if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { - println!("cargo:rustc-link-arg=-Tmemory.x"); - println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt - } - println!("cargo:rustc-link-search={out_dir}"); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/firmware-binaries/test-cases/time_self_test/memory.x b/firmware-binaries/test-cases/time_self_test/memory.x deleted file mode 100644 index 5b0e902e8..000000000 --- a/firmware-binaries/test-cases/time_self_test/memory.x +++ /dev/null @@ -1,18 +0,0 @@ -/* -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 -*/ - -MEMORY -{ - IMEM : ORIGIN = 0x80000000, LENGTH = 64K - DMEM : ORIGIN = 0x40000000, LENGTH = 32K -} - -REGION_ALIAS("REGION_TEXT", IMEM); -REGION_ALIAS("REGION_RODATA", DMEM); -REGION_ALIAS("REGION_DATA", DMEM); -REGION_ALIAS("REGION_BSS", DMEM); -REGION_ALIAS("REGION_HEAP", DMEM); -REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/test-cases/time_self_test/src/main.rs b/firmware-binaries/test-cases/time_self_test/src/main.rs deleted file mode 100644 index 07c0a5432..000000000 --- a/firmware-binaries/test-cases/time_self_test/src/main.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![no_std] -#![cfg_attr(not(test), no_main)] - -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use ufmt::uwriteln; - -use bittide_sys::time::self_test::self_test; -use bittide_sys::{time::Clock, uart::Uart}; -#[cfg(not(test))] -use riscv_rt::entry; - -#[cfg_attr(not(test), entry)] -fn main() -> ! { - // Initialize peripherals. - let mut uart = unsafe { Uart::new(0x8000_0000 as *const ()) }; - let clock = unsafe { Clock::new(0xc000_0000 as *const ()) }; - let test_results = self_test(clock); - uwriteln!(uart, "Start time self test").unwrap(); - for (name, result) in test_results { - match result { - Some(s) => uwriteln!(uart, "{}: Some({})", name, s).unwrap(), - None => uwriteln!(uart, "{}: None", name).unwrap(), - }; - } - uwriteln!(uart, "Done").unwrap(); - loop { - continue; - } -} - -#[panic_handler] -fn panic_handler(_info: &core::panic::PanicInfo) -> ! { - loop { - continue; - } -} diff --git a/firmware-support/.cargo/config.toml b/firmware-support/.cargo/config.toml deleted file mode 100644 index 4a8dab0b8..000000000 --- a/firmware-support/.cargo/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[build] -target-dir = "../_build/cargo/firmware-support" diff --git a/firmware-support/Cargo.lock b/firmware-support/Cargo.lock deleted file mode 100644 index 5b1e1098a..000000000 --- a/firmware-support/Cargo.lock +++ /dev/null @@ -1,707 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bittide-sys" -version = "0.1.0" -dependencies = [ - "fdt", - "heapless", - "lazy_static", - "libc", - "log", - "object", - "proptest", - "rand", - "smoltcp", - "tempfile", - "test-strategy", - "ufmt", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fdt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", - "ufmt-write", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" - -[[package]] -name = "libm" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" - -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "managed" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "crc32fast", - "flate2", - "hashbrown 0.11.2", - "indexmap", - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" -dependencies = [ - "bit-set", - "bitflags", - "byteorder", - "lazy_static", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "rustix" -version = "0.37.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "smoltcp" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" -dependencies = [ - "bitflags", - "byteorder", - "cfg-if", - "heapless", - "log", - "managed", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "structmeta" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104842d6278bf64aa9d2f182ba4bde31e8aec7a131d29b7f444bb9b344a09e2a" -dependencies = [ - "proc-macro2", - "quote", - "structmeta-derive", - "syn", -] - -[[package]] -name = "structmeta-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24420be405b590e2d746d83b01f09af673270cf80e9b003a5fa7b651c58c7d93" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "test-strategy" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62d6408d1406657be2f9d1701fbae379331d30d2f6e92050710edb0d34eeb480" -dependencies = [ - "proc-macro2", - "quote", - "structmeta", - "syn", -] - -[[package]] -name = "ufmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a64846ec02b57e9108d6469d98d1648782ad6bb150a95a9baac26900bbeab9d" -dependencies = [ - "ufmt-macros", - "ufmt-write", -] - -[[package]] -name = "ufmt-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ufmt-write" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-ident" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/firmware-support/Cargo.lock.license b/firmware-support/Cargo.lock.license deleted file mode 100644 index 74e57f201..000000000 --- a/firmware-support/Cargo.lock.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022-2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/firmware-support/Cargo.toml b/firmware-support/Cargo.toml deleted file mode 100644 index f1a0233d3..000000000 --- a/firmware-support/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[workspace] -members = [ - "bittide-sys", -] -resolver = "2" diff --git a/firmware-support/bittide-sys/Cargo.toml b/firmware-support/bittide-sys/Cargo.toml deleted file mode 100644 index 28c1d323c..000000000 --- a/firmware-support/bittide-sys/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "bittide-sys" -version = "0.1.0" -edition = "2021" -license = "Apache-2.0" -authors = ["Google LLC"] -resolver = "2" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -default = [] - -[dependencies] -fdt = "0.1.0" -heapless = { version = "0.8", features = ["ufmt"]} -log = "0.4.21" -rand = {version = "0.8.3", features = ["small_rng"], default-features = false } -smoltcp = { version = "0.11", default-features = false, features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"] } -ufmt = "0.2.0" - -[dev-dependencies] -proptest = "1.0" -object = { version = "0.28", features = ["write"] } -libc = "0.2" -test-strategy = "0.2.0" -rand = "0.8" -lazy_static = "1.0" -tempfile = "3" diff --git a/firmware-support/bittide-sys/src/axi.rs b/firmware-support/bittide-sys/src/axi.rs deleted file mode 100644 index d3ad319f9..000000000 --- a/firmware-support/bittide-sys/src/axi.rs +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -use ufmt::derive::uDebug; -pub mod self_test; - -pub struct AxiRxStatus { - pub buffer_full: bool, - pub packet_complete: bool, -} - -#[derive(uDebug, Copy, Clone)] -pub struct AxiRx { - base_addr: *const u8, -} - -impl AxiRx { - const PACKET_LENGTH_OFFSET: usize = BUF_SIZE; - const STATUS_OFFSET: usize = BUF_SIZE + 4; - - /// Creates a new instance of `AxiRx`. - /// - /// # Safety - /// - - /// - `base_addr` must post to a memory mapped AXI Rx peripheral. - /// - `BUF_SIZE` must be a valid buffer size. - /// - /// - pub unsafe fn new(addr: *const ()) -> Self { - AxiRx { - base_addr: addr as *const u8, - } - } - - // Returns true if there is a packet in the buffer or the buffer is full. - pub fn has_data(&self) -> bool { - self.read_status_raw() != 0 - } - - // Reads the raw bits of the status register. - pub fn read_status_raw(&self) -> u8 { - // If the instantiation of the AxiRx struct is correct, - // the status register should be located at the base address + (4 * (BUF_SIZE + 1)) - unsafe { self.base_addr.add(Self::STATUS_OFFSET).read_volatile() } - } - - // Returns a struct with the status of the buffer. - pub fn read_status(&self) -> AxiRxStatus { - let bits = self.read_status_raw(); - AxiRxStatus { - buffer_full: bits & 0b1 == 0b1, - packet_complete: bits & 0b10 == 0b10, - } - } - - // Clears the bits in the status register. - pub fn clear_status(&self) { - unsafe { - self.base_addr - .add(Self::STATUS_OFFSET) - .cast_mut() - .write_volatile(0); - } - } - - pub fn packet_length(&self) -> usize { - unsafe { - self.base_addr - .add(Self::PACKET_LENGTH_OFFSET) - .cast::() - .read_volatile() - } - } - - pub fn clear_packet_register(&self) { - unsafe { - self.base_addr - .add(Self::PACKET_LENGTH_OFFSET) - .cast_mut() - .write_volatile(0); - } - } - pub fn receive_with_timeout(&self, buffer: &mut [u8], attempts: usize) -> Option { - for _ in 0..attempts { - if let Some(s) = self.try_receive(buffer) { - return Some(s); - } - } - None - } - pub fn receive(&self, buffer: &mut [u8]) -> usize { - loop { - if let Some(s) = self.try_receive(buffer) { - return s; - } - } - } - - pub fn try_receive(&self, buffer: &mut [u8]) -> Option { - // Check if there is data to receive, this means either the buffer is full or a packet is complete - // We can check by using the utility function read_status - if self.read_status_raw() == 0 { - return None; - } - - // Get length of the incoming data - let len = self.packet_length(); - - debug_assert!(len <= buffer.len(), "Buffer too small to receive packet"); - - unsafe { - core::ptr::copy_nonoverlapping(self.base_addr, buffer.as_mut_ptr(), len); - } - Some(len) - } - - pub fn clear_packet(&self) { - self.clear_packet_register(); - self.clear_status(); - } - - pub fn get_slice(&self) -> &[u8] { - let l = self.packet_length(); - unsafe { - let ptr = self.base_addr as *const u8; - let slice = core::slice::from_raw_parts_mut(ptr as *mut u8, l); - slice - } - } -} - -#[derive(uDebug, Copy, Clone)] -pub struct AxiTx { - base_addr: *mut u8, -} - -impl AxiTx { - /// Creates a new instance of `AxiTx`. - /// # Safety - /// - `base_addr` Must be the base address of the Axi Tx peripheral. - pub unsafe fn new(base_addr: *const ()) -> Self { - AxiTx { - base_addr: base_addr as *mut u8, - } - } - - pub fn send(&mut self, packet: &[u8]) { - // Deal with unaligned packets by splitting them into 3 parts - // The use of align_to is safe because the binary representation of 4 bytes is the same as 1 word - let (bytes_slice_prefix, words_slice, bytes_slice_suffix) = unsafe { packet.align_to() }; - - for byte in bytes_slice_prefix { - unsafe { - self.base_addr.write_volatile(*byte); - } - } - // Coerce the payload address to a u32 pointer - let dst = self.base_addr as *mut u32; - for word in words_slice { - unsafe { - dst.write_volatile(*word); - } - } - for byte in bytes_slice_suffix { - unsafe { - self.base_addr.write_volatile(*byte); - } - } - - // Initiate transmission by writing the packet size in words to the send_bytes register - unsafe { - self.base_addr.add(4).write_volatile(0); - } - } -} diff --git a/firmware-support/bittide-sys/src/axi/self_test.rs b/firmware-support/bittide-sys/src/axi/self_test.rs deleted file mode 100644 index 74c4ed1b6..000000000 --- a/firmware-support/bittide-sys/src/axi/self_test.rs +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -use crate::axi::{AxiRx, AxiTx}; -use heapless::String; -use rand::rngs::SmallRng; -use rand::{RngCore, SeedableRng}; -use ufmt::uwrite; - -use crate::uart::Uart; -use ufmt::uwriteln; -const STRING_SIZE: usize = 1024; - -/// Tests for the axi module. -#[allow(dead_code)] -pub fn self_test(mut uart: Uart, tx: AxiTx, rx: AxiRx) { - type TestFn = - fn(AxiTx, AxiRx, &mut String) -> bool; - - // Construct a list of tests with their names. - let tests: &[(TestFn, &str)] = &[ - (read_rx_status, "read_rx_status"), - (clear_rx_status, "clear_rx_status"), - (clear_rx_packet_register, "clear_rx_packet_register"), - (send_empty_packet, "send_empty_packet"), - (send_static_packet, "send_static_packet"), - (send_receive_empty_packet, "send_receive_empty_packet"), - (read_rx_packet_length, "read_rx_packet_length"), - (send_receive_random_packet, "send_receive_random_packet"), - ( - send_multiple_packets_receive_all, - "send_multiple_packets_receive_all", - ), - ]; - let mut str: String = String::new(); - - uwriteln!(uart, "Start axi self test").unwrap(); - for (test, name) in tests { - str.clear(); - let result = test(tx, rx, &mut str); - if result { - uwriteln!(uart, "{}: Some({})", name, str.as_str()).unwrap(); - } else { - uwriteln!(uart, "{}: None", name).unwrap(); - } - } - uwriteln!(uart, "Done").unwrap(); - loop { - continue; - } -} - -fn send_empty_packet( - mut tx: AxiTx, - rx: AxiRx<{ BUF_SIZE }>, - _str: &mut String, -) -> bool { - let packet = []; - tx.send(&packet); - while !rx.read_status().packet_complete {} - rx.clear_packet(); - false -} - -fn send_static_packet( - mut tx: AxiTx, - rx: AxiRx<{ BUF_SIZE }>, - _str: &mut String, -) -> bool { - let packet = [0x01, 0x02, 0x03, 0x04]; - tx.send(&packet); - while !rx.read_status().packet_complete {} - rx.clear_packet(); - false -} - -fn read_rx_status( - _tx: AxiTx, - rx: AxiRx<{ BUF_SIZE }>, - str: &mut String, -) -> bool { - let status = rx.read_status(); - if status.packet_complete || status.buffer_full { - uwrite!( - str, - "Packet complete and buffer full are {} and {}", - status.packet_complete, - status.buffer_full - ) - .unwrap(); - return true; - } - false -} - -fn clear_rx_status( - _tx: AxiTx, - rx: AxiRx<{ BUF_SIZE }>, - _str: &mut String, -) -> bool { - rx.clear_status(); - false -} -fn clear_rx_packet_register( - _tx: AxiTx, - rx: AxiRx<{ BUF_SIZE }>, - _str: &mut String, -) -> bool { - rx.clear_packet_register(); - false -} -fn read_rx_packet_length( - _tx: AxiTx, - rx: AxiRx<{ BUF_SIZE }>, - str: &mut String, -) -> bool { - if rx_clear_and_verify(rx, str) { - return true; - } - let len = rx.packet_length(); - if len > 0 { - uwrite!(str, "Packet length is not 0, but {}", len).unwrap(); - return true; - } - false -} - -fn send_receive_empty_packet( - mut tx: AxiTx, - rx: AxiRx, - str: &mut String, -) -> bool { - if rx_clear_and_verify(rx, str) { - return true; - } - let packet: [u8; 0] = [0; 0]; - tx.send(&packet); - let mut received = [0; 32]; - - match rx.receive_with_timeout(&mut received, 10) { - Some(i) => { - if i != 0 { - uwrite!(str, "Received packet length is not 0, but {}", i as u32).unwrap(); - return true; - } - } - None => { - uwrite!(str, "Packet not received").unwrap(); - return true; - } - } - false -} - -const N_PACKETS: usize = 5; -//// Generate a random packet with a random length, send it over AxiTx and verify that it is received on AxiRx<{ BUF_SIZE }>. -fn send_receive_random_packet( - mut tx: AxiTx, - rx: AxiRx, - str: &mut String, -) -> bool { - let mut rng = SmallRng::seed_from_u64(0x0DDB1A5E5BAD5EED); - const MAX_LEN: usize = 128; - let mut tx_buffer = [0; MAX_LEN]; - let mut rx_buffer = [0; MAX_LEN]; - if BUF_SIZE != MAX_LEN { - uwrite!(str, "Buffer size is {}, bust must be {}", BUF_SIZE, MAX_LEN).unwrap(); - return true; - } - for _ in 0..N_PACKETS { - // Make sure there is no packet in the rx buffer. - if rx_clear_and_verify(rx, str) { - return true; - } - - // Generate a random packet. - let len = (rng.next_u32() % MAX_LEN as u32) as usize; - for dst in tx_buffer.iter_mut() { - *dst = rng.next_u32() as u8; - } - let tx_packet = &tx_buffer[0..len]; - - // Send the packet. - tx.send(tx_packet); - - // Receive the packet. - match rx.receive_with_timeout(&mut rx_buffer, 10) { - Some(i) => { - let rx_packet = &rx_buffer[0..i]; - let status = rx.read_status(); - if !status.packet_complete { - uwriteln!(str, "Packet complete: {}", status.packet_complete).unwrap(); - uwriteln!(str, "Buffer full: {}", status.buffer_full).unwrap(); - return true; - } - if i != len { - uwriteln!(str, "Packet length: sent: {}, received:{}", len, i).unwrap(); - return true; - } - if rx_packet != tx_packet { - uwriteln!(str, "Received packet does not match").unwrap(); - uwriteln!(str, "Sent: ").unwrap(); - for d in tx_packet { - uwrite!(str, "{:02X} ", *d).unwrap(); - } - uwriteln!(str, "Received: ").unwrap(); - for d in rx_packet { - uwrite!(str, "{:02X} ", *d).unwrap(); - } - return true; - } - } - None => { - uwrite!(str, "Packet not received").unwrap(); - return true; - } - } - } - false -} - -fn send_multiple_packets_receive_all( - mut tx: AxiTx, - rx: AxiRx, - str: &mut String, -) -> bool { - let mut rng = SmallRng::seed_from_u64(0x0DDB1A5E5BAD5EED); - const MAX_LEN: usize = 128; - if BUF_SIZE != MAX_LEN { - uwrite!(str, "Buffer size is {}, but must be {}", BUF_SIZE, MAX_LEN).unwrap(); - return true; - } - - // Make sure there is no packet in the rx buffer. - if rx_clear_and_verify(rx, str) { - return true; - } - - let mut tx_buffers = [[0; MAX_LEN]; N_PACKETS]; - let mut tx_packets: [&[u8]; N_PACKETS] = [&[]; N_PACKETS]; - for (i, tx_buffer) in tx_buffers.iter_mut().enumerate() { - // Generate a random packet. - let len = generate_random_packet(&mut rng, tx_buffer); - tx_packets[i] = &tx_buffer[0..len]; - // Send the packet. - tx.send(tx_packets[i]); - } - let mut rx_buffer = [0; MAX_LEN]; - for tx_packet in tx_packets { - // Receive the packet. - match rx.receive_with_timeout(&mut rx_buffer, 10) { - Some(i) => { - let rx_packet = &rx_buffer[0..i]; - let status = rx.read_status(); - if !status.packet_complete { - uwriteln!(str, "Packet complete: {}", status.packet_complete).unwrap(); - uwriteln!(str, "Buffer full: {}", status.buffer_full).unwrap(); - return true; - } - if rx_packet != tx_packet { - uwriteln!(str, "Received packet does not match").unwrap(); - uwriteln!(str, "Sent: ").unwrap(); - for d in tx_packet { - uwrite!(str, "{:02X} ", *d).unwrap(); - } - uwriteln!(str, "Received: ").unwrap(); - for d in rx_packet { - uwrite!(str, "{:02X} ", *d).unwrap(); - } - return true; - } - rx.clear_packet(); - rx.clear_status(); - } - None => { - uwrite!(str, "Packet not received").unwrap(); - return true; - } - } - } - false -} -fn rx_clear_and_verify( - rx: AxiRx, - str: &mut String, -) -> bool { - rx.clear_packet(); - rx.clear_status(); - let status = rx.read_status(); - if status.packet_complete { - uwrite!(str, "Packet complete is set").unwrap(); - return true; - } - if status.buffer_full { - uwrite!(str, "Buffer full is set").unwrap(); - return true; - } - let len = rx.packet_length(); - if len != 0 { - uwrite!(str, "Packet length is not 0, but {}", len).unwrap(); - return true; - } - false -} - -fn generate_random_packet(rng: &mut SmallRng, buffer: &mut [u8]) -> usize { - let len = (rng.next_u32() % buffer.len() as u32) as usize; - for dst in buffer.iter_mut() { - *dst = rng.next_u32() as u8; - } - len -} diff --git a/firmware-support/bittide-sys/src/callisto.rs b/firmware-support/bittide-sys/src/callisto.rs deleted file mode 100644 index dc4975a25..000000000 --- a/firmware-support/bittide-sys/src/callisto.rs +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use ufmt::derive::uDebug; - -use crate::clock_control::SpeedChange; - -/// Rust sibling of -/// `Bittide.ClockControl.StabilityChecker.StabilityIndication`. -#[repr(transparent)] -#[derive(PartialEq, PartialOrd)] -pub struct StabilityIndication(usize); - -impl StabilityIndication { - /// Indicates stability of the signal over time. - pub fn stable(&self) -> bool { - (self.0 & 0b01) == 0b01 - } - /// Indicates whether the signal is stable and close to - /// `targetDataCount`. - pub fn settled(&self) -> bool { - (self.0 & 0b10) == 0b10 - } -} - -/// Rust version of -/// `Bittide.ClockControl.Callisto.Types.ControlConfig`. -#[derive(uDebug, Clone)] -pub struct ControlConfig { - /// Enable reframing. Reframing allows a system to resettle buffers around - /// their midpoints, without dropping any frames. For more information, see - /// [arXiv:2303.11467](https://arxiv.org/abs/2303.11467). - pub reframing_enabled: usize, - /// Number of cycles to wait until reframing takes place after - /// stability has been detected. - pub wait_time: usize, - /// Target data count. See `Bittide.ClockControl.targetDataCount`. - pub target_count: isize, -} - -/// Rust version of -/// `Bittide.ClockControl.Callisto.Types.ReframingState`. -#[derive(Clone)] -pub enum ReframingState { - /// The controller remains in this state until stability has been - /// detected. - Detect, - /// Reframing has taken place. There is nothing more to do. - Done, - /// The controller remains in this state for the predefined - /// number of cycles with the assumption that the elastic buffers - /// of all other nodes are sufficiently stable after that time. - Wait { - /// Stored correction value to be applied at reframing time. - target_correction: f32, - /// Number of cycles to wait until reframing takes place. - cur_wait_time: u32, - }, -} - -/// Rust version of `Bittide.ClockControl.Callisto.Types.ControlSt`. -#[derive(Clone)] -pub struct ControlSt { - /// Accumulated speed change requests, where - /// * `speedup ~ 1` - /// * `slowdown ~ -1` - pub z_k: i32, - /// Previously submitted speed change request. Used to determine - /// the estimated clock frequency. - pub b_k: SpeedChange, - /// Steady-state value (determined when stability is detected for - /// the first time). - pub steady_state_target: f32, - /// finite state machine for reframing detection - pub rf_state: ReframingState, -} - -impl ControlSt { - fn rf_state_update(&mut self, wait_time: usize, enabled: bool, stable: bool, target: f32) { - if enabled { - match self.rf_state { - ReframingState::Detect if stable => { - self.rf_state = ReframingState::Wait { - target_correction: target, - cur_wait_time: wait_time as u32, - } - } - ReframingState::Wait { - ref mut cur_wait_time, - .. - } if *cur_wait_time > 0 => *cur_wait_time -= 1, - ReframingState::Wait { - target_correction, .. - } => { - self.rf_state = ReframingState::Done; - self.steady_state_target = target_correction; - } - _ => (), - } - } - } -} - -/// Clock correction strategy based on: -/// [https://github.com/bittide/Callisto.jl](https://github.com/bittide/Callisto.jl) -pub fn callisto( - config: &ControlConfig, - availability_mask: u32, - links_stable: u32, - data_counts: impl Iterator, - state: &mut ControlSt, -) { - // see clock control algorithm simulation here: - // - // https://github.com/bittide/Callisto.jl/blob/e47139fca128995e2e64b2be935ad588f6d4f9fb/demo/pulsecontrol.jl#L24 - // - // `k_p` (proportional gain) is copied from the Julia implementation. `fStep` should - // match the step size of the clock boards. For all our HITL tests this is set by - // `HwCcTopologies.commonStepSizeSelect`. - const K_P: f32 = 2e-8; - const FSTEP: f32 = 100e-9; - - let n_buffers = availability_mask.count_ones(); - let measured_sum = data_counts.sum::() as i32; - let r_k = (measured_sum - n_buffers as i32 * config.target_count as i32) as f32; - let c_des = K_P * r_k + state.steady_state_target; - - state.z_k += state.b_k.sign(); - - let c_est = FSTEP * state.z_k as f32; - - state.b_k = if c_des < c_est { - SpeedChange::SlowDown - } else if c_des > c_est { - SpeedChange::SpeedUp - } else { - SpeedChange::NoChange - }; - - state.rf_state_update( - config.wait_time, - config.reframing_enabled != 0, - links_stable.count_ones() == n_buffers, - c_des, - ); -} diff --git a/firmware-support/bittide-sys/src/clock_control.rs b/firmware-support/bittide-sys/src/clock_control.rs deleted file mode 100644 index c4cd2559f..000000000 --- a/firmware-support/bittide-sys/src/clock_control.rs +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -#[derive(ufmt::derive::uDebug, Copy, Clone)] -pub enum SpeedChange { - /// Increases clock speed. - SpeedUp, - /// Decreases clock speed. - SlowDown, - /// Keeps the clock as it is. - NoChange, -} - -impl SpeedChange { - pub fn sign(&self) -> i32 { - match &self { - SpeedChange::SpeedUp => 1, - SpeedChange::NoChange => 0, - SpeedChange::SlowDown => -1, - } - } -} - -pub struct ClockControl { - num_links: *const u8, - link_mask: *const u32, - finc_fdec: *mut u32, - links_stable: *const u32, - links_settled: *const u32, - data_counts_start: *const i32, -} - -impl ClockControl { - /// Load the clock-control registers from a flattened-devicetree. - /// - /// # Safety - /// - /// The `base_addr` must be a valid memory address to clock-control registers. - pub unsafe fn from_base_addr(base_addr: *const u32) -> Self { - Self { - num_links: base_addr.cast::(), - link_mask: base_addr.add(1), - finc_fdec: base_addr.add(2).cast_mut(), - links_stable: base_addr.add(3), - links_settled: base_addr.add(4), - data_counts_start: base_addr.add(5).cast(), - } - } - - pub fn num_links(&self) -> u8 { - // SAFETY: This is safe since this function can only be called - // after construction, which is only valid with valid addresses. - unsafe { self.num_links.read_volatile() } - } - - pub fn link_mask(&self) -> u32 { - // SAFETY: This is safe since this function can only be called - // after construction, which is only valid with valid addresses. - unsafe { self.link_mask.read_volatile() } - } - - pub fn change_speed(&mut self, speed_change: SpeedChange) { - let n = match speed_change { - SpeedChange::NoChange => 0, - SpeedChange::SlowDown => 1, - SpeedChange::SpeedUp => 2, - }; - - // SAFETY: This is safe since this function can only be called - // after construction, which is only valid with valid addresses. - unsafe { - self.finc_fdec.write_volatile(n); - } - } - - pub fn link_stable(&self, link: u8) -> bool { - let mask = 1u32 << link; - (self.links_stable() & mask) != 0 - } - - pub fn links_stable(&self) -> u32 { - // SAFETY: This is safe since this function can only be called - // after construction, which is only valid with valid addresses. - unsafe { self.links_stable.read_volatile() } - } - - pub fn link_settled(&self, link: u8) -> bool { - let mask = 1u32 << link; - (self.links_settled() & mask) != 0 - } - - pub fn links_settled(&self) -> u32 { - // SAFETY: This is safe since this function can only be called - // after construction, which is only valid with valid addresses. - unsafe { self.links_settled.read_volatile() } - } - - pub fn data_counts(&self) -> impl Iterator + '_ { - let n = self.num_links(); - let mut i = 0; - core::iter::from_fn(move || { - if i == n { - None - } else { - let count = unsafe { self.data_counts_start.add(i as usize).read_volatile() }; - i += 1; - Some(count) - } - }) - } -} diff --git a/firmware-support/bittide-sys/src/dna_port_e2.rs b/firmware-support/bittide-sys/src/dna_port_e2.rs deleted file mode 100644 index cca6d07fd..000000000 --- a/firmware-support/bittide-sys/src/dna_port_e2.rs +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -/// The `DnaValue` type is a unsigned 96-bit integer. We represent it as a 12-byte array -/// because Rust does not have a built-in 96-bit integer type. -pub type DnaValue = [u8; 12]; - -/// Convert a `DnaValue` to a `u128` integer. -pub fn dna_to_u128(dna: DnaValue) -> u128 { - let mut u128_array = [0u8; 16]; - u128_array[..12].copy_from_slice(&dna); - u128::from_le_bytes(u128_array) -} diff --git a/firmware-support/bittide-sys/src/gather_unit.rs b/firmware-support/bittide-sys/src/gather_unit.rs deleted file mode 100644 index 5a15bcc3e..000000000 --- a/firmware-support/bittide-sys/src/gather_unit.rs +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use fdt::node::FdtNode; - -use crate::{utils::matches_fdt_name, ComponentLoadError}; - -pub struct GatherUnit { - memory: *mut u8, - metacycle_register: *const u8, -} - -impl GatherUnit { - /// Load the Gather-Unit information from a flattened-devicetree. - /// - /// # Safety - /// - /// The FDT must be a valid description of the hardware components that this - /// code is running on. - pub unsafe fn from_fdt_node(node: &FdtNode) -> Result { - let get_node = |path| { - node.children() - .find(|child| matches_fdt_name(child, path)) - .ok_or(ComponentLoadError::FdtNodeNotFound(path)) - }; - - let get_reg = |node: &fdt::node::FdtNode, component| { - node.reg() - .ok_or(ComponentLoadError::RegNotFound { component })? - .next() - .ok_or(ComponentLoadError::RegNotFound { component }) - }; - - let memory_node = get_node("gather-memory")?; - let metacycle_register_node = get_node("metacycle-reg")?; - - let memory = get_reg(&memory_node, "memory")?; - - if let Some(size) = memory.size { - if size != FRAME_SIZE { - return Err(ComponentLoadError::SizeMismatch { - property: "memory frame size", - expected: FRAME_SIZE, - found: size, - }); - } - } - - let metacycle_register = { - let reg = get_reg(&metacycle_register_node, "metacycle_register")?; - if let Some(size) = reg.size { - if size != 1 { - return Err(ComponentLoadError::SizeMismatch { - property: "metacycle register size", - expected: 1, - found: size, - }); - } - } - reg.starting_address as *const u8 - }; - - Ok(GatherUnit { - memory: memory.starting_address as *mut u8, - metacycle_register, - }) - } - - pub const fn frame_size(&self) -> usize { - FRAME_SIZE - } - - pub fn write_frame_memory(&mut self, data: &[u8; FRAME_SIZE]) { - for (i, d) in data.iter().copied().enumerate() { - unsafe { - self.memory.add(i).write_volatile(d); - } - } - } - - /// Wait for the start of a new metacycle. - /// - /// Execution will stall until the start of a new metacycle. - pub fn wait_for_new_metacycle(&self) { - unsafe { - // reading from the register will cause a stall until the end of the - // metacycle, the read value is not actually relevant, so it's safe - // to discard. - let _val = self.metacycle_register.read_volatile(); - } - } -} diff --git a/firmware-support/bittide-sys/src/lib.rs b/firmware-support/bittide-sys/src/lib.rs deleted file mode 100644 index df4112a5c..000000000 --- a/firmware-support/bittide-sys/src/lib.rs +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -#![no_std] - -use fdt::Fdt; -use utils::matches_fdt_name; - -pub mod axi; -pub mod callisto; -pub mod clock_control; -pub mod dna_port_e2; -pub mod gather_unit; -pub mod mac; -pub mod program_stream; -pub mod scatter_unit; -pub mod smoltcp; -pub mod time; -pub mod uart; - -pub(crate) mod utils; - -pub struct Initialiser<'a> { - fdt: Fdt<'a>, -} - -/// Address to load the FDT from. -pub const FDT_ADDR: *const u8 = 0x1000_0000 as *const u8; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ComponentLoadError { - FdtNodeNotFound(&'static str), - RegNotFound { - component: &'static str, - }, - SizeMismatch { - property: &'static str, - expected: usize, - found: usize, - }, -} - -impl<'a> Initialiser<'a> { - /// Create a new [`Initialiser`] from a memory-mapped FDT. - /// - /// # Safety - /// - /// This function expected a flattened-device-tree to be located at - /// [`FDT_ADDR`]. - pub unsafe fn new() -> Result, fdt::FdtError> { - let fdt = fdt::Fdt::from_ptr(FDT_ADDR)?; - Ok(Initialiser { fdt }) - } - - /// Create a new [`Initialiser`] from the contents of a flattened-device-tree. - pub fn from_bytes(bytes: &'a [u8]) -> Result, fdt::FdtError> { - let fdt = fdt::Fdt::new(bytes)?; - Ok(Initialiser { fdt }) - } - - /// Initialise a Scatter-Unit component. - /// - /// # Safety - /// - /// The `name` must correspond to a node in the device tree, - /// the contents of this node must all be valid for the configuration of the - /// hardware this function gets executed on. - pub unsafe fn initialise_scatter_unit( - &self, - name: &'static str, - ) -> Result, ComponentLoadError> { - let node = self - .fdt - .find_node("/") - .unwrap() - .children() - .find(|child| matches_fdt_name(child, name)) - .ok_or(ComponentLoadError::FdtNodeNotFound(name))?; - - scatter_unit::ScatterUnit::from_fdt_node(&node) - } - - /// Initialise a Gather-Unit component. - /// - /// # Safety - /// - /// The `name` must correspond to a node in the device tree, - /// the contents of this node must all be valid for the configuration of the - /// hardware this function gets executed on. - pub unsafe fn initialise_gather_unit( - &self, - name: &'static str, - ) -> Result, ComponentLoadError> { - let node = self - .fdt - .find_node("/") - .unwrap() - .children() - .find(|child| matches_fdt_name(child, name)) - .ok_or(ComponentLoadError::FdtNodeNotFound(name))?; - - gather_unit::GatherUnit::from_fdt_node(&node) - } -} diff --git a/firmware-support/bittide-sys/src/mac.rs b/firmware-support/bittide-sys/src/mac.rs deleted file mode 100644 index 36c65441e..000000000 --- a/firmware-support/bittide-sys/src/mac.rs +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use core::ops::Sub; -use ufmt::derive::uDebug; - -/// Contains counters that are used the occurance of various events in the MAC. -/// It's very important to understand that we use `repr(C)` to ensure that the -/// struct is laid out in memory in the same way as the hardware registers. -#[repr(C)] -#[derive(uDebug, PartialEq, Eq, Copy, Clone)] -pub struct MacStatus { - pub tx_fifo_underflow_counter: u32, - pub tx_fifo_overflow_counter: u32, - pub tx_fifo_bad_frame_counter: u32, - pub tx_fifo_good_frame_counter: u32, - pub rx_bad_frame_counter: u32, - pub rx_bad_fcs_counter: u32, - pub rx_fifo_overflow_counter: u32, - pub rx_fifo_bad_frame_counter: u32, - pub rx_fifo_good_frame_counter: u32, -} - -impl Sub for MacStatus { - type Output = MacStatus; - fn sub(self, other: MacStatus) -> MacStatus { - MacStatus { - tx_fifo_underflow_counter: self.tx_fifo_underflow_counter - - other.tx_fifo_underflow_counter, - tx_fifo_overflow_counter: self.tx_fifo_overflow_counter - - other.tx_fifo_overflow_counter, - tx_fifo_bad_frame_counter: self.tx_fifo_bad_frame_counter - - other.tx_fifo_bad_frame_counter, - tx_fifo_good_frame_counter: self.tx_fifo_good_frame_counter - - other.tx_fifo_good_frame_counter, - rx_bad_frame_counter: self.rx_bad_frame_counter - other.rx_bad_frame_counter, - rx_bad_fcs_counter: self.rx_bad_fcs_counter - other.rx_bad_fcs_counter, - rx_fifo_overflow_counter: self.rx_fifo_overflow_counter - - other.rx_fifo_overflow_counter, - rx_fifo_bad_frame_counter: self.rx_fifo_bad_frame_counter - - other.rx_fifo_bad_frame_counter, - rx_fifo_good_frame_counter: self.rx_fifo_good_frame_counter - - other.rx_fifo_good_frame_counter, - } - } -} diff --git a/firmware-support/bittide-sys/src/program_stream.rs b/firmware-support/bittide-sys/src/program_stream.rs deleted file mode 100644 index b4eaf56ad..000000000 --- a/firmware-support/bittide-sys/src/program_stream.rs +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -//! Types and functions to read a program using a streaming format - -/// Information about the available memory for loaded programs. -pub struct MemoryConfiguration { - /// Range of valid instruction memory (writeable and executable) - pub instruction_memory: core::ops::Range, - /// Range of valid data memory - pub data_memory: core::ops::Range, -} - -/// Header of the program being sent. -pub struct ProgramHeader { - /// Address of the entry point to the program. - /// - /// Should point into executable memory. - pub entry: u32, - /// Number of segments that this program consists of and will be sent. - pub num_segments: u32, -} - -impl ProgramHeader { - /// Validate the program header in the context of a [`MemoryConfiguration`]. - pub fn is_valid(&self, conf: &MemoryConfiguration) -> bool { - conf.instruction_memory.contains(&self.entry) - } -} - -/// Header of a segment being sent. -pub struct SegmentHeader { - /// Flag whether the segment contains executable instructions or data. - pub is_executable: bool, - /// The address at which the segment should be loaded in memory. - pub addr: u32, - /// The length of the streaming data to be received. - pub data_len: u32, - /// The amount of zero-padding to add to the end of the segment data. - pub zero_padding: u32, -} - -impl SegmentHeader { - /// Validate the segment header in the context of a [`MemoryConfiguration`]. - pub fn is_valid(&self, conf: &MemoryConfiguration) -> bool { - let end = self.addr + self.data_len + self.zero_padding; - - if self.is_executable { - conf.instruction_memory.contains(&self.addr) && conf.instruction_memory.contains(&end) - } else { - conf.data_memory.contains(&self.addr) && conf.instruction_memory.contains(&end) - } - } -} - -/// Read the program header from a stream -/// -/// Return `None` when there is no more data in the stream. -pub fn read_program_header(input: &mut impl Iterator) -> Option { - let entry = read_u32_le(input)?; - let num_segments = read_u32_le(input)?; - - Some(ProgramHeader { - entry, - num_segments, - }) -} - -/// Read the next segment header from a stream -/// -/// Return `None` when there is no more data in the stream. -pub fn read_segment_header(input: &mut impl Iterator) -> Option { - let is_executable = input.next()? == 1; - let addr = read_u32_le(input)?; - let data_len = read_u32_le(input)?; - let zero_padding = read_u32_le(input)?; - - Some(SegmentHeader { - is_executable, - addr, - data_len, - zero_padding, - }) -} - -/// Reads the segment data from a stream and directly writes it to the appropriate -/// address. -/// -/// Return `None` when there is no more data in the stream. -/// -/// # Safety -/// -/// The [`SegmentHeader`]`::addr` must be a valid memory chunk of at least -/// [`SegmentHeader`]`::data_len` bytes. -pub unsafe fn write_segment_data( - seg: &SegmentHeader, - input: &mut impl Iterator, -) -> Option<()> { - let addr = seg.addr as *mut u8; - - for offset in 0..seg.data_len { - let b = input.next()?; - - addr.offset(offset as isize).write(b); - } - - Some(()) -} - -/// Writes the zero padding of a segment in memory. -/// -/// # Safety -/// -/// The [`SegmentHeader`]`::addr` must be a valid memory chunk of at least -/// [`SegmentHeader`]`::data_len` + [`SegmentHeader`]`::zero_padding` bytes. -pub unsafe fn write_padding(seg: &SegmentHeader) { - let start_addr = (seg.addr as *mut u8).offset(seg.data_len as isize); - - core::ptr::write_bytes(start_addr, 0, seg.zero_padding as usize); -} - -fn read_u32_le(input: &mut impl Iterator) -> Option { - let bytes = [input.next()?, input.next()?, input.next()?, input.next()?]; - Some(u32::from_le_bytes(bytes)) -} diff --git a/firmware-support/bittide-sys/src/scatter_unit.rs b/firmware-support/bittide-sys/src/scatter_unit.rs deleted file mode 100644 index 483b90c26..000000000 --- a/firmware-support/bittide-sys/src/scatter_unit.rs +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use fdt::node::FdtNode; - -use crate::{utils::matches_fdt_name, ComponentLoadError}; - -pub struct ScatterUnit { - memory: *const u8, - metacycle_register: *const u8, -} - -impl ScatterUnit { - /// Load the Scatter-Unit information from a flattened-devicetree. - /// - /// # Safety - /// - /// The FDT must be a valid description of the hardware components that this - /// code is running on. - pub unsafe fn from_fdt_node(node: &FdtNode) -> Result { - let get_node = |path| { - node.children() - .find(|child| matches_fdt_name(child, path)) - .ok_or(ComponentLoadError::FdtNodeNotFound(path)) - }; - - let get_reg = |node: &fdt::node::FdtNode, component| { - node.reg() - .ok_or(ComponentLoadError::RegNotFound { component })? - .next() - .ok_or(ComponentLoadError::RegNotFound { component }) - }; - - let memory_node = get_node("scatter-memory")?; - let metacycle_register_node = get_node("metacycle-reg")?; - - let memory = get_reg(&memory_node, "memory")?; - - if let Some(size) = memory.size { - if size != FRAME_SIZE { - return Err(ComponentLoadError::SizeMismatch { - property: "memory frame size", - expected: FRAME_SIZE, - found: size, - }); - } - } - - let metacycle_register = { - let reg = get_reg(&metacycle_register_node, "metacycle_register")?; - if let Some(size) = reg.size { - if size != 1 { - return Err(ComponentLoadError::SizeMismatch { - property: "metacycle register size", - expected: 1, - found: size, - }); - } - } - reg.starting_address as *const u8 - }; - - Ok(ScatterUnit { - memory: memory.starting_address, - metacycle_register, - }) - } - - pub const fn frame_size(&self) -> usize { - FRAME_SIZE - } - - pub fn read_frame_memory(&self, data: &mut [u8; FRAME_SIZE]) { - for (i, d) in data.iter_mut().enumerate() { - unsafe { - *d = self.memory.add(i).read_volatile(); - } - } - } - - /// Wait for the start of a new metacycle. - /// - /// Execution will stall until the start of a new metacycle. - pub fn wait_for_new_metacycle(&self) { - unsafe { - // reading from the register will cause a stall until the end of the - // metacycle, the read value is not actually relevant, so it's safe - // to discard. - let _val = self.metacycle_register.read_volatile(); - } - } -} diff --git a/firmware-support/bittide-sys/src/smoltcp.rs b/firmware-support/bittide-sys/src/smoltcp.rs deleted file mode 100644 index 6eaa828e6..000000000 --- a/firmware-support/bittide-sys/src/smoltcp.rs +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -pub mod axi; - -use smoltcp::wire::EthernetAddress; - -/// Clears the multicast bit in the MAC address. -/// This is the most significant bit of the first byte. -/// ``` -/// use smoltcp::wire::EthernetAddress; -/// use bittide_sys::smoltcp::set_unicast; -/// let mut mac = EthernetAddress::from_bytes(&[0xFF; 6]); -/// assert!(!mac.is_unicast()); -/// set_unicast(&mut mac); -/// assert!(mac.is_unicast()); -/// ``` -pub fn set_unicast(addr: &mut EthernetAddress) { - addr.0[0] &= 0b11111110; -} - -/// Set the locally administered bit in the MAC address. -/// This is the second least significant bit of the first byte. -/// ``` -/// use smoltcp::wire::EthernetAddress; -/// use bittide_sys::smoltcp::set_local; -/// let mut mac = EthernetAddress::from_bytes(&[0; 6]); -/// assert!(!mac.is_local()); -/// set_local(&mut mac); -/// assert!(mac.is_local()); -/// ``` -pub fn set_local(addr: &mut EthernetAddress) { - addr.0[0] |= 0b00000010; -} diff --git a/firmware-support/bittide-sys/src/smoltcp/axi.rs b/firmware-support/bittide-sys/src/smoltcp/axi.rs deleted file mode 100644 index 01c7f572d..000000000 --- a/firmware-support/bittide-sys/src/smoltcp/axi.rs +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -use crate::axi::{AxiRx, AxiTx}; -use log::debug; -use smoltcp::phy::{self, Device, DeviceCapabilities, Medium}; -use smoltcp::time::Instant; - -/// Abstraction over `Axirx` and `AxiTx` to provide a `Device` implementation for smoltcp. -/// -pub struct AxiEthernet { - axi_rx: AxiRx, - axi_tx: AxiTx, - max_burst: Option, - medium: Medium, -} - -#[allow(clippy::new_without_default)] -impl AxiEthernet { - pub fn new( - medium: Medium, - axi_rx: AxiRx, - axi_tx: AxiTx, - max_burst: Option, - ) -> AxiEthernet { - AxiEthernet { - axi_rx, - axi_tx, - max_burst, - medium, - } - } -} - -impl Device for AxiEthernet { - type RxToken<'a> = RxToken<'a, MTU>; - type TxToken<'a> = TxToken<'a, MTU>; - fn capabilities(&self) -> DeviceCapabilities { - let mut cap = DeviceCapabilities::default(); - cap.max_transmission_unit = MTU; - cap.medium = self.medium; - cap.max_burst_size = self.max_burst; - cap - } - - fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - // If there is data available, - let status = self.axi_rx.read_status(); - if status.packet_complete { - debug!("Data available"); - - // Produce a receive toking with the data and tx token that can - // be used to respond to the received data. - let rx = RxToken { - axi_rx: &mut self.axi_rx, - }; - let tx = TxToken { - axi_tx: &mut self.axi_tx, - }; - Some((rx, tx)) - } else { - if status.buffer_full { - debug!("Clearing full buffer"); - self.axi_rx.clear_packet(); - self.axi_rx.clear_status(); - } - None - } - } - - fn transmit(&mut self, _timestamp: Instant) -> Option> { - Some(TxToken { - axi_tx: &mut self.axi_tx, - }) - } -} - -pub struct RxToken<'a, const BUFFER_SIZE: usize> { - axi_rx: &'a mut AxiRx<{ BUFFER_SIZE }>, -} - -impl phy::RxToken for RxToken<'_, BUFFER_SIZE> { - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // Get a slice containing the received data - let buf = self.axi_rx.get_slice(); - - // TODO: This is a hack to get around the fact that the buffer is not mutable, - // but the smoltcp API requires it to be. This Should be fixed by - // https://github.com/smoltcp-rs/smoltcp/pull/924 - #[allow(clippy::cast_ref_to_mut)] - let mutable_buf = - unsafe { core::slice::from_raw_parts_mut(buf.as_ptr().cast_mut(), buf.len()) }; - - // Process the received data - let r = f(mutable_buf); - - // Clear the packet and status registers - self.axi_rx.clear_packet(); - self.axi_rx.clear_status(); - debug!("Consumed {} bytes", buf.len()); - - r - } -} - -pub struct TxToken<'a, const BUFFER_SIZE: usize> { - axi_tx: &'a mut AxiTx, -} - -impl<'a, const BUFFER_SIZE: usize> TxToken<'a, BUFFER_SIZE> { - pub fn new(axi_tx: &'a mut AxiTx) -> TxToken<'a, BUFFER_SIZE> { - TxToken { axi_tx } - } -} - -impl<'a, const BUFFER_SIZE: usize> phy::TxToken for TxToken<'a, BUFFER_SIZE> { - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // The HAL of our peripheral manually sends the packet word by word and byte by byte. - // For this reason we need to ensure that all bytes - let mut buffer = [0; BUFFER_SIZE]; - let packet = &mut buffer[0..len]; - let result: R = f(packet); - self.axi_tx.send(packet); - result - } -} diff --git a/firmware-support/bittide-sys/src/time.rs b/firmware-support/bittide-sys/src/time.rs deleted file mode 100644 index d4336ca60..000000000 --- a/firmware-support/bittide-sys/src/time.rs +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -/*! Time structures. - -The `time` module contains structures used to get some sense of time. -While clocks are dynamic in a Bittide System, it is expected that the deviation from -wall clock time is no worse than any on-board static clock source and the effect is -insignificant for timing purposes. - - - [`Instant`] is used to represent absolute time. - - [`Duration`] is used to represent relative time. - - [`Clock`] Manages time and provides utility methods for waiting and updating time. -*/ - -use core::cmp; -use core::ops; -use ufmt::derive::uDebug; -use ufmt::uDisplay; -use ufmt::uWrite; -use ufmt::uwrite; -pub mod self_test; - -/// A representation of an absolute time value. -/// -/// The `Instant` type is a wrapper around a `u64` value that represents the number -/// of clock cycles since system startup. -/// -#[derive(uDebug, Copy, Clone)] -pub struct Instant { - clock_cycles: u64, - frequency: u64, -} - -impl core::cmp::PartialEq for Instant { - fn eq(&self, other: &Self) -> bool { - self.clock_cycles == other.clock_cycles && self.frequency == other.frequency - } -} - -impl core::cmp::PartialOrd for Instant { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.to_micros().cmp(&other.to_micros())) - } -} - -impl Instant { - /// Create a new `Instant` from a number of microseconds and the frequency of the timing peripheral. - pub fn from_cycles(cycles: u64, frequency: u64) -> Instant { - if frequency == 0 { - panic!("Tried to create an Instant with a frequency of 0 hertz.") - } - Instant { - clock_cycles: cycles, - frequency, - } - } - /// Create a new `Instant` from a number of microseconds and the frequency of the timing peripheral. - pub fn from_micros(micros: u64, frequency: u64) -> Instant { - if frequency == 0 { - panic!("Tried to create an Instant with a frequency of 0 hertz.") - } - Instant { - clock_cycles: (micros * frequency / 1e6 as u64), - frequency, - } - } - - /// Create a new `Instant` from a number of milliseconds and the frequency of the timing peripheral. - pub fn from_millis(millis: u64, frequency: u64) -> Instant { - if frequency == 0 { - panic!("Tried to create an Instant with a frequency of 0 hertz.") - } - Instant { - clock_cycles: (millis * frequency / 1e3 as u64), - frequency, - } - } - - /// Create a new `Instant` from a number of seconds and the frequency of the timing peripheral. - pub fn from_secs(secs: u64, frequency: u64) -> Instant { - if frequency == 0 { - panic!("Tried to create an Instant with a frequency of 0 hertz.") - } - Instant { - clock_cycles: secs * frequency, - frequency, - } - } - - /// Create a new `Instant` from a number of minutes and the frequency of the timing peripheral. - pub fn from_mins(mins: u64, frequency: u64) -> Instant { - if frequency == 0 { - panic!("Tried to create an Instant with a frequency of 0 hertz.") - } - Instant { - clock_cycles: mins * (60 * frequency), - frequency, - } - } - - /// Create a new `Instant` from a number of hours and the frequency of the timing peripheral. - pub fn from_hours(hours: u64, frequency: u64) -> Instant { - if frequency == 0 { - panic!("Tried to create an Instant with a frequency of 0 hertz.") - } - Instant { - clock_cycles: hours * (60 * 60 * frequency), - frequency, - } - } - - /// The number of whole hours represented by this `Instant`. - pub fn to_hours(&self) -> u64 { - self.clock_cycles / (60 * 60 * self.frequency) - } - - /// The number of whole minutes represented by this `Instant`. - pub fn to_mins(&self) -> u64 { - self.clock_cycles / (60 * self.frequency) - } - - /// The number of whole seconds represented by this `Instant`. - pub fn to_secs(&self) -> u64 { - self.clock_cycles / self.frequency - } - - /// The number of whole milliseconds represented by this `Instant`. - pub fn to_millis(&self) -> u64 { - (self.clock_cycles * 1e3 as u64) / self.frequency - } - - /// The number of whole microseconds represented by this `Instant`. - pub fn to_micros(&self) -> u64 { - (self.clock_cycles * 1e6 as u64) / self.frequency - } - - /// The number of cycles stored by this `Instant`. - pub fn get_cycles(&self) -> u64 { - self.clock_cycles - } - - /// The frequency corresponding to this `Instant`s clock cycles. - pub fn get_frequency(&self) -> u64 { - self.frequency - } -} - -impl ops::Add for Instant { - type Output = Instant; - - fn add(self, rhs: Duration) -> Instant { - Instant { - clock_cycles: self.clock_cycles + rhs.to_cycles(self.frequency), - frequency: self.frequency, - } - } -} - -impl ops::AddAssign for Instant { - fn add_assign(&mut self, rhs: Duration) { - self.clock_cycles += rhs.to_cycles(self.frequency) - } -} - -impl ops::Sub for Instant { - type Output = Instant; - - fn sub(self, rhs: Duration) -> Instant { - Instant { - clock_cycles: self.clock_cycles - rhs.to_cycles(self.frequency), - frequency: self.frequency, - } - } -} - -impl ops::SubAssign for Instant { - fn sub_assign(&mut self, rhs: Duration) { - self.clock_cycles -= rhs.to_cycles(self.frequency) - } -} - -impl ops::Sub for Instant { - type Output = Duration; - - fn sub(self, rhs: Instant) -> Duration { - Duration::from_micros(self.to_micros() - rhs.to_micros()) - } -} - -/// A representation of a relative time, stored in microseconds. -#[derive(uDebug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Duration { - micros: u64, -} - -impl Duration { - /// Create a new `Duration` from a number of microseconds. - pub fn from_micros(micros: u64) -> Duration { - Duration { micros } - } - - /// Create a new `Duration` from a number of milliseconds. - pub fn from_millis(millis: u64) -> Duration { - Duration { - micros: millis * (1e3 as u64), - } - } - - /// Create a new `Duration` from a number of seconds. - pub fn from_secs(secs: u64) -> Duration { - Duration { - micros: secs * (1e6 as u64), - } - } - - /// Create a new `Duration` from a number of minutes. - pub fn from_mins(mins: u64) -> Duration { - Duration { - micros: mins * (60 * 1e6 as u64), - } - } - - /// Create a new `Duration` from a number of hours. - pub fn from_hours(hours: u64) -> Duration { - Duration { - micros: hours * (60 * 60 * 1e6 as u64), - } - } - - /// The number of whole hours represented by this Duration. - pub const fn to_hours(&self) -> u64 { - self.micros / ((1e6 as u64) * 60 * 60) - } - - /// The number of whole minutes represented by this Duration. - pub const fn to_mins(&self) -> u64 { - self.micros / ((1e6 as u64) * 60) - } - - /// The number of whole seconds represented by this Duration. - pub const fn to_secs(&self) -> u64 { - self.micros / (1e6 as u64) - } - - /// The number of whole milliseconds represented by this Duration. - pub const fn to_millis(&self) -> u64 { - self.micros / (1e3 as u64) - } - - /// The number of whole microseconds represented by this Duration. - pub const fn to_micros(&self) -> u64 { - self.micros - } - - /// The number of clock cycles represented by this Duration given the frequency. - pub fn to_cycles(&self, frequency: u64) -> u64 { - (self.micros * frequency) / (1e6 as u64) - } -} - -impl ops::Add for Duration { - type Output = Duration; - - fn add(self, rhs: Duration) -> Duration { - Duration { - micros: self.micros + rhs.micros, - } - } -} - -impl ops::AddAssign for Duration { - fn add_assign(&mut self, rhs: Duration) { - self.micros += rhs.micros; - } -} - -impl ops::Sub for Duration { - type Output = Duration; - - fn sub(self, rhs: Duration) -> Duration { - Duration { - micros: self - .micros - .checked_sub(rhs.micros) - .expect("overflow when subtracting durations"), - } - } -} - -/// The Clock struct is a hardware abstraction for the timekeeping peripheral. -/// It provides methods for waiting and updating time. -/// Typical usage: -/// use bittide_sys::time::{Clock, Duration}; -/// -/// let addr = 0x1000_0000 as *const u32; -/// let clock = unsafe{ Clock::new(addr) }; -/// let now = clock.elapsed(); -/// clock.wait(Duration::from_millis(1)); -/// let later = clock.elapsed(); -/// let duration = later - now; -/// assert!(duration > Duration::from_millis(1)); - -#[derive(uDebug, Clone)] -pub struct Clock { - freeze_count: *mut u32, - counter: *const u64, - frequency: *const u64, -} - -impl Clock { - /// Create a new Clock instance. - /// - /// # Safety - /// - /// `addr` needs to point to a mapped memory address for a timer component. - pub unsafe fn new(base_addr: *const ()) -> Clock { - unsafe { - let addr = base_addr as *mut u32; - Clock { - freeze_count: addr, - counter: addr.add(1).cast::(), - frequency: addr.add(3).cast::(), - } - } - } - - /// Wait for a `Duration`. - pub fn wait(&self, duration: Duration) { - let mut now = self.elapsed(); - let target = now + duration; - while target > now { - now = self.elapsed(); - } - } - - /// Wait until we have passed an `Instant`. - pub fn wait_until(&self, target: Instant) { - let mut now = self.elapsed(); - while target > now { - now = self.elapsed(); - } - } - - /// Update the clock and return the current Instant. - pub fn elapsed(&self) -> Instant { - self.freeze(); - Instant { - clock_cycles: self.get_counter(), - frequency: self.get_frequency(), - } - } - - /// Freezes the time counter. - fn freeze(&self) { - unsafe { - self.freeze_count.write_volatile(0); - } - } - - /// Retrieves the current value of the time counter. - /// - /// Returns: - /// - The current value of the time counter as a `u64`. - fn get_counter(&self) -> u64 { - unsafe { self.counter.read_volatile() } - } - - /// Retrieves the frequency of the time counter. - /// - /// Returns: - /// - The frequency of the time counter as a `u64`. - pub fn get_frequency(&self) -> u64 { - unsafe { self.frequency.read_volatile() } - } -} - -impl From for smoltcp::time::Instant { - fn from(val: Instant) -> Self { - smoltcp::time::Instant::from_micros(val.to_micros() as i64) - } -} - -impl From for Duration { - fn from(duration: smoltcp::time::Duration) -> Self { - Duration::from_micros(duration.micros()) - } -} - -impl uDisplay for Instant { - fn fmt(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error> - where - W: uWrite + ?Sized, - { - let hours = self.to_hours(); - let mins = self.to_mins() % 60; - let secs = self.to_secs() % 60; - let millis = self.to_millis() % 1000; - - uwrite!(f, "{}:{}:{}.{}", hours, mins, secs, millis) - } -} - -impl uDisplay for Duration { - fn fmt(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error> - where - W: uWrite + ?Sized, - { - let hours = self.to_hours(); - let mins = self.to_mins() % 60; - let secs = self.to_secs() % 60; - let millis = self.to_millis() % 1000; - let micros = self.to_micros() % 1000; - - uwrite!(f, "{}:{}:{}.{}.{}", hours, mins, secs, millis, micros) - } -} diff --git a/firmware-support/bittide-sys/src/time/self_test.rs b/firmware-support/bittide-sys/src/time/self_test.rs deleted file mode 100644 index eabd192f9..000000000 --- a/firmware-support/bittide-sys/src/time/self_test.rs +++ /dev/null @@ -1,258 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -use crate::time::Clock; -use crate::time::Duration; -use crate::time::Instant; - -/// Tests for the time module. -/// It receives a pointer to the timing peripheral and returns a list of tuples containing the -/// name of the test and an Option<&'static str> indicating if the test passed or an error message. -#[allow(dead_code)] -pub fn self_test( - clock: Clock, -) -> impl Iterator)> { - // Construct a list of tests with their names. - let tests = &[ - ( - now_not_null as fn(Clock) -> Option<&'static str>, - "now_not_null", - ), - ( - freq_not_null as fn(Clock) -> Option<&'static str>, - "freq_not_null", - ), - (wait_1ms as fn(Clock) -> Option<&'static str>, "wait_1ms"), - ( - skip_next_ms as fn(Clock) -> Option<&'static str>, - "skip_next_ms", - ), - ( - duration_hour_minute as fn(Clock) -> Option<&'static str>, - "duration_hour_minute", - ), - ( - duration_minute_second as fn(Clock) -> Option<&'static str>, - "duration_minute_second", - ), - ( - duration_second_millisecond as fn(Clock) -> Option<&'static str>, - "duration_second_millisecond", - ), - ( - duration_millisecond_microsecond as fn(Clock) -> Option<&'static str>, - "duration_millisecond_microsecond", - ), - ( - instant_hour_minute as fn(Clock) -> Option<&'static str>, - "instant_hour_minute", - ), - ( - instant_minute_second as fn(Clock) -> Option<&'static str>, - "instant_minute_second", - ), - ( - instant_second_millisecond as fn(Clock) -> Option<&'static str>, - "instant_second_millisecond", - ), - ( - instant_millisecond_microsecond as fn(Clock) -> Option<&'static str>, - "instant_millisecond_microsecond", - ), - ]; - // Run the tests and collect the results. - let results = tests.iter().map(move |(f, name)| (name, f(clock.clone()))); - results -} - -//// Obtain the value of the counter, check if it's not 0. -pub fn now_not_null(clock: Clock) -> Option<&'static str> { - let frequency = clock.get_frequency(); - let now = clock.elapsed(); - if now == Instant::from_cycles(0, frequency) { - Some("now_not_null test failed: now is null") - } else { - None - } -} - -//// Read the frequency value, check if it's not 0. -pub fn freq_not_null(clock: Clock) -> Option<&'static str> { - let freq = clock.get_frequency(); - if freq == 0 { - Some("freq_not_null test failed: frequency is null") - } else { - None - } -} - -/// Read the current time in milliseconds, wait a ms and read again. -/// The new time should differ less than 100 us from the expected target. -pub fn wait_1ms(clock: Clock) -> Option<&'static str> { - let wait_time = Duration::from_millis(1); - let time0 = clock.elapsed(); - clock.wait(wait_time); - let time1 = clock.elapsed(); - let expected = time0 + wait_time; - if time1 - expected >= Duration::from_micros(100) { - Some("wait_1ms test failed: time difference is too large") - } else { - None - } -} - -/// Read the current time in milliseconds and create a target of the current time in ms + 2. -/// Wait until we reach the target and obtain the new time. The time should differ no more than -/// 100 ms from the target. -pub fn skip_next_ms(clock: Clock) -> Option<&'static str> { - let frequency = clock.get_frequency(); - let time0 = clock.elapsed(); - let target = Instant::from_millis(time0.to_millis() + 2, frequency); - clock.wait_until(target); - let time1 = clock.elapsed(); - if time1 - target >= Duration::from_micros(100) { - Some("skip_next_ms test failed: time difference is too large") - } else { - None - } -} - -/// Create a Duration of 1 hour and 60 minutes, convert between them and check if they are equal. -pub fn duration_hour_minute(_: Clock) -> Option<&'static str> { - let duration_hour = Duration::from_hours(1); - let duration_minute = Duration::from_mins(60); - let hour_to_minute = duration_hour.to_mins(); - let minute_to_hour = duration_minute.to_hours(); - if duration_hour != duration_minute { - Some("duration_hour_minute test failed: durations are not equal") - } else if hour_to_minute != 60 { - Some("duration_hour_minute test failed: hour to minute conversion failed") - } else if minute_to_hour != 1 { - Some("duration_hour_minute test failed: minute to hour conversion failed") - } else { - None - } -} - -/// Create a Duration of 1 minute and 60 seconds, convert between them and check if they are equal. -pub fn duration_minute_second(_: Clock) -> Option<&'static str> { - let duration_minute = Duration::from_mins(1); - let duration_second = Duration::from_secs(60); - let minute_to_second = duration_minute.to_secs(); - let second_to_minute = duration_second.to_mins(); - if duration_minute != duration_second { - Some("duration_minute_second test failed: durations are not equal") - } else if minute_to_second != 60 { - Some("duration_minute_second test failed: minute to second conversion failed") - } else if second_to_minute != 1 { - Some("duration_minute_second test failed: second to minute conversion failed") - } else { - None - } -} - -/// Create a Duration of 1 second and 1000 milliseconds, convert between them and check if they are equal. -pub fn duration_second_millisecond(_: Clock) -> Option<&'static str> { - let duration_second = Duration::from_secs(1); - let duration_millisecond = Duration::from_millis(1000); - let second_to_millisecond = duration_second.to_millis(); - let millisecond_to_second = duration_millisecond.to_secs(); - if duration_second != duration_millisecond { - Some("duration_second_millisecond test failed: durations are not equal") - } else if second_to_millisecond != 1000 { - Some("duration_second_millisecond test failed: second to millisecond conversion failed") - } else if millisecond_to_second != 1 { - Some("duration_second_millisecond test failed: millisecond to second conversion failed") - } else { - None - } -} - -/// Create a Duration of 1 millisecond and 1000 microseconds, convert between them and check if they are equal. -pub fn duration_millisecond_microsecond(_: Clock) -> Option<&'static str> { - let duration_millisecond = Duration::from_millis(1); - let duration_microsecond = Duration::from_micros(1000); - let millisecond_to_microsecond = duration_millisecond.to_micros(); - let microsecond_to_millisecond = duration_microsecond.to_millis(); - if duration_millisecond != duration_microsecond { - Some("duration_millisecond_microsecond test failed: durations are not equal") - } else if millisecond_to_microsecond != 1000 { - Some("duration_millisecond_microsecond test failed: millisecond to microsecond conversion failed") - } else if microsecond_to_millisecond != 1 { - Some("duration_millisecond_microsecond test failed: microsecond to millisecond conversion failed") - } else { - None - } -} - -/// Create an Instant of 1 hour and 60 minutes, convert between them and check if they are equal. -pub fn instant_hour_minute(clock: Clock) -> Option<&'static str> { - let frequency = clock.get_frequency(); - let instant_hour = Instant::from_hours(1, frequency); - let instant_minute = Instant::from_mins(60, frequency); - let hour_to_minute = instant_hour.to_mins(); - let minute_to_hour = instant_minute.to_hours(); - if instant_hour != instant_minute { - Some("instant_hour_minute test failed: instants are not equal") - } else if hour_to_minute != 60 { - Some("instant_hour_minute test failed: hour to minute conversion failed") - } else if minute_to_hour != 1 { - Some("instant_hour_minute test failed: minute to hour conversion failed") - } else { - None - } -} - -/// Create an Instant of 1 minute and 60 seconds, convert between them and check if they are equal. -pub fn instant_minute_second(clock: Clock) -> Option<&'static str> { - let frequency = clock.get_frequency(); - let instant_minute = Instant::from_mins(1, frequency); - let instant_second = Instant::from_secs(60, frequency); - let minute_to_second = instant_minute.to_secs(); - let second_to_minute = instant_second.to_mins(); - if instant_minute != instant_second { - Some("instant_minute_second test failed: instants are not equal") - } else if minute_to_second != 60 { - Some("instant_minute_second test failed: minute to second conversion failed") - } else if second_to_minute != 1 { - Some("instant_minute_second test failed: second to minute conversion failed") - } else { - None - } -} - -/// Create an Instant of 1 second and 1000 milliseconds, convert between them and check if they are equal. -pub fn instant_second_millisecond(clock: Clock) -> Option<&'static str> { - let frequency = clock.get_frequency(); - let instant_second = Instant::from_secs(1, frequency); - let instant_millisecond = Instant::from_millis(1000, frequency); - let second_to_millisecond = instant_second.to_millis(); - let millisecond_to_second = instant_millisecond.to_secs(); - if instant_second != instant_millisecond { - Some("instant_second_millisecond test failed: instants are not equal") - } else if second_to_millisecond != 1000 { - Some("instant_second_millisecond test failed: second to millisecond conversion failed") - } else if millisecond_to_second != 1 { - Some("instant_second_millisecond test failed: millisecond to second conversion failed") - } else { - None - } -} - -/// Create an Instant of 1 millisecond and 1000 microseconds, convert between them and check if they are equal. -pub fn instant_millisecond_microsecond(clock: Clock) -> Option<&'static str> { - let frequency = clock.get_frequency(); - let instant_millisecond = Instant::from_millis(1, frequency); - let instant_microsecond = Instant::from_micros(1000, frequency); - let millisecond_to_microsecond = instant_millisecond.to_micros(); - let microsecond_to_millisecond = instant_microsecond.to_millis(); - if instant_millisecond != instant_microsecond { - Some("instant_millisecond_microsecond test failed: instants are not equal") - } else if millisecond_to_microsecond != 1000 { - Some("instant_millisecond_microsecond test failed: millisecond to microsecond conversion failed") - } else if microsecond_to_millisecond != 1 { - Some("instant_millisecond_microsecond test failed: microsecond to millisecond conversion failed") - } else { - None - } -} diff --git a/firmware-support/bittide-sys/src/uart.rs b/firmware-support/bittide-sys/src/uart.rs deleted file mode 100644 index 7b0831a28..000000000 --- a/firmware-support/bittide-sys/src/uart.rs +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -pub mod log; -pub struct UartStatus { - pub receive_buffer_empty: bool, - pub transmit_buffer_full: bool, -} - -pub struct TransmitBufferFull; -pub struct ReceiveBufferEmpty; - -#[derive(Clone)] -/// `Uart` is a structure representing a universal asynchronous receiver-transmitter. -pub struct Uart { - /// `payload_addr` is a mutable pointer to the address of the data payload. - payload_addr: *mut u8, - /// `flags_addr` is a constant pointer to the address of the flags. - flags_addr: *const u8, -} - -impl Uart { - /// Create a new [`Uart`] instance given a base address. - /// - /// # Safety - /// - /// The `base_addr` pointer MUST BE a valid pointer that is backed - /// by a memory mapped UART instance. - pub const unsafe fn new(base_addr: *const ()) -> Uart { - let addr = base_addr as *const u8; - Uart { - payload_addr: addr.cast_mut(), - flags_addr: addr.add(4), - } - } - - /// UART status register output - pub fn read_status(&self) -> UartStatus { - let flags: u8 = unsafe { self.flags_addr.read_volatile() }; - - let rx_mask = 0b10; - let rx_empty = flags & rx_mask; - - let tx_mask = 0b01; - let tx_full = flags & tx_mask; - - UartStatus { - receive_buffer_empty: rx_empty != 0, - transmit_buffer_full: tx_full != 0, - } - } - - /// The `receive` function attempts to receive data from the UART. If no - /// data is available, it keeps looping until data is available. - pub fn receive(&self) -> u8 { - loop { - if let Ok(val) = self.try_receive() { - return val; - } - } - } - - /// The `try_receive` function attempts to receive data from the UART. If no - /// data is available, it returns None. - pub fn try_receive(&self) -> Result { - if self.read_status().receive_buffer_empty { - Err(ReceiveBufferEmpty) - } else { - unsafe { - let data: u8 = self.payload_addr.read_volatile(); - Ok(data) - } - } - } - - /// The `send` function sends the given data to the UART. If the UART is - /// unable to accept the data, it keeps looping until it can send the data. - pub fn send(&self, data: u8) { - loop { - if let Ok(()) = self.try_send(data) { - return; - } - } - } - - /// The `try_send` function attempts to send the given data to the UART. If - /// the UART is unable to accept the data, it returns an error. - pub fn try_send(&self, data: u8) -> Result<(), TransmitBufferFull> { - if self.read_status().transmit_buffer_full { - Err(TransmitBufferFull) - } else { - unsafe { - self.payload_addr.write_volatile(data); - Ok(()) - } - } - } -} - -impl ufmt::uWrite for Uart { - fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { - for b in s.bytes() { - self.send(b); - } - Ok(()) - } - - type Error = (); -} - -impl core::fmt::Write for Uart { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - for b in s.bytes() { - self.send(b); - } - Ok(()) - } -} diff --git a/firmware-support/bittide-sys/src/uart/log.rs b/firmware-support/bittide-sys/src/uart/log.rs deleted file mode 100644 index 1cd0f5afc..000000000 --- a/firmware-support/bittide-sys/src/uart/log.rs +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -use crate::uart; - -// The logger utilizes core::fmt to format the log messages because ufmt formatting is not -// compatible with (dependencies of) the log crate. -use core::fmt::Write; - -/// A global logger instance to be used with the `log` crate. -/// -/// Use `set_logger` to set the `Uart` instance to be used for logging. -/// # Safety -/// Using this logger is only safe if there is only one thread of execution. -/// Even though `UartLogger` is `Send` and `Sync`, The underlying `Uart` is not `Send` or `Sync`. -pub static mut LOGGER: UartLogger = UartLogger { uart: None }; - -/// Wrapper for `Uart` to be used as a logger with the `log` crate -/// Instead of making a new logger, use the `set_logger` method of the `LOGGER` instance. -/// # Safety -/// Using this logger is only safe if there is only one thread of execution. -/// Even though `UartLogger` is `Send` and `Sync`, The underlying `Uart` is not `Send` or `Sync`. -pub struct UartLogger { - uart: Option, -} - -impl UartLogger { - /// Set the logger to use the given UART. - /// # Safety - /// Using this function and logger is only safe if there is only one thread of execution. - /// This function is used to assign the `Uart` instance to a global (`static mut`), but `Uart` is not `Send` or `Sync`. - pub unsafe fn set_logger(&mut self, uart: uart::Uart) { - self.uart = Some(uart); - } -} - -impl log::Log for UartLogger { - fn enabled(&self, metadata: &log::Metadata) -> bool { - log::Level::Info <= metadata.level() - } - - fn log(&self, record: &log::Record) { - if self.enabled(record.metadata()) { - unsafe { - match &mut LOGGER.uart { - Some(l) => { - writeln!( - l, - "{} | {}:{} - {}", - record.level(), - record.file().unwrap(), - record.line().unwrap(), - record.args() - ) - .ok(); - } - None => panic!("Logger not set"), - } - } - } - } - - fn flush(&self) {} -} - -unsafe impl core::marker::Send for UartLogger {} -unsafe impl core::marker::Sync for UartLogger {} diff --git a/firmware-support/bittide-sys/src/utils.rs b/firmware-support/bittide-sys/src/utils.rs deleted file mode 100644 index 2c8d178f9..000000000 --- a/firmware-support/bittide-sys/src/utils.rs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use fdt::node::FdtNode; - -pub fn matches_fdt_name(node: &FdtNode, name: &str) -> bool { - if let Some(node_name) = node.name.split('@').next() { - node_name == name - } else { - false - } -} diff --git a/firmware-support/bittide-sys/tests/elf_common.rs b/firmware-support/bittide-sys/tests/elf_common.rs deleted file mode 100644 index 123c4e345..000000000 --- a/firmware-support/bittide-sys/tests/elf_common.rs +++ /dev/null @@ -1,349 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -#![allow(dead_code)] - -use bittide_sys::program_stream::MemoryConfiguration; -use object::{ - elf, - write::elf::{FileHeader, ProgramHeader, Writer}, -}; -use proptest::{collection, prelude::*}; -use rand::Fill; -use std::panic::AssertUnwindSafe; - -type Addr = u64; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SegmentType { - Text, - Data, - RoData, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Segment { - pub addr: Addr, - pub ty: SegmentType, - pub data: Vec, - pub zero_padding: u64, - pub load: bool, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ElfCreateInfo { - pub segments: Vec, - pub entry: Addr, - pub is_64: bool, - pub endian: object::Endianness, - pub machine: u16, - pub elf_ty: u16, -} - -pub fn elf_info_set_base_addr(info: &mut ElfCreateInfo, base_addr: u64) { - for seg in &mut info.segments { - seg.addr += base_addr; - } - info.entry += base_addr; -} - -pub fn create_elf_file(info: &ElfCreateInfo) -> Vec { - let mut buffer = vec![]; - let mut writer = Writer::new(info.endian, info.is_64, &mut buffer); - writer.reserve_file_header(); - - writer.reserve_program_headers(info.segments.len() as u32); - - let mut segment_offsets = Vec::with_capacity(info.segments.len()); - for seg in &info.segments { - let offset = if seg.load { - writer.reserve(seg.data.len(), 64) - } else { - 0 - }; - - segment_offsets.push(offset); - } - - // Write file header - - let e_type = info.elf_ty; - let e_machine = info.machine; - let e_flags = 0; - - writer - .write_file_header(&FileHeader { - os_abi: elf::ELFOSABI_NONE, - abi_version: 0, - e_type, - e_machine, - e_entry: info.entry, - e_flags, - }) - .unwrap(); - - // Write segments - - for (i, seg) in info.segments.iter().enumerate() { - let p_type = if seg.load { elf::PT_LOAD } else { elf::PT_NULL }; - let p_flags = match &seg.ty { - SegmentType::Text => elf::PF_X | elf::PF_R, - SegmentType::Data => elf::PF_R | elf::PF_W, - SegmentType::RoData => elf::PF_R, - }; - - writer.write_program_header(&ProgramHeader { - p_type, - p_flags, - p_offset: segment_offsets[i] as u64, - p_vaddr: seg.addr, - p_paddr: seg.addr, - p_filesz: if seg.load { seg.data.len() as u64 } else { 0 }, - p_memsz: seg.data.len() as u64 + seg.zero_padding, - p_align: 64, - }); - } - - for seg in &info.segments { - if seg.load && !seg.data.is_empty() { - writer.write_align(64); - writer.write(&seg.data); - } - } - - assert_eq!(writer.reserved_len(), writer.len()); - - buffer -} - -/// Run a function with an allocated buffer within the 32bit address space. -/// -/// To aid with testing, instead of returning a zeroed out buffer, the buffer -/// gets filled with random values. -/// -/// # Safety -/// -/// This function uses `mmap`, which might not work as intended on Windows or non-POSIX -/// platforms. -pub unsafe fn with_32bit_addr_buffer(size: u32, f: impl FnOnce(&mut [u8]) -> R) -> R { - // allocate a buffer within the 32bit address space - - let mem = { - // SAFETY: The protection flags are valid flags and allow memory - // access (not PROT_NONE). - // The flags are valid flags, the memory is not shared and - // guaranteed to be at the specified address. - // The file descriptor is -1, as suggested to be most portable by - // https://linux.die.net/man/2/mmap. - // The offset is 0, as it is ignored due to MAP_ANONYMOUS. - libc::mmap( - std::ptr::null_mut(), - size as usize, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_ANONYMOUS | libc::MAP_PRIVATE | libc::MAP_32BIT, - -1, - 0, - ) - }; - - assert!(mem != libc::MAP_FAILED); - - assert!(!mem.is_null(), "mmap'ed pointer is not null"); - assert!( - (mem as usize) < u32::MAX as usize, - "mmap'ed pointer is within 32 bit address space" - ); - assert!( - (mem as usize + size as usize) < u32::MAX as usize, - "mmap'ed pointer is within 32 bit address space" - ); - assert!( - mem as usize & 0x0FFF == 0, - "mmap'ed pointer is aligned to 4K" - ); - let mem_bytes = mem.cast::(); - - let buf = { - // SAFETY: `size` bytes at `mem_bytes` are initialised to 0 - // (MAP_ANONYMOUS in mmap initialises memory to 0). - // The pointer is a valid pointer (no mmap error, not null). - // The pointer has an alignment valid for any reasonable type (4K). - std::slice::from_raw_parts_mut(mem_bytes, size as usize) - }; - - buf.try_fill(&mut rand::thread_rng()).unwrap(); - - let res = std::panic::catch_unwind(AssertUnwindSafe(|| f(buf))); - - // SAFETY: the pointer `mem` was previously allocated by mmap, `size` is the size of - // the full allocation. - libc::munmap(mem, size as usize); - - match res { - Ok(val) => val, - Err(payload) => std::panic::resume_unwind(payload), - } -} - -pub fn mem_config_from_segs(segs: &[Segment]) -> MemoryConfiguration { - // The ranges are inversed so that they don't default to 0..MAX - // but instead are properly read from the segments. - // Since they are not used as iterators but instead as a pair, it does not - // matter that they yield no values when iterated. - #[allow(clippy::reversed_empty_ranges)] - let mut config = MemoryConfiguration { - instruction_memory: u32::MAX..0, - data_memory: u32::MAX..0, - }; - - for seg in segs { - match seg.ty { - SegmentType::Text => { - config.instruction_memory.start = - config.instruction_memory.start.min(seg.addr as u32); - config.instruction_memory.end = config - .instruction_memory - .end - .max(seg.addr as u32 + seg.data.len() as u32 + seg.zero_padding as u32); - } - SegmentType::Data | SegmentType::RoData => { - config.data_memory.start = config.data_memory.start.min(seg.addr as u32); - config.data_memory.end = config - .data_memory - .end - .max(seg.addr as u32 + seg.data.len() as u32 + seg.zero_padding as u32); - } - } - } - - config -} - -pub fn elf_loaded_buffer_size(info: &ElfCreateInfo) -> u32 { - let config = mem_config_from_segs(&info.segments); - - let start = config - .instruction_memory - .start - .min(config.data_memory.start); - let end = config.instruction_memory.end.max(config.data_memory.end); - - end - start -} - -// -// -// Proptest generators -// -// - -/// A strategy to generate ELF segmtents. The addresses of the segments are -/// consecutive. -pub fn gen_segments( - n_text: usize, - n_data: usize, - n_rodata: usize, - text_can_be_empty: bool, -) -> impl Strategy> { - let text_lower_bound = if text_can_be_empty { 0 } else { 1 }; - let texts = (0..n_text) - .map(|_| { - ( - collection::vec(any::(), text_lower_bound..1000), - (0..1000u64), - ) - }) - .collect::>(); - - let data = (0..n_data) - .map(|_| (collection::vec(any::(), 0..1000), (0..1000u64))) - .collect::>(); - - let rodata = (0..n_rodata) - .map(|_| (collection::vec(any::(), 0..1000), (0..1000u64))) - .collect::>(); - - (texts, data, rodata).prop_map(|(texts, data, rodata)| { - let mut offset = 0; - - let mut v = vec![]; - for (d, padding) in texts { - let data_len = d.len() as u64; - v.push(Segment { - addr: offset, - ty: SegmentType::Text, - zero_padding: padding, - data: d, - load: true, - }); - - offset += data_len + padding; - } - - for (d, padding) in data { - let data_len = d.len() as u64; - v.push(Segment { - addr: offset, - ty: SegmentType::Data, - zero_padding: padding, - data: d, - load: true, - }); - - offset += data_len + padding; - } - - for (d, padding) in rodata { - let data_len = d.len() as u64; - v.push(Segment { - addr: offset, - ty: SegmentType::RoData, - zero_padding: padding, - data: d, - load: true, - }); - - offset += data_len + padding; - } - - v - }) -} - -pub fn gen_entry_in_seg_ty(segs: Vec, seg_ty: SegmentType) -> impl Strategy { - let text_segments = segs - .into_iter() - .filter(|seg| seg.ty == seg_ty) - .collect::>(); - - // range 0..0 is invalid as a strategy, so a bounds check and special case - // for the case the segments are empty are added. - (0..text_segments.len().max(1)).prop_flat_map(move |i| { - if let Some(seg) = text_segments.get(i) { - seg.addr..(seg.addr + seg.data.len().max(1) as u64) - } else { - 0..1 - } - }) -} - -pub fn gen_valid_elf_file() -> impl Strategy { - let segments = (0..10usize, 0..10usize) - .prop_flat_map(|(n_data, n_rodata)| gen_segments(1, n_data, n_rodata, false)); - - // generate entry in text segment - let segments_entry = segments.prop_flat_map(|segs| { - let entry = gen_entry_in_seg_ty(segs.clone(), SegmentType::Text); - (Just(segs), entry) - }); - - segments_entry.prop_map(|(segs, entry)| ElfCreateInfo { - elf_ty: object::elf::ET_EXEC, - endian: object::Endianness::Little, - is_64: false, - machine: object::elf::EM_RISCV, - segments: segs, - entry, - }) -} diff --git a/firmware-support/bittide-sys/tests/program_stream.rs b/firmware-support/bittide-sys/tests/program_stream.rs deleted file mode 100644 index 0a32558e6..000000000 --- a/firmware-support/bittide-sys/tests/program_stream.rs +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - error::Error, - path::{Path, PathBuf}, - process::Command, -}; - -use proptest::prelude::*; -use test_strategy::proptest; - -use bittide_sys::program_stream::*; - -mod elf_common; -use elf_common::*; - -fn find_cabal_root() -> Option { - let current_path = std::env::current_dir().ok()?; - - fn find_root(path: PathBuf) -> Option { - let manifest = path.join("cabal.project"); - if manifest.exists() { - return Some(path); - } - let parent = path.parent()?; - - find_root(parent.to_owned()) - } - - find_root(current_path) -} - -fn build_program_stream_exec(root_path: &Path) -> bool { - let status = Command::new("cabal") - .args(["build", "program-stream"]) - .current_dir(root_path) - .status(); - - let Ok(status) = status else { return false }; - - status.success() -} - -fn program_stream_exec_path() -> Result> { - let root = find_cabal_root().ok_or("Can't find cabal root")?; - let compile_success = build_program_stream_exec(&root); - - if !compile_success { - eprintln!("building of program-stream executable not successful!!"); - return Err("Could not build `program-stream` executable")?; - } - - let output = Command::new("cabal") - .arg("list-bin") - .arg("-v0") - .arg("program-stream") - .current_dir(&root) - .output()?; - - if !output.status.success() { - eprintln!("Running `list-bin` failed"); - return Err("Running `list-bin` failed")?; - } - - let path_str = String::from_utf8(output.stdout)?; - - Ok(PathBuf::from(path_str.trim_end())) -} - -fn stream_for_elf(elf: &Path) -> Result, Box> { - let output = Command::new(&*PROG_STREAM_EXEC).arg(elf).output()?; - - if !output.status.success() { - return Err(format!("Running `{}` failed", PROG_STREAM_EXEC.display()))?; - } - - Ok(output.stdout) -} - -lazy_static::lazy_static! { - static ref PROG_STREAM_EXEC: PathBuf = program_stream_exec_path().unwrap(); -} - -#[test] -fn find_cabal_stuff() { - dbg!(&*PROG_STREAM_EXEC); -} - -unsafe fn write_program_to_memory(input: &mut impl Iterator) -> Option<()> { - let prog_hd = read_program_header(input)?; - - for _ in 0..prog_hd.num_segments { - let seg_hd = read_segment_header(input)?; - write_segment_data(&seg_hd, input)?; - write_padding(&seg_hd); - } - - Some(()) -} - -unsafe fn verify_program_contents(info: &ElfCreateInfo) { - for seg in &info.segments { - let addr = seg.addr as usize as *mut u8; - - let data_slice = - std::slice::from_raw_parts(addr, seg.data.len() + seg.zero_padding as usize); - - assert_eq!(data_slice[0..seg.data.len()], seg.data); - assert!(data_slice[seg.data.len()..].iter().all(|x| *x == 0)); - } -} - -// Generate valid ELF files and convert them into a streaming format. -#[proptest(ProptestConfig { cases: 5000, max_shrink_iters: 1000, ..ProptestConfig::default() })] -fn all_elfs_can_be_converted_to_streaming(#[strategy(gen_valid_elf_file())] info: ElfCreateInfo) { - use std::io::Write; - let mut file = tempfile::NamedTempFile::new().unwrap(); - let elf = create_elf_file(&info); - let _ = file.write(&elf).unwrap(); - - let _ = stream_for_elf(file.path()).expect("creating program-stream works"); -} - -// Generate valid ELF files and make sure that all streams are smaller than the ELF. -#[proptest(ProptestConfig { cases: 5000, max_shrink_iters: 1000, ..ProptestConfig::default() })] -fn all_streams_are_smaller_than_elfs(#[strategy(gen_valid_elf_file())] info: ElfCreateInfo) { - use std::io::Write; - let mut file = tempfile::NamedTempFile::new().unwrap(); - let elf = create_elf_file(&info); - let _ = file.write(&elf).unwrap(); - - let stream = stream_for_elf(file.path()).expect("creating program-stream works"); - - assert!(elf.len() >= stream.len()); -} - -// Generate valid ELF files, convert them to a streaming format and make sure -// the segment data is loaded into the buffer properly. -#[proptest(ProptestConfig { cases: 5000, max_shrink_iters: 1000, ..ProptestConfig::default() })] -fn all_elfs_are_loaded_properly(#[strategy(gen_valid_elf_file())] mut info: ElfCreateInfo) { - use std::io::Write; - - let buffer_size = elf_loaded_buffer_size(&info); - - unsafe { - with_32bit_addr_buffer(buffer_size, |buf| { - let base_addr = buf.as_mut_ptr() as usize as u64; - - elf_info_set_base_addr(&mut info, base_addr); - - let mut file = tempfile::NamedTempFile::new().unwrap(); - let elf = create_elf_file(&info); - let _ = file.write(&elf).unwrap(); - - let stream = stream_for_elf(file.path()).expect("creating program-stream works"); - - write_program_to_memory(&mut stream.into_iter()).unwrap(); - - verify_program_contents(&info); - }); - } -} diff --git a/fourmolu.yaml b/fourmolu.yaml deleted file mode 100644 index 612370822..000000000 --- a/fourmolu.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 -indentation: 2 -column-limit: 90 diff --git a/hie.yaml b/hie.yaml deleted file mode 100644 index 17769b538..000000000 --- a/hie.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -cradle: - cabal: diff --git a/hitl-setup/README.md b/hitl-setup/README.md deleted file mode 100644 index 4be260cbf..000000000 --- a/hitl-setup/README.md +++ /dev/null @@ -1,12 +0,0 @@ - -This folder contains all information and scripts to bootstrap the HITL setup. Setting up should be done manually by running `setup.sh`, CI should only check if the setup is correct. - -The setup script does the following: -* Create a bridge device -* Connect all interfaces defined in the `fpganet` file to the bridge -* Set up the bridge device with a static IP address defined in `fpganet` -* Set up DHCP server for the bridge device diff --git a/hitl-setup/dhcpd.conf b/hitl-setup/dhcpd.conf deleted file mode 100644 index b7164c2c2..000000000 --- a/hitl-setup/dhcpd.conf +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -default-lease-time 600; -max-lease-time 7200; -ddns-update-style none; -subnet 10.0.0.0 netmask 255.255.255.0 { - range 10.0.0.2 10.0.0.254; -} diff --git a/hitl-setup/fpganet b/hitl-setup/fpganet deleted file mode 100644 index f799d1c01..000000000 --- a/hitl-setup/fpganet +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -BRIDGE_NAME=eth-fpga -INTERFACES="enp3s0 enp4s0 enp5s0 enp6s0 enp13s0 enp14s0 enp15s0 enp16s0" -INTERFACE_IP=10.0.0.1/24 diff --git a/hitl-setup/fpganet.service b/hitl-setup/fpganet.service deleted file mode 100644 index d492011f2..000000000 --- a/hitl-setup/fpganet.service +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[Unit] -Description=Set up bridge network to combine multiple NICs into one interface -DefaultDependencies=no -Wants=network.target systemd-udev-settle.service -After=local-fs.target network-pre.target systemd-sysctl.service systemd-modules-load.service systemd-udev-settle.service -Before=network.target shutdown.target network-online.target -Conflicts=shutdown.target - -[Service] -Type=oneshot -RemainAfterExit=true -EnvironmentFile=/etc/fpganet -ExecStart=/usr/sbin/ip link add name $BRIDGE_NAME type bridge -ExecStart=/usr/sbin/ip link set dev $BRIDGE_NAME up -ExecStart=/usr/sbin/ip address add $INTERFACE_IP dev $BRIDGE_NAME -ExecStart=/usr/bin/sh -c 'for INTERFACE in $INTERFACES; do /usr/sbin/ip link set dev $INTERFACE up; done' -ExecStart=/usr/bin/sh -c 'for INTERFACE in $INTERFACES; do /usr/sbin/ip link set dev $INTERFACE master $BRIDGE_NAME; done' - -ExecStop=/usr/sbin/ip link delete name $BRIDGE_NAME type bridge - -[Install] -WantedBy=multi-user.target -WantedBy=network-online.target diff --git a/hitl-setup/setup.sh b/hitl-setup/setup.sh deleted file mode 100755 index 4294029ac..000000000 --- a/hitl-setup/setup.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -FORCE="$1" -set -xuo pipefail -HERE=$(realpath "$(dirname "$0")") -source "$HERE/fpganet" || exit $? - -echo Checking that all INTERFACES exist -for INTERFACE in $INTERFACES; do - ip addr show $INTERFACE > /dev/null || exit $? -done -echo All INTERFACES exist - -FILES="/etc/systemd/system/fpganet.service /etc/fpganet /etc/dhcp/dhcpd.conf /etc/default/isc-dhcp-server" -EXISTS= -for FILE in $FILES; do - if [ -e $FILE ]; then - EXISTS="$EXISTS $FILE" - fi -done -if [ -n "$EXISTS" -a "$FORCE" != -f ]; then - set +x - echo "Running this script with -f will overwrite:" - echo "$EXISTS" - echo "But now it will stop, because -f wasn't supplied" - exit 1 -fi - -set -e -apt-get install -y isc-dhcp-server iproute2 - -cp "$HERE/fpganet.service" /etc/systemd/system -cp "$HERE/fpganet" /etc - -cp "$HERE/dhcpd.conf" /etc/dhcp/ -echo "INTERFACESv4=\"$BRIDGE_NAME\"" > /etc/default/isc-dhcp-server - -systemctl daemon-reload - -systemctl enable fpganet.service -systemctl restart fpganet.service - -systemctl disable isc-dhcp-server6.service -systemctl enable isc-dhcp-server.service -systemctl restart isc-dhcp-server.service diff --git a/host-tools/.cargo/config.toml b/host-tools/.cargo/config.toml deleted file mode 100644 index 52a2b6b00..000000000 --- a/host-tools/.cargo/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[build] -target-dir = "../_build/cargo/host-tools" diff --git a/host-tools/Cargo.lock b/host-tools/Cargo.lock deleted file mode 100644 index 293a134fd..000000000 --- a/host-tools/Cargo.lock +++ /dev/null @@ -1,179 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bittide-sys" -version = "0.1.0" -dependencies = [ - "fdt", - "heapless", - "log", - "rand", - "smoltcp", - "ufmt", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "callisto-lib" -version = "0.1.0" -dependencies = [ - "bittide-sys", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "dummy" -version = "0.1.0" - -[[package]] -name = "fdt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", - "ufmt-write", -] - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "managed" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "smoltcp" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" -dependencies = [ - "bitflags", - "byteorder", - "cfg-if", - "heapless", - "log", - "managed", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "ufmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a64846ec02b57e9108d6469d98d1648782ad6bb150a95a9baac26900bbeab9d" -dependencies = [ - "ufmt-macros", - "ufmt-write", -] - -[[package]] -name = "ufmt-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ufmt-write" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" diff --git a/host-tools/Cargo.lock.license b/host-tools/Cargo.lock.license deleted file mode 100644 index 74e57f201..000000000 --- a/host-tools/Cargo.lock.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2022-2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 diff --git a/host-tools/Cargo.toml b/host-tools/Cargo.toml deleted file mode 100644 index 3d6edb9bc..000000000 --- a/host-tools/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[workspace] -members = [ - "callisto-lib", - "dummy", -] -resolver = "2" diff --git a/host-tools/callisto-lib/Cargo.toml b/host-tools/callisto-lib/Cargo.toml deleted file mode 100644 index 6df484b8b..000000000 --- a/host-tools/callisto-lib/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - -[package] -name = "callisto-lib" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bittide-sys = { path = "../../firmware-support/bittide-sys" } - -[lib] -crate-type = ["staticlib"] diff --git a/host-tools/callisto-lib/src/lib.rs b/host-tools/callisto-lib/src/lib.rs deleted file mode 100644 index 2d7531e69..000000000 --- a/host-tools/callisto-lib/src/lib.rs +++ /dev/null @@ -1,340 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 -use bittide_sys::{callisto, clock_control}; -use std::mem::{align_of, size_of}; - -/// Rust sibling of `Bittide.ClockControl.SpeedChange`. -#[repr(u32)] -pub enum SpeedChange { - /// Keeps the clock as it is. - NoChange = 0, - /// Decreases clock speed. - SlowDown = 1, - /// Increases clock speed. - SpeedUp = 2, -} - -// We use a static assertion to verify that the memory layout appears -// to be as expected for ensuring a consistent encoding at the Haskell -// side. -const _: () = assert!(4 == size_of::()); -const _: () = assert!(4 == align_of::()); - -/// Rust sibling of -/// `Bittide.ClockControl.Callisto.Types.ReframingState`. -#[repr(C)] -pub enum ReframingState { - /// The controller remains in this state until stability has been - /// detected. - Detect, - /// Reframing has taken place. There is nothing more to do. - Done, - /// The controller remains in this state for the predefined - /// number of cycles with the assumption that the elastic buffers - /// of all other nodes are sufficiently stable after that time. - Wait { - /// Stored correction value to be applied at reframing time. - target_correction: f32, - /// Number of cycles to wait until reframing takes place. - cur_wait_time: u32, - }, -} - -const _: () = assert!(12 == size_of::()); -const _: () = assert!(4 == align_of::()); - -const _: () = assert!( - 0u32 == { - let v = ReframingState::Detect; - unsafe { *(&v as *const _ as *const u32) } - } -); - -const _: () = assert!( - 1u32 == { - let v = ReframingState::Done; - unsafe { *(&v as *const _ as *const u32) } - } -); - -#[allow(dead_code)] -const SOME_RF_STATE_WAIT: ReframingState = ReframingState::Wait { - target_correction: 3.321, - cur_wait_time: 12345, -}; - -#[allow(dead_code)] -const CASTED_RF_STATE_WAIT: *const (u32, f32, u32) = - &SOME_RF_STATE_WAIT as *const _ as *const (u32, f32, u32); - -const _: () = assert!( - 2u32 == { - let (x, _, _) = unsafe { *CASTED_RF_STATE_WAIT }; - x - } -); - -const _: () = assert!( - 3.321f32 == { - let (_, x, _) = unsafe { *CASTED_RF_STATE_WAIT }; - x - } -); - -const _: () = assert!( - 12345u32 == { - let (_, _, x) = unsafe { *CASTED_RF_STATE_WAIT }; - x - } -); - -/// Rust sibling of `Bittide.ClockControl.Callisto.Types.ControlSt`. -#[repr(C)] -pub struct ControlSt { - /// Accumulated speed change requests, where - /// * `speedup ~ 1` - /// * `slowdown ~ -1` - pub z_k: i32, - /// Previously submitted speed change request. Used to determine - /// the estimated clock frequency. - pub b_k: SpeedChange, - /// Steady-state value (determined when stability is detected for - /// the first time). - pub steady_state_target: f32, - /// finite state machine for reframing detection - pub rf_state: ReframingState, -} - -const _: () = assert!(24 == size_of::()); -const _: () = assert!(4 == align_of::()); - -#[allow(dead_code)] -const SOME_CONTROL_STATE: ControlSt = ControlSt { - z_k: 54321, - b_k: SpeedChange::SlowDown, - steady_state_target: 3.185, - rf_state: SOME_RF_STATE_WAIT, -}; - -#[allow(dead_code)] -const CASTED_CONTROL_STATE: *const (i32, u32, f32, u32, f32, u32) = - &SOME_CONTROL_STATE as *const _ as *const (i32, u32, f32, u32, f32, u32); - -const _: () = assert!( - 54321i32 == { - let (x, _, _, _, _, _) = unsafe { *CASTED_CONTROL_STATE }; - x - } -); - -const _: () = assert!( - 1u32 == { - let (_, x, _, _, _, _) = unsafe { *CASTED_CONTROL_STATE }; - x - } -); - -const _: () = assert!( - 3.185f32 == { - let (_, _, x, _, _, _) = unsafe { *CASTED_CONTROL_STATE }; - x - } -); - -const _: () = assert!( - 2u32 == { - let (_, _, _, x, _, _) = unsafe { *CASTED_CONTROL_STATE }; - x - } -); - -const _: () = assert!( - 3.321f32 == { - let (_, _, _, _, x, _) = unsafe { *CASTED_CONTROL_STATE }; - x - } -); - -const _: () = assert!( - 12345u32 == { - let (_, _, _, _, _, x) = unsafe { *CASTED_CONTROL_STATE }; - x - } -); - -/// Rust sibling of -/// `Bittide.ClockControl.Callisto.Types.ControlConfig`. -#[repr(C)] -pub struct ControlConfig { - /// Enable reframing. Reframing allows a system to resettle buffers around - /// their midpoints, without dropping any frames. For more information, see - /// [arXiv:2303.11467](https://arxiv.org/abs/2303.11467). - pub reframing_enabled: usize, - /// Number of cycles to wait until reframing takes place after - /// stability has been detected. - pub wait_time: usize, - /// Target data count. See `Bittide.ClockControl.targetDataCount`. - pub target_count: isize, -} - -const _: () = assert!(3 * size_of::() == size_of::()); -const _: () = assert!(align_of::() == align_of::()); - -#[allow(dead_code)] -const SOME_CONTROL_CONFIG: ControlConfig = ControlConfig { - reframing_enabled: 1, - wait_time: 88, - target_count: -23, -}; - -#[allow(dead_code)] -const CASTED_CONTROL_CONFIG: *const (usize, usize, isize) = - &SOME_CONTROL_CONFIG as *const _ as *const (usize, usize, isize); - -const _: () = assert!( - 1usize == { - let (x, _, _) = unsafe { *CASTED_CONTROL_CONFIG }; - x - } -); - -const _: () = assert!( - 88usize == { - let (_, x, _) = unsafe { *CASTED_CONTROL_CONFIG }; - x - } -); - -const _: () = assert!( - -23isize == { - let (_, _, x) = unsafe { *CASTED_CONTROL_CONFIG }; - x - } -); - -fn speed_change_from_ffi(val: &SpeedChange) -> clock_control::SpeedChange { - match val { - SpeedChange::SpeedUp => clock_control::SpeedChange::SpeedUp, - SpeedChange::SlowDown => clock_control::SpeedChange::SlowDown, - SpeedChange::NoChange => clock_control::SpeedChange::NoChange, - } -} - -fn speed_change_to_ffi(val: &clock_control::SpeedChange) -> SpeedChange { - match val { - clock_control::SpeedChange::SpeedUp => SpeedChange::SpeedUp, - clock_control::SpeedChange::SlowDown => SpeedChange::SlowDown, - clock_control::SpeedChange::NoChange => SpeedChange::NoChange, - } -} - -fn reframing_state_from_ffi(val: &ReframingState) -> callisto::ReframingState { - match val { - ReframingState::Detect => callisto::ReframingState::Detect, - ReframingState::Done => callisto::ReframingState::Done, - ReframingState::Wait { - target_correction, - cur_wait_time, - } => callisto::ReframingState::Wait { - target_correction: *target_correction, - cur_wait_time: *cur_wait_time, - }, - } -} - -fn reframing_state_to_ffi(val: &callisto::ReframingState) -> ReframingState { - match val { - callisto::ReframingState::Detect => ReframingState::Detect, - callisto::ReframingState::Done => ReframingState::Done, - callisto::ReframingState::Wait { - target_correction, - cur_wait_time, - } => ReframingState::Wait { - target_correction: *target_correction, - cur_wait_time: *cur_wait_time, - }, - } -} - -fn control_state_from_ffi(st: &ControlSt) -> callisto::ControlSt { - callisto::ControlSt { - z_k: st.z_k, - b_k: speed_change_from_ffi(&st.b_k), - steady_state_target: st.steady_state_target, - rf_state: reframing_state_from_ffi(&st.rf_state), - } -} - -fn control_state_to_ffi(st: &callisto::ControlSt, rs: &mut ControlSt) { - rs.z_k = st.z_k; - rs.b_k = speed_change_to_ffi(&st.b_k); - rs.steady_state_target = st.steady_state_target; - rs.rf_state = reframing_state_to_ffi(&st.rf_state); -} - -fn control_config_from_ffi(cfg: &ControlConfig) -> callisto::ControlConfig { - callisto::ControlConfig { - reframing_enabled: cfg.reframing_enabled, - wait_time: cfg.wait_time, - target_count: cfg.target_count, - } -} - -unsafe fn vsi_from_ptr<'a>(ptr: *const ()) -> &'a [callisto::StabilityIndication] { - let usize_ptr = ptr as *const usize; - let data_ptr = usize_ptr.offset(1) as *const callisto::StabilityIndication; - return std::slice::from_raw_parts(data_ptr, *usize_ptr); -} - -unsafe fn data_counts_from_ptr<'a>(ptr: *const ()) -> &'a [isize] { - let usize_ptr = ptr as *const usize; - let data_ptr = usize_ptr.offset(1) as *const isize; - return std::slice::from_raw_parts(data_ptr, *usize_ptr); -} - -/// Runs the callisto algorithm -/// -/// # Safety -/// -/// - `config_ptr` needs to point to a valid memory address holding a -/// `ControlConfig` -/// - `stability_checks_ptr` needs to point to a valid memory block holding -/// a `VecS n StabilityIndication` -/// - `data_counts_ptr` needs to point to a valid memory address holding -/// a `VecS n (RelDataCountS m) -/// - `control_state_ptr` needs to point to a valid memory address holding -/// a `ControlSt` -#[no_mangle] -pub unsafe extern "C" fn __c_callisto_rust( - config_ptr: *const ControlConfig, - availability_mask: u32, - stability_checks_ptr: *const (), - data_counts_ptr: *const (), - control_state_ptr: *mut ControlSt, -) { - let mut state = control_state_from_ffi(&*control_state_ptr); - - let vsi = vsi_from_ptr(stability_checks_ptr); - let data_counts = data_counts_from_ptr(data_counts_ptr); - - let links_stable = { - let mut mask = 0u32; - for (i, indication) in vsi.iter().enumerate() { - if indication.stable() { - mask |= 1 << i; - } - } - mask & availability_mask - }; - - callisto::callisto( - &control_config_from_ffi(&*(config_ptr as *const ControlConfig)), - availability_mask, - links_stable, - data_counts.iter().copied(), - &mut state, - ); - - control_state_to_ffi(&state, &mut *control_state_ptr) -} diff --git a/host-tools/dummy/Cargo.toml b/host-tools/dummy/Cargo.toml deleted file mode 100644 index 92a69f3ef..000000000 --- a/host-tools/dummy/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Google LLC -# -# SPDX-License-Identifier: CC0-1.0 - - -[package] -name = "dummy" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/host-tools/dummy/src/main.rs b/host-tools/dummy/src/main.rs deleted file mode 100644 index 53d2c8c27..000000000 --- a/host-tools/dummy/src/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Google LLC -// -// SPDX-License-Identifier: Apache-2.0 - -fn main() { - println!("Hello, world!"); -} diff --git a/nix/bin/cache b/nix/bin/cache deleted file mode 100755 index 1b69704b7..000000000 --- a/nix/bin/cache +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -cd "$(git rev-parse --show-toplevel)" -.github/scripts/cache.py "$@" diff --git a/nix/bin/format b/nix/bin/format deleted file mode 100755 index 2f79e5586..000000000 --- a/nix/bin/format +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -ROOT=$(git rev-parse --show-toplevel) - -echo "Formatting Cabal files.." -"${ROOT}"/.github/scripts/cabal-gild.sh - -echo "Formatting Rust files.." -"${ROOT}"/cargo.sh fmt --all -- --emit files - -echo "Formatting Haskell files.." -"${ROOT}"/.github/scripts/fourmolu.sh diff --git a/nix/bin/install-openocd-vexriscv-udev b/nix/bin/install-openocd-vexriscv-udev deleted file mode 100755 index 1dd0e1e4c..000000000 --- a/nix/bin/install-openocd-vexriscv-udev +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -# -# Setup plugdev group, group membership, and udev rules to get openocd-vexriscv -# to work nicely with our JTAG debugger: -# -# https://www.digikey.nl/en/products/detail/seeed-technology-co.,-ltd/114991786/10060366 -# -set -euf -o pipefail - -DEV_GROUP=plugdev -UDEV_RULES_BASENAME="60-openocd.rules" -UDEV_RULES_D="/etc/udev/rules.d" - -exec_print() { - echo "> $@" - "$@" -} - -set +e -getent group "${DEV_GROUP}" >/dev/null 2>&1 -GETENT_EXIT_CODE=$? -set -e - -if [ $GETENT_EXIT_CODE -eq 0 ]; then - echo "'${DEV_GROUP}' group already exists, doing nothing" -else - exec_print sudo groupadd "${DEV_GROUP}" -fi - -CURRENT_USER="$(whoami)" -if groups "${CURRENT_USER}" | grep -q '\b'"${DEV_GROUP}"'\b'; then - echo "'${CURRENT_USER}' is already a member of '${DEV_GROUP}', doing nothing" -else - exec_print sudo usermod -aG "${DEV_GROUP}" "${CURRENT_USER}" - exec_print newgrp "${DEV_GROUP}" -fi - -if [ -f "${UDEV_RULES_D}/${UDEV_RULES_BASENAME}" ]; then - echo "'${UDEV_RULES_D}/${UDEV_RULES_BASENAME}' already exists, doing nothing" -else - exec_print sudo cp "$OPENOCD_DIST/share/openocd/contrib/${UDEV_RULES_BASENAME}" "${UDEV_RULES_D}" - exec_print sudo udevadm control --reload-rules -fi diff --git a/nix/bin/shake b/nix/bin/shake deleted file mode 100755 index e1d2a30a2..000000000 --- a/nix/bin/shake +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: 2022-2024 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# TODO: Calling cargo here is a workaround for the Shakefile now importing -# Bittide.Instances.Hitl.Tests which requires Bittide.Instances.Hitl.VexRiscv -# which uses Template Haskell to read firmware binaries. I.e. Shake can -# no longer call cargo to build the binaries before they are required. -# The future goal is to not have the firmware in the FPGA bitstream as -# described here: https://github.com/bittide/bittide-hardware/issues/502 -echo "Building firmware binaries..." -$(cd firmware-binaries/; cargo build --release) -$(cd firmware-binaries/; cargo build) - -cabal run shake -- "$@" diff --git a/nix/mc.nix b/nix/mc.nix deleted file mode 100644 index e1c7ce368..000000000 --- a/nix/mc.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ pkgs ? import ./nixpkgs.nix {} }: - -pkgs.stdenv.mkDerivation rec { - name = "mc"; - - src = pkgs.fetchurl { - url = "https://dl.min.io/client/mc/release/linux-amd64/archive/mc.RELEASE.2023-10-24T05-18-28Z"; - hash = "sha256-XxKSa2RrUzzeoaVIxURgpNrXjye4sX05m6Av9O42jk0="; - }; - - unpackPhase = ":"; - - installPhase = '' - mkdir -p $out/bin - cp $src $out/bin/mc - chmod +x $out/bin/mc - ''; -} diff --git a/nix/nixpkgs.nix b/nix/nixpkgs.nix index 88896f83d..83112d245 100644 --- a/nix/nixpkgs.nix +++ b/nix/nixpkgs.nix @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2022 Google LLC +# +# SPDX-License-Identifier: Apache-2.0 { sources ? import ./sources.nix }: let @@ -5,9 +8,6 @@ let overlay = _: nixpkgs: { # Nix tooling gitignore = import sources.gitignore { inherit (nixpkgs) lib; }; - - verilog-ethernet = import ./verilog-ethernet.nix { inherit (nixpkgs) pkgs; }; - mc = import ./mc.nix { inherit (nixpkgs) pkgs; }; openocd-vexriscv = import ./openocd-vexriscv.nix { inherit (nixpkgs) pkgs; }; # Haskell overrides diff --git a/nix/openocd-vexriscv.nix b/nix/openocd-vexriscv.nix index f909cb744..ff2d6b777 100644 --- a/nix/openocd-vexriscv.nix +++ b/nix/openocd-vexriscv.nix @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 Google LLC + +# SPDX-License-Identifier: CC0-1.0 { pkgs ? import ./nixpkgs.nix {} }: pkgs.stdenv.mkDerivation rec { diff --git a/nix/sources.json b/nix/sources.json index f81b3e174..01e657fb9 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -5,22 +5,22 @@ "homepage": "", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "sha256": "02wxkdpbhlm3yk5mhkhsp3kwakc16xpmsf2baw57nz1dg459qv8w", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", + "sha256": "1rlja3ba9s1n0icy3aarwhx9hk1jyfgngzizbn1afwwdlpvdlqw0", "type": "tarball", - "url": "https://github.com/hercules-ci/gitignore.nix/archive/637db329424fd7e46cf4185293b9cc8c88c95394.tar.gz", + "url": "https://github.com/hercules-ci/gitignore.nix/archive/43e1aa1308018f37118e34d3a9cb4f5e75dc11d5.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgs": { - "branch": "nixos-24.05", + "branch": "nixos-23.11", "description": "Nix Packages collection", "homepage": "", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8c50662509100d53229d4be607f1a3a31157fa12", - "sha256": "15a6a3v4wlb94qa6cqfn11vpjhyall4ycbj171al5lk2l48nca6r", + "rev": "5bf1cadb72ab4e77cb0b700dab76bcdaf88f706b", + "sha256": "0zjwk71lsri9zmlmhwgz1ny9dv91kaznii2wjff4g2d3w47gy8nj", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/8c50662509100d53229d4be607f1a3a31157fa12.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/5bf1cadb72ab4e77cb0b700dab76bcdaf88f706b.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } diff --git a/clash-vexriscv/nix/sources.json.license b/nix/sources.json.license similarity index 100% rename from clash-vexriscv/nix/sources.json.license rename to nix/sources.json.license diff --git a/clash-vexriscv/nix/sources.nix.license b/nix/sources.nix.license similarity index 100% rename from clash-vexriscv/nix/sources.nix.license rename to nix/sources.nix.license diff --git a/nix/verilog-ethernet.nix b/nix/verilog-ethernet.nix deleted file mode 100644 index da18437e4..000000000 --- a/nix/verilog-ethernet.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ pkgs ? import ./nixpkgs.nix {} }: - -pkgs.stdenv.mkDerivation rec { - name = "verilog-ethernet"; - src = verilog-ethernet-patched; - - verilog-ethernet-src = pkgs.fetchgit { - url = "https://github.com/alexforencich/verilog-ethernet.git"; - rev = "baac5f8d811d43853d59d69957975ead8bbed088"; - sha256 = "sha256-rxoUHjOxxQc/JjEp06vibCJ2OIWbsbEtnkqS1gS+A7g="; - }; - - verilog-ethernet-patched = pkgs.applyPatches { - name = "verilog-ethernet-patched"; - src = verilog-ethernet-src; - patches = [ - (pkgs.fetchpatch { - url = "https://github.com/lmbollen/verilog-ethernet/commit/f762edd71dfebc129dacac64ff5cd5fbf7d67801.patch"; - hash = "sha256-hKHsNViPInDKzOP3OwMZFlQ0RjYYTFPRu6rOJF1g0hQ="; - }) - ]; - }; - - installPhase = '' - mkdir -p $out - mv * $out - ''; -} diff --git a/run_test.sh b/run_test.sh deleted file mode 100755 index f480946e8..000000000 --- a/run_test.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -set -euo pipefail -IFS=$'\n\t' - -cd contranomy -cabal run -friscv-altopts -- clash --verilog -main-is contranomyRVFITE Contranomy -cd .. - -.github/scripts/run_riscv_formal_check.sh $1 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 4006878a0..22968aa2f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,9 +1,18 @@ -# SPDX-FileCopyrightText: 2022 Google LLC +# SPDX-FileCopyrightText: 2022-2023 Google LLC # # SPDX-License-Identifier: CC0-1.0 [toolchain] -channel = "nightly-2023-05-03" -# date = "2023-05-03" -components = ["rustfmt", "clippy", "rust-src"] -targets = ["riscv32imc-unknown-none-elf", "x86_64-unknown-linux-gnu"] + +# !!!!! +# +# When the Rust version here gets updated, also update it in +# .github/workflows/ci.yml. The version is written out for multiple jobs, so +# please make sure that all jobs are updated. +# +# !!!!! + +channel = "1.67.1" +targets = [ "riscv32imc-unknown-none-elf", "x86_64-unknown-linux-gnu" ] +profile = "minimal" +components = [ "clippy", "rustfmt" ] diff --git a/scripts/diff-clash-vexriscv-subtree.py b/scripts/diff-clash-vexriscv-subtree.py deleted file mode 100755 index a19273fbc..000000000 --- a/scripts/diff-clash-vexriscv-subtree.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 - -# Show differences between committed subtree and current version. -# -# Usage: -# -# ./diff-clash-vexriscv-subtree.py -# - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -import os -import re -import tempfile - -from subprocess import check_output, check_call, run - -HEX_RE = "[a-f0-9]+" -SUBTREE_RE = re.compile(f"Squashed 'clash-vexriscv/' changes from (?P{HEX_RE})..(?P{HEX_RE})") - -CLASH_VEXRISCV_REPO = "https://github.com/clash-lang/clash-vexriscv.git" - -def get_git_root(): - return check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip() - -def git_log_clash_vexriscv(): - return check_output(["git", "log", "--oneline", "clash-vexriscv"], text=True) - -def get_latest_clash_vexriscv_commit(): - for line in git_log_clash_vexriscv().splitlines(): - match = SUBTREE_RE.search(line) - if match: - return match.group("to") - -def diff(dir1, dir2): - cmd = ["diff", "-ru", dir1, dir2] - run(cmd, check=True) - -def main(): - commit = get_latest_clash_vexriscv_commit() - - with tempfile.TemporaryDirectory() as tmpdir: - check_call(["git", "clone", CLASH_VEXRISCV_REPO, tmpdir]) - check_call(["git", "checkout", commit], cwd=tmpdir) - diff(tmpdir, "clash-vexriscv") - -if __name__ == "__main__": - os.chdir(get_git_root()) - main() diff --git a/scripts/update-clash-vexriscv-subtree.sh b/scripts/update-clash-vexriscv-subtree.sh deleted file mode 100755 index e9494fc11..000000000 --- a/scripts/update-clash-vexriscv-subtree.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Usage: -# -# ./update-clash-vexriscv-subtree.sh -# -# Example: -# -# ./update-clash-vexriscv-subtree.sh 6a0805f237ec511d2db9cbabbb8f28e6bbe93db4 -# - -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 -cd "$(git rev-parse --show-toplevel)" -git subtree pull --prefix clash-vexriscv/ https://github.com/clash-lang/clash-vexriscv.git "$1" --squash diff --git a/shell.nix b/shell.nix index 766765f5f..714afdd50 100644 --- a/shell.nix +++ b/shell.nix @@ -6,60 +6,28 @@ pkgs.mkShell { name = "shell"; buildInputs = - # Ideally, we'd add all (Hackage) dependencies to the dependency list so we - # don't have to compile everything ourselves. I haven't figured out how to - # do this yet without introducing conflicts with `cabal.project`. - # - # pkgs.haskellPackages.bittide-extra.env.nativeBuildInputs ++ - # [ - pkgs.cabal-install - pkgs.haskellPackages.cabal-gild - pkgs.haskellPackages.fourmolu - pkgs.dtc pkgs.gcc - - pkgs.ghc pkgs.pkg-config - pkgs.python311Full - pkgs.python311Packages.matplotlib - pkgs.python311Packages.scipy - pkgs.python311Packages.GitPython - pkgs.python311Packages.pyaml pkgs.sbt pkgs.scala pkgs.verilator - pkgs.which - pkgs.jq - pkgs.unzip - pkgs.ndisc6 - # Simulation report generation - pkgs.dot2tex - pkgs.texlive.combined.scheme-medium - pkgs.poppler_utils + # Haskell toolchain + pkgs.cabal-install + # pkgs.haskell.compiler.ghc90 + # pkgs.haskell.compiler.ghc92 + pkgs.haskell.compiler.ghc94 + # pkgs.haskell.compiler.ghc96 (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml) - # For Cabal to clone git repos - pkgs.git - pkgs.cacert - - # HDL dependencies - pkgs.verilog-ethernet - - # CI scripts - pkgs.python311Packages.docopt - pkgs.python311Packages.dateutil - pkgs.mc - pkgs.pcre - - # VexRiscv OpenOCD + # VexRiscV needs a special openocd pkgs.openocd-vexriscv pkgs.gdb - # UART communication - pkgs.picocom + # For Cabal to clone git repos + pkgs.git # For upgrading Nix env. To update dependencies (within bounds of the currently # tracking NixOS version) use: @@ -77,13 +45,8 @@ pkgs.mkShell { shellHook = '' # Prevents Perl warnings export LC_ALL="C.UTF-8"; - export VERILOG_ETHERNET_SRC="${pkgs.verilog-ethernet}" - export OPENOCD_DIST="${pkgs.openocd-vexriscv}" # Mixing Nix Cabal and non-Nix Cabal yields some weird linking errors. export CABAL_DIR="$HOME/.cabal-nix"; - - # Allow writing 'shake ...' instead of 'cabal run shake -- ...' - export PATH="$(git rev-parse --show-toplevel)/nix/bin:$PATH"; ''; } diff --git a/vivado-hs/LICENSE b/vivado-hs/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/vivado-hs/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vivado-hs/src/Vivado.hs b/vivado-hs/src/Vivado.hs deleted file mode 100644 index 58d0bd6b1..000000000 --- a/vivado-hs/src/Vivado.hs +++ /dev/null @@ -1,49 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -{- | Lets Vivado execute Tcl code by attaching to stdin and stdout of Vivado in -Tcl mode. - -There are two main things to keep in mind when working with this module: - - 1. Vivado Tcl commands can return objects that, when evaluated, are echoed - to the Vivado console and log file as a Tcl string due to a feature of - Tcl called "shimmering". This module can then return them as a Haskell - string. Commands that expect such objects cannot be passed the shimmered - string. There are several options to work around this: - - a. Keep the object in Tcl land by storing it in a variable using `set`. - - b. Keep the object in Tcl land by using dedicated Vivado helper commands - such as `current_hw_server`, `current_hw_target`, `current_hw_ila`, - etc, to "set" the current object of that type. - - c. Pass the shimmered string from Haskell to a function that can lookup - the corresponding object again. This can be done using the same - commands as in the previous option, but now used to get the objects - instead of setting them. - - 2. Bringing objects into Haskell results in shimmering, which changes their - representation from a faster native Tcl object to a Tcl string. This may - have performance implications. Furthermore, Vivado truncates shimmered - strings to the number of characters set in the - tcl.collectionResultDisplayLimit parameter, which supposedly has a - default value of 500. This implies that it is challenging to transfer - large amounts of data from Vivado to Haskell through the approach taken - by this module. - -Refer to the "Vivado Design Suite Tcl Command Reference Guide" (UG835) for -more information. --} -module Vivado ( - with, - exec, - exec_, - execPrint, - execPrint_, - VivadoHandle (..), - TclException (..), -) where - -import Vivado.Internal diff --git a/vivado-hs/src/Vivado/Internal.hs b/vivado-hs/src/Vivado/Internal.hs deleted file mode 100644 index e3e1f503a..000000000 --- a/vivado-hs/src/Vivado/Internal.hs +++ /dev/null @@ -1,266 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE NoFieldSelectors #-} - -module Vivado.Internal where - -import Prelude - -import Control.Exception (Exception, finally, throwIO) -import Control.Monad (forM_, unless, void) -import Data.Foldable (toList) -import Data.List (intercalate) -import Data.List.Extra (isPrefixOf, splitOn, trim) -import Data.Maybe (fromJust) -import Data.Sequence (Seq ((:|>))) -import Data.String.Interpolate (__i) -import Data.Typeable (Typeable) -import GHC.Stack (HasCallStack) -import System.Directory.Extra (removeFile) -import System.Environment (setEnv) -import System.IO (Handle) -import System.Process - -import System.IO qualified as IO -import System.IO.Temp qualified as Temp - -data VivadoHandle = VivadoHandle - { stdin :: Handle - -- ^ Handle to write to - , stdout :: Handle - -- ^ Handle to read from. Note that TCL does not use stderr. - , process :: ProcessHandle - , logHandle :: Handle - -- ^ Handle to full log: everything that Vivado writes to stdout, and everything - -- that we write to Vivado. - , logPath :: FilePath - , prettyLogHandle :: Handle - -- ^ Handle to pretty log: everything that Vivado writes to stdout, and everything - -- that we write to Vivado, but without the magic and error handling cruft. - , prettyLogPath :: FilePath - } - -data TclException = TclException - { cmd :: String - , stdout :: String - , retCode :: ErrorCode - , errMsg :: String - , logPath :: String - , prettyLogPath :: String - } - deriving (Typeable) - -instance Show TclException where - show :: TclException -> String - show (TclException{..}) = - "TclException: " - <> [__i| - Got return code #{retCode} while executing: - - #{cmd} - - Error message was: - - #{errMsg} - - Output before and during the crash: - - #{trim stdout} - - Log up to the crash: - - Full: #{logPath} - Pretty: #{prettyLogPath} - |] - -instance Exception TclException - -{- | Magic string that we'll instruct Vivado to echo back to us to signal that -it has finished processing a command. --} -magic :: String -magic = "471ac6f71ba9bd7982741d53edfe809d50f43035645fe99f890761b2bf1ef6bfac18" - -type ErrorCode = String -data Filter = Continue | Stop | StopWithError ErrorCode - -{- | Utility function that reads lines from a handle, and applies a filter to -each line. If the filter returns 'Continue', the function will continue -reading lines. If the filter returns @Stop Ok@, the function will return -successfully. If the filter returns @Stop e@, the function will return a -'Just' with the supplied error message. --} -expectLine :: - (HasCallStack) => - VivadoHandle -> - (String -> IO Filter) -> - IO (Seq String, Maybe ErrorCode) -expectLine v f = go mempty - where - go :: Seq String -> IO (Seq String, Maybe ErrorCode) - go acc = do - line <- IO.hGetLine v.stdout - - IO.hPutStrLn v.logHandle line - unless (magic `isPrefixOf` line) $ - IO.hPutStrLn v.prettyLogHandle line - - let lines' = acc :|> line - f line >>= \case - Continue -> go lines' - Stop -> return (lines', Nothing) - StopWithError code -> return (lines', Just code) - --- | Write a line to the Vivado handle -writeLine :: VivadoHandle -> String -> String -> IO () -writeLine v prettyS s = do - forM_ (lines prettyS) $ \l -> IO.hPutStrLn v.prettyLogHandle (">>> " <> l) - forM_ (lines s) $ \l -> IO.hPutStrLn v.logHandle (">>> " <> l) - IO.hPutStrLn v.stdin s - -{- | Execute a command in Vivado and return the resulting standard output and -the command result. - -Careful: do not use this function with unverified user input, as it does not -attempt to sanitize the input. --} -exec :: VivadoHandle -> String -> IO (String, String) -exec v cmd = do - -- handle exceptionHandler $ do - -- Write a line that would let Vivado run the command in a catch construct and - -- print our magic string after that. If an error occurred, the return code is - -- included and what is returned is the error message. Otherwise the command - -- result is returned. - writeLine - v - cmd - [__i| - if { [catch {#{cmd}} result_#{magic} opt_dict_#{magic}] } { - puts {} - puts -nonewline {#{magic} ERR } - puts [dict get $opt_dict_#{magic} {-code}] - puts $result_#{magic} - } else { - puts {} - puts {#{magic} OK} - puts $result_#{magic} - } - |] - - -- Discard the line with the magic string at the end - (stdout :|> _, mErr) <- expectLine v filtUntilMagic - let stdoutS = intercalate "\n" $ toList stdout - - -- The return value - (retVal, _) <- expectLine v filtUntilEnd - let retValS = intercalate "\n" $ toList retVal - - case mErr of - Nothing -> - return (stdoutS, retValS) - Just returnCode -> do - throwIO - ( TclException - { cmd = cmd - , stdout = stdoutS - , retCode = returnCode - , errMsg = retValS - , logPath = v.logPath - , prettyLogPath = v.prettyLogPath - } - ) - where - filtUntilMagic :: String -> IO Filter - filtUntilMagic line - | magic `isPrefixOf` line = case splitOn " " line of - [_magic, "OK"] -> return Stop - [_magic, "ERR", code] -> return (StopWithError code) - _ -> error $ "Unexpected magic string format: " <> line - | otherwise = return Continue - - filtUntilEnd :: String -> IO Filter - filtUntilEnd _ = do - inputAvailable <- IO.hReady v.stdout - return $ if inputAvailable then Continue else Stop - -{- | Execute a command in Vivado and ignore the command result. - -Careful: do not use this function with unverified user input, as it does not -attempt to sanitize the input. --} -exec_ :: VivadoHandle -> String -> IO () -exec_ v cmd = void (exec v cmd) - -{- | Execute a command in Vivado, print the resulting standard output and return -the command result. - -Careful: do not use this function with unverified user input, as it does not -attempt to sanitize the input. --} -execPrint :: VivadoHandle -> String -> IO String -execPrint v cmd = do - (stdout, result) <- exec v cmd - putStr stdout - return result - -{- | Execute a command in Vivado, print the resulting standard output and ignore -the command result. - -Careful: do not use this function with unverified user input, as it does not -attempt to sanitize the input. --} -execPrint_ :: VivadoHandle -> String -> IO () -execPrint_ v cmd = do - (stdout, _) <- exec v cmd - putStr stdout - -{- | Run a block of code with a Vivado handle. Example usage: - -> import qualified Vivado as V -> -> V.with $ \v -> do -> output <- V.exec v "puts hello" -> putStrLn output --} -with :: (VivadoHandle -> IO a) -> IO a -with f = do - systemTmpDir <- Temp.getCanonicalTemporaryDirectory - (logPath, logHandle) <- Temp.openTempFile systemTmpDir "vivado-hs.log" - (prettyLogPath, prettyLogHandle) <- Temp.openTempFile systemTmpDir "pretty-vivado-hs.log" - - a <- - finally - -- do: - ( do - setEnv "XILINX_LOCAL_USER_DATA" "no" -- Prevents multiprocessing issues - withCreateProcess vivadoProc $ - \(fromJust -> stdin) (fromJust -> stdout) _stderr process -> do - IO.hSetBuffering stdout IO.LineBuffering - IO.hSetBuffering stdin IO.LineBuffering - let v = VivadoHandle{..} - f v - ) - -- finally: - ( do - IO.hClose logHandle - IO.hClose prettyLogHandle - ) - - -- Remove log files if there weren't any exceptions - removeFile logPath - removeFile prettyLogPath - - pure a - where - vivadoProc = - (proc "vivado" ["-mode", "tcl"]) - { std_in = CreatePipe - , std_out = CreatePipe - } diff --git a/vivado-hs/src/Vivado/Tcl.hs b/vivado-hs/src/Vivado/Tcl.hs deleted file mode 100644 index 589571c64..000000000 --- a/vivado-hs/src/Vivado/Tcl.hs +++ /dev/null @@ -1,346 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} - -{-# HLINT ignore "Use camelCase" #-} - -{- | Haskell abstractions over Vivado Hardware Manager Tcl objects and commands. -See section "Description of Hardware Manager Tcl Objects and Commands" of the -"Vivado Design Suite User Guide Programming and Debugging" (UG908) for more -information. --} -module Vivado.Tcl where - -import Control.Monad (unless, void, when) -import Data.Maybe (listToMaybe) -import Vivado - --- | Executes a TCL command with an optional list of arguments. -execCmd :: VivadoHandle -> String -> [String] -> IO String -execCmd v cmd args = execPrint v $ unwords $ cmd : args - --- | Executes a TCL command with an optional list of arguments. -execCmd_ :: VivadoHandle -> String -> [String] -> IO () -execCmd_ v cmd = void . execCmd v cmd - -{- | Attempts to interpret a Tcl expression as a list and return it as a Haskell -list. May very well fail, even with valid Tcl lists. --} -tclToList :: String -> [String] -tclToList = go [] - where - go :: [String] -> String -> [String] - go acc [] = acc - go acc (' ' : xs) = go acc xs - go acc ('\n' : xs) = go acc xs - go acc list@('"' : xs) = do - let (word, list') = span (/= '"') xs - unless (listToMaybe list' == Just '"') $ - error $ - "No closing '\"' found in " <> show list - go (acc <> [word]) (tail list') - go acc list@('{' : xs) = do - let (word, list') = span (/= '}') xs - unless (listToMaybe list' == Just '}') $ - error $ - "No closing brace '}' found in " <> show list - when ('{' `elem` list) $ - error "Nested Tcl braces ('{', '}') are not supported by this function." - go (acc <> [word]) (tail list') - go acc xs = go (acc <> [head $ words xs]) (unwords $ tail $ words xs) - -embrace :: String -> String -embrace s = '{' : s <> "}" - --- | Produces a Tcl expression for a list from a given Haskell list. -listToTcl :: [String] -> String -listToTcl l = "[list " <> unwords (toWord <$> l) <> "]" - where - toWord s = if ' ' `elem` s then embrace s else s - --- * Hardware Manager Tcl objects and commands - --- ** hw_server Tcl commands - --- | hw_server Tcl object -newtype HwServer = HwServer {fromHwServer :: String} - deriving (Eq) - -instance Show HwServer where - show = fromHwServer - --- | Open a connection to a hardware server. Wrapper for the equally named Vivado Hardware Server Tcl command. -connect_hw_server :: - VivadoHandle -> - -- | Arguments - [String] -> - IO HwServer -connect_hw_server v = fmap HwServer . execCmd v "connect_hw_server" - --- | Get or set the current hardware server. -current_hw_server :: - VivadoHandle -> - -- | Arguments - [String] -> - IO HwServer -current_hw_server v = fmap HwServer . execCmd v "current_hw_server" - --- | Close a connection to a hardware server. -disconnect_hw_server :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -disconnect_hw_server v = execCmd_ v "disconnect_hw_server" - --- | Get list of hardware server names for the hardware servers. -get_hw_servers :: - VivadoHandle -> - -- | Arguments - [String] -> - IO [HwServer] -get_hw_servers v args = do - hwServers <- execCmd v "get_hw_servers" args - return $ HwServer <$> tclToList hwServers - --- | Refresh a connection to a hardware server. -refresh_hw_server :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -refresh_hw_server v = execCmd_ v "refresh_hw_server" - --- ** hw_target Tcl commands - --- | hw_target Tcl object -newtype HwTarget = HwTarget {fromHwTarget :: String} - deriving (Eq, Ord) - -instance Show HwTarget where - show = fromHwTarget - --- | Close a hardware target. -close_hw_target :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -close_hw_target v = execCmd_ v "close_hw_target" - --- | Get or set the current hardware target. -current_hw_target :: - VivadoHandle -> - -- | Arguments - [String] -> - IO HwTarget -current_hw_target v = fmap HwTarget . execCmd v "current_hw_target" - --- | Get list of hardware targets for the hardware servers. -get_hw_targets :: - VivadoHandle -> - -- | Arguments - [String] -> - IO [HwTarget] -get_hw_targets v args = do - hwTargets <- execCmd v "get_hw_targets" args - return $ HwTarget <$> tclToList hwTargets - --- | Open a connection to a hardware target on the hardware server. -open_hw_target :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -open_hw_target v = execCmd_ v "open_hw_target" - --- | Refresh a connection to a hardware server. -refresh_hw_target :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -refresh_hw_target v = execCmd_ v "refresh_hw_target" - --- ** hw_device Tcl commands - --- | hw_device Tcl object -newtype HwDevice = HwDevice {fromHwDevice :: String} - deriving (Eq) - -instance Show HwDevice where - show = fromHwDevice - --- | Get or set the current hardware device. -current_hw_device :: - VivadoHandle -> - -- | Arguments - [String] -> - IO HwDevice -current_hw_device v = fmap HwDevice . execCmd v "current_hw_device" - --- | Get list of hardware devices for the target. -get_hw_devices :: - VivadoHandle -> - -- | Arguments - [String] -> - IO [HwDevice] -get_hw_devices v args = do - hwDevices <- execCmd v "get_hw_devices" args - return $ HwDevice <$> tclToList hwDevices - --- | Program AMD FPGA devices. -program_hw_devices :: - VivadoHandle -> - -- | Arguments - [String] -> - IO [HwDevice] -program_hw_devices v args = do - hwDevices <- execCmd v "program_hw_devices" args - return $ HwDevice <$> tclToList hwDevices - --- | Refresh a hardware device. -refresh_hw_device :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -refresh_hw_device v = execCmd_ v "refresh_hw_device" - --- ** hw_ila Tcl commands - --- | hw_ila Tcl object -newtype HwIla = HwIla {fromHwIla :: String} - deriving (Eq) - -instance Show HwIla where - show = fromHwIla - --- | Get or set the current hardware ILA. -current_hw_ila :: - VivadoHandle -> - -- | Arguments - [String] -> - IO HwIla -current_hw_ila v = fmap HwIla . execCmd v "current_hw_ila" - --- | Get list of hardware ILAs for the target. -get_hw_ilas :: - VivadoHandle -> - -- | Arguments - [String] -> - IO [HwIla] -get_hw_ilas v args = do - hwIlas <- execCmd v "get_hw_ilas" args - return $ HwIla <$> tclToList hwIlas - --- | Reset hw_ila control properties to default values. -reset_hw_ila :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -reset_hw_ila v = execCmd_ v "reset_hw_ila" - --- | Arm hw_ila triggers. -run_hw_ila :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -run_hw_ila v = execCmd_ v "run_hw_ila" - --- | Wait until all data has been captured. -wait_on_hw_ila :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -wait_on_hw_ila v = execCmd_ v "wait_on_hw_ila" - --- ** hw_probe Tcl commands - --- | hw_probe Tcl object -newtype HwProbe = HwProbe {fromHwProbe :: String} - deriving (Eq) - -instance Show HwProbe where - show = fromHwProbe - --- | Creates a new hardware probe from physical ILA probe ports and/or constant values. -create_hw_probe :: - VivadoHandle -> - -- | Arguments - [String] -> - IO HwProbe -create_hw_probe v = fmap HwProbe . execCmd v "create_hw_probe" - --- | Deletes a user-defined hardware probe creating using the create_hw_probe command. -delete_hw_probe :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -delete_hw_probe v = execCmd_ v "delete_hw_probe" - --- | Get list of hardware probes. -get_hw_probes :: - VivadoHandle -> - -- | Arguments - [String] -> - IO [HwProbe] -get_hw_probes v args = do - hwProbes <- execCmd v "get_hw_probes" args - return $ HwProbe <$> tclToList hwProbes - --- ** hw_vio Tcl commands - --- | hw_vio Tcl object -newtype HwVio = HwVio {fromHwVio :: String} - deriving (Eq) - -instance Show HwVio where - show = fromHwVio - --- | Write hw_probe OUTPUT_VALUE properties values to VIO cores. -commit_hw_vio :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -commit_hw_vio v = execCmd_ v "commit_hw_vio" - --- | Get a list of hw_vios. -get_hw_vios :: - VivadoHandle -> - -- | Arguments - [String] -> - IO [HwVio] -get_hw_vios v args = do - hwVios <- execCmd v "get_hw_vios" args - return $ HwVio <$> tclToList hwVios - --- | Update hw_probe INPUT_VALUE and ACTIVITY_VALUE properties with values read from VIO cores. -refresh_hw_vio :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -refresh_hw_vio v = execCmd_ v "refresh_hw_vio" - --- | Reset VIO ACTIVITY_VALUE properties, for hw_probes associated with specified hw_vio objects. -reset_hw_vio_activity :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -reset_hw_vio_activity v = execCmd_ v "reset_hw_vio_activity" - --- | Reset VIO core outputs to initial values. -reset_hw_vio_outputs :: - VivadoHandle -> - -- | Arguments - [String] -> - IO () -reset_hw_vio_outputs v = execCmd_ v "refresh_hw_vio" diff --git a/vivado-hs/tests/Tests/Vivado.hs b/vivado-hs/tests/Tests/Vivado.hs deleted file mode 100644 index 468306a4f..000000000 --- a/vivado-hs/tests/Tests/Vivado.hs +++ /dev/null @@ -1,78 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DuplicateRecordFields #-} - -module Tests.Vivado where - -import Control.Concurrent.Async (forConcurrently) -import Control.Exception (try) - -import Test.Tasty -import Test.Tasty.HUnit -import Test.Tasty.TH - -import Vivado qualified as V - -case_hello_world :: Assertion -case_hello_world = do - (stdout, retval) <- V.with $ \v -> V.exec v "" >> V.exec v "puts hello" - stdout @?= "hello\n" - retval @?= "" - -case_hello_world_nonewline :: Assertion -case_hello_world_nonewline = do - (stdout, retval) <- V.with $ \v -> V.exec v "" >> V.exec v "puts -nonewline hello" - stdout @?= "hello" - retval @?= "" - -case_returnVal :: Assertion -case_returnVal = do - (stdout, retval) <- V.with $ \v -> do - _ <- V.exec v "proc my_proc {} { return \"hello\nthere\" }" - V.exec v "my_proc" - stdout @?= "" - retval @?= "hello\nthere" - -case_error :: Assertion -case_error = do - result <- try (V.with (`V.exec` "error fail")) - case result of - Left (V.TclException{retCode, errMsg}) -> do - retCode @?= "1" - errMsg @?= "fail" - _ -> assertFailure "Expected an error" - -case_invalidCommand :: Assertion -case_invalidCommand = do - result <- try (V.with (`V.exec` " / ")) - case result of - Left (V.TclException{retCode, errMsg}) -> do - retCode @?= "1" - errMsg @?= "invalid command name \"/\"" - _ -> assertFailure "Expected an error" - -case_usable_after_error :: Assertion -case_usable_after_error = do - V.with $ \v -> do - result <- try $ V.exec v "error fail" - case result of - Left (V.TclException{retCode, errMsg}) -> do - retCode @?= "1" - errMsg @?= "fail" - (stdout, retval) <- V.exec v "puts hello" - stdout @?= "hello\n" - retval @?= "" - _ -> assertFailure "Expected an error" - -case_async :: Assertion -case_async = do - let nsExpected = [1 .. 8] - nsActual <- forConcurrently nsExpected $ \n -> do - V.with $ \v -> do - V.exec_ v "" -- To get rid of the Vivado initialization message - fmap fst $ V.exec v $ "puts " <> show (n :: Int) - nsActual @?= map ((<> "\n") . show) nsExpected - -tests :: TestTree -tests = $(testGroupGenerator) diff --git a/vivado-hs/tests/unittests.hs b/vivado-hs/tests/unittests.hs deleted file mode 100644 index 9a48256b1..000000000 --- a/vivado-hs/tests/unittests.hs +++ /dev/null @@ -1,19 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Google LLC --- --- SPDX-License-Identifier: Apache-2.0 - -module Main where - -import Test.Tasty - -import Tests.Vivado qualified - -tests :: TestTree -tests = - testGroup - "unittests" - [ Tests.Vivado.tests - ] - -main :: IO () -main = defaultMain tests diff --git a/vivado-hs/vivado-hs.cabal b/vivado-hs/vivado-hs.cabal deleted file mode 100644 index 0b353a2c7..000000000 --- a/vivado-hs/vivado-hs.cabal +++ /dev/null @@ -1,93 +0,0 @@ -cabal-version: 3.4 -name: vivado-hs -version: 0.1.0.0 -synopsis: Haskell wrapper around Vivado TCL -homepage: https://github.com/bittide/bittide -license: Apache-2.0 -license-file: LICENSE -author: QBayLogic B.V. -maintainer: devops@qbaylogic.com -copyright: Google LLC -category: Development -build-type: Simple - -common common-options - default-extensions: - -- TemplateHaskell is used to support convenience functions such as - -- 'listToVecTH' and 'bLit'. - BangPatterns - BinaryLiterals - ConstraintKinds - DataKinds - DefaultSignatures - DeriveAnyClass - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveLift - DeriveTraversable - DerivingStrategies - InstanceSigs - KindSignatures - LambdaCase - NoStarIsType - PolyKinds - QuasiQuotes - RankNTypes - ScopedTypeVariables - StandaloneDeriving - TemplateHaskell - TupleSections - TypeApplications - TypeFamilies - TypeOperators - ViewPatterns - - ghc-options: - -Wall - -Wcompat - -library - import: common-options - exposed-modules: - Vivado - Vivado.Tcl - - other-modules: - Vivado.Internal - - build-depends: - base, - containers, - deepseq, - extra, - process, - string-interpolate, - temporary, - - hs-source-dirs: src - default-language: GHC2021 - -test-suite unittests - import: common-options - type: exitcode-stdio-1.0 - main-is: unittests.hs - other-modules: - Tests.Vivado - - build-depends: - async, - base, - tasty, - tasty-hunit, - tasty-th, - vivado-hs, - - ghc-options: - -threaded - -rtsopts - -with-rtsopts=-N - - hs-source-dirs: tests - default-language: GHC2021 diff --git a/vivado-hs/vivado-hs.cabal.license b/vivado-hs/vivado-hs.cabal.license deleted file mode 100644 index 2b4d55897..000000000 --- a/vivado-hs/vivado-hs.cabal.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 Google LLC - -SPDX-License-Identifier: CC0-1.0 From 67bc71e35038c3c9574fd839003a40b9ef092c59 Mon Sep 17 00:00:00 2001 From: Martijn Bastiaan Date: Wed, 4 Dec 2024 14:16:04 +0100 Subject: [PATCH 2/2] Bump `clash-vexriscv` for VCD support --- .../Bittide/Instances/Hitl/FullMeshHwCc.hs | 2 +- .../Bittide/Instances/Hitl/FullMeshSwCc.hs | 2 +- .../Bittide/Instances/Hitl/HwCcTopologies.hs | 2 +- .../src/Bittide/Instances/Hitl/VexRiscv.hs | 2 +- .../src/Bittide/Instances/Pnr/Ethernet.hs | 2 +- .../Instances/Pnr/ProcessingElement.hs | 2 +- .../tests/Tests/ClockControlWb.hs | 3 +- bittide-instances/tests/Wishbone/Axi.hs | 3 +- .../tests/Wishbone/CaptureUgn.hs | 3 +- bittide-instances/tests/Wishbone/DnaPortE2.hs | 3 +- bittide-instances/tests/Wishbone/Time.hs | 3 +- bittide/src/Bittide/Node.hs | 49 +++++++++++-------- bittide/src/Bittide/ProcessingElement.hs | 12 +++-- 13 files changed, 51 insertions(+), 37 deletions(-) diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs index f7e5f28df..175727ed8 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs @@ -115,7 +115,7 @@ fullMeshRiscvCopyTest clk rst callistoResult dataCounts = unbundle fIncDec toSignals ( circuit $ \jtag -> do [wbFincFdec, wbClockControl, wbDebug] <- - withClockResetEnable clk rst enableGen $ processingElement @dom peConfig -< jtag + withClockResetEnable clk rst enableGen $ processingElement @dom NoDumpVcd peConfig -< jtag fIncDecCallisto -< wbFincFdec [ccd0, ccd1] <- csDupe diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs index 95cc23ed4..ed7cd4d0d 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs @@ -113,7 +113,7 @@ fullMeshRiscvTest clk rst dataCounts = unbundle fIncDec toSignals ( circuit $ \jtag -> do [wbClockControl, wbDebug, wbDummy] <- - withClockResetEnable clk rst enableGen $ processingElement @dom peConfig -< jtag + withClockResetEnable clk rst enableGen $ processingElement @dom NoDumpVcd peConfig -< jtag idleSink -< wbDummy [ccd0, ccd1] <- csDupe diff --git a/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs b/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs index 5fff00c9c..1ace066f0 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs @@ -245,7 +245,7 @@ riscvCopyTest clk rst mask callistoResult dataCounts = unbundle fIncDec toSignals ( circuit $ \jtag -> do [wbFincFdec, wbClockControl, wbDebug] <- - withClockResetEnable clk rst enableGen $ processingElement @dom peConfig -< jtag + withClockResetEnable clk rst enableGen $ processingElement @dom NoDumpVcd peConfig -< jtag fIncDecCallisto -< wbFincFdec [ccd0, ccd1] <- csDupe diff --git a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs b/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs index 18e8afc96..f21bdf99a 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs @@ -63,7 +63,7 @@ vexRiscvInner jtagIn0 uartRx = circuitFn ((uartRx, jtagIn0), (pure (), pure ())) Circuit circuitFn = circuit $ \(uartRx, jtag) -> do - [timeBus, uartBus, statusRegisterBus] <- processingElement peConfig -< jtag + [timeBus, uartBus, statusRegisterBus] <- processingElement NoDumpVcd peConfig -< jtag (uartTx, _uartStatus) <- uartInterfaceWb @dom d16 d16 (uartDf $ SNat @921600) -< (uartBus, uartRx) timeWb -< timeBus diff --git a/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs b/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs index 8cc7d6cdf..f86faa911 100644 --- a/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs +++ b/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs @@ -101,7 +101,7 @@ vexRiscGmii SNat sysClk sysRst rxClk rxRst txClk txRst fwd = rxClkEna macStatIf = wcre $ macStatusInterfaceWb d16 uart = wcre $ uartInterfaceWb d32 d2 (uartDf baud) - pe = wcre processingElement peConfig + pe = wcre processingElement NoDumpVcd peConfig wbToAxiTx' = wcre wbToAxiTx wbAxiRxBuffer = wcre wbAxisRxBufferCircuit (SNat @2048) axiTxPipe = wcre (axiUserMapC (const False) <| axiStreamToByteStream) diff --git a/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs b/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs index 56aee048f..a4fab8158 100644 --- a/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs +++ b/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs @@ -47,7 +47,7 @@ vexRiscUartHello diffClk rst_in = $ withClockResetEnable clk200 rst200 enableGen $ circuit $ \(uartRx, jtag) -> do - [uartBus, timeBus] <- processingElement @Basic200 peConfig -< jtag + [uartBus, timeBus] <- processingElement @Basic200 NoDumpVcd peConfig -< jtag (uartTx, _uartStatus) <- uartInterfaceWb d16 d16 (uartDf $ SNat @921600) -< (uartBus, uartRx) timeWb -< timeBus diff --git a/bittide-instances/tests/Tests/ClockControlWb.hs b/bittide-instances/tests/Tests/ClockControlWb.hs index 3d98f0ef5..8f3d6f12c 100644 --- a/bittide-instances/tests/Tests/ClockControlWb.hs +++ b/bittide-instances/tests/Tests/ClockControlWb.hs @@ -28,6 +28,7 @@ import Test.Tasty.HUnit import Test.Tasty.TH import Text.Parsec import Text.Parsec.String +import VexRiscv (DumpVcd (NoDumpVcd)) -- internal imports import Bittide.Arithmetic.Time (PeriodToCycles) @@ -135,7 +136,7 @@ dut = $ circuit $ \_unit -> do (uartRx, jtag) <- idleSource -< () - [uartBus, ccWb, dbgWb] <- processingElement peConfig -< jtag + [uartBus, ccWb, dbgWb] <- processingElement NoDumpVcd peConfig -< jtag (uartTx, _uartStatus) <- uartInterfaceWb d2 d2 uartSim -< (uartBus, uartRx) [ccd0, ccd1] <- csDupe diff --git a/bittide-instances/tests/Wishbone/Axi.hs b/bittide-instances/tests/Wishbone/Axi.hs index 636e5ad10..d0e9d1acf 100644 --- a/bittide-instances/tests/Wishbone/Axi.hs +++ b/bittide-instances/tests/Wishbone/Axi.hs @@ -37,6 +37,7 @@ import Test.Tasty.HUnit import Test.Tasty.TH import Text.Parsec import Text.Parsec.String +import VexRiscv (DumpVcd (NoDumpVcd)) -- Qualified import qualified Protocols.Df as Df @@ -67,7 +68,7 @@ dut = $ circuit $ \_unit -> do (uartTx, jtag) <- idleSource -< () - [uartBus, axiTxBus, wbNull, axiRxBus] <- processingElement peConfig -< jtag + [uartBus, axiTxBus, wbNull, axiRxBus] <- processingElement NoDumpVcd peConfig -< jtag wbAlwaysAck -< wbNull (uartRx, _uartStatus) <- uartInterfaceWb d2 d2 uartSim -< (uartBus, uartTx) _interrupts <- wbAxisRxBufferCircuit (SNat @128) -< (axiRxBus, axiStream) diff --git a/bittide-instances/tests/Wishbone/CaptureUgn.hs b/bittide-instances/tests/Wishbone/CaptureUgn.hs index bdd429527..4f29ada4c 100644 --- a/bittide-instances/tests/Wishbone/CaptureUgn.hs +++ b/bittide-instances/tests/Wishbone/CaptureUgn.hs @@ -23,6 +23,7 @@ import System.FilePath import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.TH +import VexRiscv (DumpVcd (NoDumpVcd)) import Bittide.CaptureUgn import Bittide.DoubleBufferedRam @@ -92,7 +93,7 @@ dut :: dut eb localCounter = circuit $ \uartRx -> do eb <- ebCircuit -< () jtagIdle <- idleSource -< () - [uartBus, ugnBus] <- processingElement @dom peConfig -< jtagIdle + [uartBus, ugnBus] <- processingElement @dom NoDumpVcd peConfig -< jtagIdle (uartTx, _uartStatus) <- uartInterfaceWb d2 d2 uartSim -< (uartBus, uartRx) _bittideData <- captureUgn localCounter -< (ugnBus, eb) idC -< uartTx diff --git a/bittide-instances/tests/Wishbone/DnaPortE2.hs b/bittide-instances/tests/Wishbone/DnaPortE2.hs index 8df053bd5..6dcff4c11 100644 --- a/bittide-instances/tests/Wishbone/DnaPortE2.hs +++ b/bittide-instances/tests/Wishbone/DnaPortE2.hs @@ -23,6 +23,7 @@ import System.FilePath import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.TH +import VexRiscv (DumpVcd (NoDumpVcd)) import Bittide.DoubleBufferedRam import Bittide.ProcessingElement @@ -56,7 +57,7 @@ dut :: Circuit (Df dom (BitVector 8)) (Df dom (BitVector 8)) dut = circuit $ \uartRx -> do jtag <- idleSource -< () - [uartBus, dnaWb] <- processingElement @dom peConfig -< jtag + [uartBus, dnaWb] <- processingElement @dom NoDumpVcd peConfig -< jtag (uartTx, _uartStatus) <- uartInterfaceWb d2 d2 uartSim -< (uartBus, uartRx) readDnaPortE2Wb simDna2 -< dnaWb idC -< uartTx diff --git a/bittide-instances/tests/Wishbone/Time.hs b/bittide-instances/tests/Wishbone/Time.hs index 92763be62..0052780f9 100644 --- a/bittide-instances/tests/Wishbone/Time.hs +++ b/bittide-instances/tests/Wishbone/Time.hs @@ -32,6 +32,7 @@ import Test.Tasty.HUnit import Test.Tasty.TH import Text.Parsec import Text.Parsec.String +import VexRiscv (DumpVcd (NoDumpVcd)) -- Qualified import qualified Protocols.Df as Df @@ -67,7 +68,7 @@ dut = withClockResetEnable clockGen resetGen enableGen $ circuit $ \_unit -> do (uartRx, jtag) <- idleSource -< () - [uartBus, timeBus] <- processingElement peConfig -< jtag + [uartBus, timeBus] <- processingElement NoDumpVcd peConfig -< jtag (uartTx, _uartStatus) <- uartInterfaceWb d2 d2 uartSim -< (uartBus, uartRx) timeWb -< timeBus idC -< uartTx diff --git a/bittide/src/Bittide/Node.hs b/bittide/src/Bittide/Node.hs index 622b80b24..7084684bb 100644 --- a/bittide/src/Bittide/Node.hs +++ b/bittide/src/Bittide/Node.hs @@ -35,9 +35,9 @@ vector of ever increasing base addresses (increments of 0x1000). simpleNodeConfig :: NodeConfig 1 2 simpleNodeConfig = NodeConfig - (ManagementConfig (ScatterConfig sgConfig) (GatherConfig sgConfig) nmuConfig) + (ManagementConfig (ScatterConfig sgConfig) (GatherConfig sgConfig) nmuConfig NoDumpVcd) switchCal - (repeat (GppeConfig (ScatterConfig sgConfig) (GatherConfig sgConfig) peConfig)) + (repeat (GppeConfig (ScatterConfig sgConfig) (GatherConfig sgConfig) peConfig NoDumpVcd)) where switchCal = CalendarConfig (SNat @1024) (switchEntry :> Nil) (switchEntry :> Nil) sgConfig = CalendarConfig (SNat @1024) (sgEntry :> Nil) (sgEntry :> Nil) @@ -104,6 +104,7 @@ data ManagementConfig nodeBusses where ScatterConfig 4 (NmuRemBusWidth nodeBusses) -> GatherConfig 4 (NmuRemBusWidth nodeBusses) -> PeConfig (nodeBusses + NmuInternalBusses) -> + DumpVcd -> ManagementConfig nodeBusses {- | Configuration for a general purpose processing element together with its link to the @@ -117,6 +118,7 @@ data GppeConfig nmuRemBusWidth where -- has four external busses connected to the instruction memory, data memory -- , 'scatterUnitWb' and 'gatherUnitWb'. PeConfig 4 -> + DumpVcd -> GppeConfig nmuRemBusWidth {-# NOINLINE gppe #-} @@ -148,14 +150,18 @@ gppe :: ( Signal dom (DataLink 64) , Vec 2 (Signal dom (WishboneS2M (Bytes 4))) ) -gppe (GppeConfig scatterConfig gatherConfig peConfig, linkIn, vecToTuple -> (nmuM2S0, nmuM2S1)) = - (linkOut, nmuS2M0 :> nmuS2M1 :> Nil) - where - (suS2M, nmuS2M0) = scatterUnitWb scatterConfig nmuM2S0 linkIn suM2S - (linkOut, guS2M, nmuS2M1) = gatherUnitWb gatherConfig nmuM2S1 guM2S - (_, wbM2Ss) = toSignals (processingElement peConfig) (pure $ JtagIn low low low, wbS2Ms) - (suM2S, guM2S) = vecToTuple wbM2Ss - wbS2Ms = suS2M :> guS2M :> Nil +gppe + ( GppeConfig scatterConfig gatherConfig peConfig dumpVcd + , linkIn + , vecToTuple -> (nmuM2S0, nmuM2S1) + ) = + (linkOut, nmuS2M0 :> nmuS2M1 :> Nil) + where + (suS2M, nmuS2M0) = scatterUnitWb scatterConfig nmuM2S0 linkIn suM2S + (linkOut, guS2M, nmuS2M1) = gatherUnitWb gatherConfig nmuM2S1 guM2S + (_, wbM2Ss) = toSignals (processingElement dumpVcd peConfig) (pure $ JtagIn low low low, wbS2Ms) + (suM2S, guM2S) = vecToTuple wbM2Ss + wbS2Ms = suS2M :> guS2M :> Nil {-# NOINLINE managementUnit #-} @@ -170,13 +176,14 @@ gppeC :: Circuit (CSignal dom (DataLink 64), Vec 2 (Wishbone dom 'Standard nmuRemBusWidth (Bytes 4))) (CSignal dom (DataLink 64)) -gppeC (GppeConfig scatterConfig gatherConfig peConfig) = circuit $ \(linkIn, nmuWbs) -> do - [wbScatCal, wbGathCal] <- idC -< nmuWbs - jtag <- idleSource -< () - [wbScat, wbGu] <- processingElement peConfig -< jtag - linkOut <- gatherUnitWbC gatherConfig -< (wbGu, wbGathCal) - scatterUnitWbC scatterConfig -< (linkIn, wbScat, wbScatCal) - idC -< linkOut +gppeC (GppeConfig scatterConfig gatherConfig peConfig dumpVcd) = + circuit $ \(linkIn, nmuWbs) -> do + [wbScatCal, wbGathCal] <- idC -< nmuWbs + jtag <- idleSource -< () + [wbScat, wbGu] <- processingElement dumpVcd peConfig -< jtag + linkOut <- gatherUnitWbC gatherConfig -< (wbGu, wbGathCal) + scatterUnitWbC scatterConfig -< (linkIn, wbScat, wbScatCal) + idC -< linkOut {- | A special purpose 'processingElement' that manages a Bittide Node. It contains a 'processingElement', 'linkToPe' and 'peToLink' which create the interface for the @@ -202,14 +209,14 @@ managementUnit :: ( Signal dom (DataLink 64) , Vec nodeBusses (Signal dom (WishboneM2S (NmuRemBusWidth nodeBusses) 4 (Bytes 4))) ) -managementUnit (ManagementConfig scatterConfig gatherConfig peConfig) linkIn nodeS2Ms = +managementUnit (ManagementConfig scatterConfig gatherConfig peConfig dumpVcd) linkIn nodeS2Ms = (linkOut, nodeM2Ss) where (suS2M, nmuS2M0) = scatterUnitWb scatterConfig nmuM2S0 linkIn suM2S (linkOut, guS2M, nmuS2M1) = gatherUnitWb gatherConfig nmuM2S1 guM2S (vecToTuple -> (suM2S, guM2S), rest) = splitAtI nmuM2Ss (vecToTuple -> (nmuM2S0, nmuM2S1), nodeM2Ss) = splitAtI rest - (_, nmuM2Ss) = toSignals (processingElement peConfig) (pure $ JtagIn low low low, nmuS2Ms) + (_, nmuM2Ss) = toSignals (processingElement dumpVcd peConfig) (pure $ JtagIn low low low, nmuS2Ms) nmuS2Ms = suS2M :> guS2M :> nmuS2M0 :> nmuS2M1 :> nodeS2Ms managementUnitC :: @@ -228,9 +235,9 @@ managementUnitC :: ( CSignal dom (DataLink 64) , Vec nodeBusses (Wishbone dom 'Standard (NmuRemBusWidth nodeBusses) (Bytes 4)) ) -managementUnitC (ManagementConfig scatterConfig gatherConfig peConfig) = circuit $ \linkIn -> do +managementUnitC (ManagementConfig scatterConfig gatherConfig peConfig dumpVcd) = circuit $ \linkIn -> do jtag <- idleSource -< () - peWbs <- processingElement peConfig -< jtag + peWbs <- processingElement dumpVcd peConfig -< jtag ([wbScatCal, wbScat, wbGathCal, wbGu], nmuWbs) <- splitAtC d4 -< peWbs linkOut <- gatherUnitWbC gatherConfig -< (wbGu, wbGathCal) scatterUnitWbC scatterConfig -< (linkIn, wbScat, wbScatCal) diff --git a/bittide/src/Bittide/ProcessingElement.hs b/bittide/src/Bittide/ProcessingElement.hs index 69af27ae9..559abdfe2 100644 --- a/bittide/src/Bittide/ProcessingElement.hs +++ b/bittide/src/Bittide/ProcessingElement.hs @@ -14,7 +14,7 @@ import Clash.Prelude import Protocols import Protocols.Wishbone -import VexRiscv (CpuIn (..), CpuOut (..), Jtag, JtagOut (debugReset), vexRiscv) +import VexRiscv (CpuIn (..), CpuOut (..), DumpVcd, Jtag, JtagOut (debugReset), vexRiscv) import Bittide.DoubleBufferedRam import Bittide.Extra.Maybe @@ -50,12 +50,13 @@ data PeConfig nBusses where processingElement :: forall dom nBusses. (HiddenClockResetEnable dom) => + DumpVcd -> PeConfig nBusses -> Circuit (Jtag dom) (Vec (nBusses - 2) (Wishbone dom 'Standard (MappedBusAddrWidth 30 nBusses) (Bytes 4))) -processingElement (PeConfig memMapConfig initI initD) = circuit $ \jtagIn -> do - (iBus0, dBus0) <- rvCircuit (pure low) (pure low) (pure low) -< jtagIn +processingElement dumpVcd (PeConfig memMapConfig initI initD) = circuit $ \jtagIn -> do + (iBus0, dBus0) <- rvCircuit dumpVcd (pure low) (pure low) (pure low) -< jtagIn iBus1 <- ilaWb (SSymbol @"instructionBus") 2 D4096 onTransactionWb onTransactionWb -< iBus0 dBus1 <- ilaWb (SSymbol @"dataBus") 2 D4096 onTransactionWb onTransactionWb -< dBus0 @@ -89,6 +90,7 @@ splitAtC SNat = Circuit go rvCircuit :: (HiddenClockResetEnable dom) => + DumpVcd -> Signal dom Bit -> Signal dom Bit -> Signal dom Bit -> @@ -97,13 +99,13 @@ rvCircuit :: ( Wishbone dom 'Standard 30 (Bytes 4) , Wishbone dom 'Standard 30 (Bytes 4) ) -rvCircuit tInterrupt sInterrupt eInterrupt = Circuit go +rvCircuit dumpVcd tInterrupt sInterrupt eInterrupt = Circuit go where go (jtagIn, (iBusIn, dBusIn)) = (jtagOut, (iBusWbM2S <$> cpuOut, dBusWbM2S <$> cpuOut)) where tupToCoreIn (timerInterrupt, softwareInterrupt, externalInterrupt, iBusWbS2M, dBusWbS2M) = CpuIn{..} rvIn = tupToCoreIn <$> bundle (tInterrupt, sInterrupt, eInterrupt, iBusIn, dBusIn) - (cpuOut, jtagOut) = vexRiscv hasClock (hasReset `unsafeOrReset` jtagReset) rvIn jtagIn + (cpuOut, jtagOut) = vexRiscv dumpVcd hasClock (hasReset `unsafeOrReset` jtagReset) rvIn jtagIn jtagReset = unsafeFromActiveHigh (delay False (bitToBool . debugReset <$> jtagOut)) -- | Map a function over the address field of 'WishboneM2S'