From 4b2401783f427d51f1c0321c4f84110569749d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20N=C3=B3brega?= Date: Mon, 7 Oct 2024 17:54:57 +0100 Subject: [PATCH 1/5] feat(ci): Add test to check compliance with REUSE Specification --- .github/workflows/ci.yml | 15 +++++++++++++++ lib/scripts/default.nix | 1 + 2 files changed, 16 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e223fe8db..ca400712b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,3 +117,18 @@ jobs: run: git clean -ffdx - name: doc test run: make doc-test + + reuse: + runs-on: self-hosted + timeout-minutes: 3 + if: ${{ !cancelled() }} + needs: [ doc ] + + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Clean untracked files and directories (like old submodules) + run: git clean -ffdx + - name: Check compliance with REUSE specification + run: nix-shell --run "reuse lint" diff --git a/lib/scripts/default.nix b/lib/scripts/default.nix index 503120660..2d378d29b 100644 --- a/lib/scripts/default.nix +++ b/lib/scripts/default.nix @@ -79,6 +79,7 @@ pkgs.mkShell { yosys gcc libcap # Allows setting POSIX capabilities + reuse py2hwsw ]; } From c63aeb468a42cf71747afbdf0d38fff5c6dc2b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20N=C3=B3brega?= Date: Tue, 8 Oct 2024 09:43:48 +0100 Subject: [PATCH 2/5] feat(lib): Move lib modules to py2hwsw repo See commit 475b2c79f56df2d5aa0e3540ade53c96179e749a of py2hwsw repo. Update py2hwsw version. Remove ci lib test --- .github/workflows/ci.yml | 15 - .gitmodules | 3 - lib/Makefile | 73 - lib/README.md | 65 - lib/hardware/altera/alt_iobuf/alt_iobuf.py | 65 - lib/hardware/altera/altddio_in/altddio_in.py | 77 - .../altera/altddio_out/altddio_out.py | 94 - .../altera/altera_alt_ddr3/altera_alt_ddr3.py | 206 -- .../altera_clk_buf_altclkctrl.py | 31 - .../altera_ddio_out_clkbuf.py | 37 - .../xilinx_axi_interconnect.py | 253 -- .../hardware/src/clock_wizard.v | 91 - .../xilinx_clock_wizard.py | 91 - .../amd/xilinx_ddr4_ctrl/hardware/src/ddr.xdc | 245 -- .../amd/xilinx_ddr4_ctrl/xilinx_ddr4_ctrl.py | 195 -- lib/hardware/amd/xilinx_ibufg/xilinx_ibufg.py | 31 - .../amd/xilinx_oddre1/xilinx_oddre1.py | 37 - .../counter/iob_counter/iob_counter.py | 96 - .../counter/iob_counter_ld/iob_counter_ld.py | 110 - .../counter/iob_modcnt/iob_modcnt.py | 112 - .../hardware/modules/iob_div_slice.py | 159 - .../hardware/simulation/src/iob_div_pipe_tb.v | 106 - .../div/iob_div_pipe/iob_div_pipe.py | 136 - .../simulation/src/iob_div_subshift_tb.v | 105 - .../div/iob_div_subshift/iob_div_subshift.py | 189 -- .../simulation/src/iob_div_subshift_frac_tb.v | 107 - .../iob_div_subshift_frac.py | 204 -- .../iob_div_subshift_signed.py | 227 -- .../iob_add/hardware/src/iob_add.v | 70 - lib/hardware/arith_logic/iob_add/iob_add.py | 18 - lib/hardware/arith_logic/iob_add2/iob_add2.py | 85 - .../hardware/simulation/src/iob_ctls_tb.v | 43 - .../iob_ctls/hardware/src/iob_ctls.v | 48 - lib/hardware/arith_logic/iob_ctls/iob_ctls.py | 22 - .../iob_diff/hardware/src/iob_diff.v | 31 - lib/hardware/arith_logic/iob_diff/iob_diff.py | 18 - .../hardware/src/iob_edge_detect.v | 64 - .../iob_edge_detect/iob_edge_detect.py | 29 - .../iob_fp_add/hardware/src/iob_fp_add.v | 305 -- .../iob_fp/iob_fp_add/iob_fp_add.py | 132 - .../iob_fp/iob_fp_clz/iob_fp_clz.py | 69 - .../iob_fp/iob_fp_cmp/iob_fp_cmp.py | 177 - .../iob_fp_div/hardware/src/iob_fp_div.v | 278 -- .../iob_fp/iob_fp_div/iob_fp_div.py | 30 - .../arith_logic/iob_fp/iob_fp_dq/iob_fp_dq.py | 98 - .../hardware/src/iob_fp_float2int.v | 92 - .../iob_fp_float2int/iob_fp_float2int.py | 18 - .../hardware/src/iob_fp_float2uint.v | 74 - .../iob_fp_float2uint/iob_fp_float2uint.py | 18 - .../iob_fp_fpu/hardware/src/iob_fp_fpu.v | 409 --- .../iob_fp/iob_fp_fpu/iob_fp_fpu.py | 54 - .../hardware/src/iob_fp_int2float.v | 862 ----- .../iob_fp_int2float/iob_fp_int2float.py | 18 - .../iob_fp/iob_fp_minmax/iob_fp_minmax.py | 180 - .../iob_fp_mul/hardware/src/iob_fp_mul.v | 221 -- .../iob_fp/iob_fp_mul/iob_fp_mul.py | 22 - .../iob_fp/iob_fp_round/iob_fp_round.py | 121 - .../iob_fp/iob_fp_special/iob_fp_special.py | 155 - .../iob_fp/iob_fp_sqrt/iob_fp_sqrt.py | 385 --- .../hardware/src/iob_fp_uint2float.v | 856 ----- .../iob_fp_uint2float/iob_fp_uint2float.py | 18 - .../hardware/src/iob_functions.vs | 39 - .../iob_functions/iob_functions.py | 12 - .../arith_logic/iob_int_sqrt/iob_int_sqrt.py | 242 -- .../hardware/simulation/src/iob_prio_enc_tb.v | 44 - .../arith_logic/iob_prio_enc/iob_prio_enc.py | 82 - lib/hardware/arith_logic/iob_xor/iob_xor.py | 78 - lib/hardware/buses/apb2iob/apb2iob.py | 44 - .../buses/apb2iob/document/ARM_AMBA3_APB.pdf | Bin 334313 -> 0 bytes .../document/ARM_AMBA3_APB.pdf.license | 3 - .../apb2iob/document/ARM_AMBA3_APBv2_0.pdf | Bin 359416 -> 0 bytes .../document/ARM_AMBA3_APBv2_0.pdf.license | 3 - .../buses/apb2iob/hardware/src/apb2iob.v | 102 - lib/hardware/buses/arbiter/arbiter.py | 12 - .../buses/arbiter/hardware/src/arbiter.v | 130 - .../arbiter/hardware/src/priority_encoder.v | 80 - lib/hardware/buses/axi2axil/axi2axil.py | 127 - lib/hardware/buses/axi2iob/axi2iob.py | 83 - .../buses/axi2iob/hardware/src/axi2iob.v | 551 ---- .../axi_interconnect/axi_interconnect.py | 131 - .../hardware/src/axi_interconnect.v | 971 ------ .../axi_interconnect_wrapper.py | 306 -- lib/hardware/buses/axil2iob/axil2iob.py | 48 - .../buses/axil2iob/hardware/src/axil2iob.v | 62 - lib/hardware/buses/axil_tasks/axil_tasks.vh | 56 - lib/hardware/buses/axis2axi/axis2axi.py | 234 -- .../hardware/simulation/src/axidelay.v | 121 - .../hardware/simulation/src/axis2axi_tb.v | 524 --- .../buses/axis2axi/hardware/src/axis2axi.v | 94 - .../submodules/axis2axi_in/axis2axi_in.py | 135 - .../axis2axi_in/hardware/src/axis2axi_in.v | 213 -- .../submodules/axis2axi_out/axis2axi_out.py | 99 - .../axis2axi_out/hardware/src/axis2axi_out.v | 158 - lib/hardware/buses/axis2axi/waves.gtkw | 150 - .../buses/axis2axi/waves.gtkw.license | 3 - lib/hardware/buses/axis2fifo/axis2fifo.py | 22 - .../buses/axis2fifo/hardware/src/axis2fifo.v | 132 - lib/hardware/buses/axis2fifo/waves.gtkw | 133 - .../buses/axis2fifo/waves.gtkw.license | 3 - lib/hardware/buses/axis_tasks/axis_tasks.py | 12 - .../axis_tasks/hardware/src/axis_tasks.vs | 39 - .../bus_width_converter.py | 166 - lib/hardware/buses/fifo2axis/fifo2axis.py | 22 - .../buses/fifo2axis/hardware/src/fifo2axis.v | 103 - lib/hardware/buses/fifo2axis/waves.gtkw | 133 - .../buses/fifo2axis/waves.gtkw.license | 3 - lib/hardware/buses/iob2apb/iob2apb.py | 189 -- .../simulation/src/iob2axi_tb.disable.v | 232 -- .../buses/iob2axi/hardware/src/iob2axi.v | 301 -- .../buses/iob2axi/hardware/src/iob2axi_rd.v | 198 -- .../buses/iob2axi/hardware/src/iob2axi_wr.v | 201 -- lib/hardware/buses/iob2axi/iob2axi.py | 50 - lib/hardware/buses/iob2axi/waves.gtkw | 133 - lib/hardware/buses/iob2axi/waves.gtkw.license | 3 - .../buses/iob2axil/hardware/src/iob2axil.v | 78 - lib/hardware/buses/iob2axil/iob2axil.py | 68 - .../hardware/src/iob_asym_converter.v | 113 - .../iob_asym_converter/iob_asym_converter.py | 140 - .../buses/iob_axil_split/iob_axil_split.py | 281 -- .../hardware/src/iob_axistream_in.v | 315 -- .../iob_axistream_in/iob_axistream_in.py | 398 --- .../iob_axistream_in/software/linux/Readme.md | 21 - .../software/linux/drivers/driver.mk | 5 - .../linux/drivers/iob_axistream_in_main.c | 367 --- .../software/linux/iob_axistream_in.dts | 32 - .../software/src/iob-axistream-in.c | 21 - .../software/src/iob-axistream-in.h | 11 - .../src/iob_axistream_in_swreg_pc_emul.c | 36 - .../hardware/src/iob_axistream_out.v | 229 -- .../iob_axistream_out/iob_axistream_out.py | 371 --- .../software/linux/Readme.md | 21 - .../software/linux/drivers/driver.mk | 5 - .../linux/drivers/iob_axistream_out_main.c | 365 -- .../software/linux/iob_axistream_out.dts | 32 - .../software/src/iob-axistream-out.c | 21 - .../software/src/iob-axistream-out.h | 11 - .../src/iob_axistream_out_swreg_pc_emul.c | 34 - .../hardware/src/iob_bus_demux.v | 125 - .../buses/iob_bus_demux/iob_bus_demux.py | 35 - lib/hardware/buses/iob_demux/iob_demux.py | 78 - .../hardware/src/iob_iob2wishbone.v | 135 - .../iob_iob2wishbone/iob_iob2wishbone.py | 18 - lib/hardware/buses/iob_merge/iob_merge.py | 379 --- lib/hardware/buses/iob_mux/iob_mux.py | 80 - lib/hardware/buses/iob_reverse/iob_reverse.py | 57 - lib/hardware/buses/iob_split/iob_split.py | 351 -- .../buses/iob_tasks/document/Makefile | 20 - .../buses/iob_tasks/document/README.md | 38 - .../buses/iob_tasks/document/iob_if_read.drom | 11 - .../document/iob_if_read.drom.license | 3 - .../buses/iob_tasks/document/iob_if_read.png | Bin 15488 -> 0 bytes .../document/iob_if_read.png.license | 3 - .../iob_tasks/document/iob_if_write.drom | 11 - .../document/iob_if_write.drom.license | 3 - .../buses/iob_tasks/document/iob_if_write.png | Bin 16806 -> 0 bytes .../document/iob_if_write.png.license | 3 - .../iob_tasks/hardware/src/iob_tasks.cpp | 124 - .../buses/iob_tasks/hardware/src/iob_tasks.h | 42 - .../buses/iob_tasks/hardware/src/iob_tasks.vs | 56 - lib/hardware/buses/iob_tasks/iob_tasks.py | 12 - .../iob_wishbone2iob/iob_wishbone2iob.py | 210 -- .../clocks_resets/iob_clkbuf/iob_clkbuf.py | 70 - .../clocks_resets/iob_clkmux/iob_clkmux.py | 83 - .../clocks_resets/iob_clock/iob_clock.py | 39 - .../iob_nco/hardware/iob_nco_sync.py | 208 -- .../hardware/simulation/src/iob_nco_tb.v | 96 - lib/hardware/clocks_resets/iob_nco/iob_nco.py | 431 --- .../iob_nco/software/example_firmware.c | 32 - .../iob_nco/software/linux/README.md | 24 - .../iob_nco/software/linux/drivers/driver.mk | 5 - .../software/linux/drivers/iob_nco_main.c | 317 -- .../iob_nco/software/linux/iob_nco.dts | 32 - .../iob_nco/software/linux/user/Makefile | 26 - .../software/linux/user/iob_nco_user.c | 143 - .../iob_nco/software/src/iob_nco.c | 28 - .../iob_nco/software/src/iob_nco.h | 15 - .../software/src/iob_nco_csrs_pc_emul.c | 55 - .../simulation/src/iob_pulse_gen_tb.v | 90 - .../iob_pulse_gen/iob_pulse_gen.py | 165 - .../fifo/iob_bfifo/hardware/src/iob_bfifo.v | 130 - lib/hardware/fifo/iob_bfifo/iob_bfifo.py | 23 - .../simulation/src/iob_fifo_async_tb.v | 255 -- .../hardware/src/iob_fifo_async.v | 196 -- .../fifo/iob_fifo_async/iob_fifo_async.py | 295 -- .../simulation/src/iob_fifo_sync_tb.v | 240 -- .../hardware/src/iob_fifo_sync.v | 143 - .../fifo/iob_fifo_sync/iob_fifo_sync.py | 238 -- .../iob_gray2bin/hardware/src/iob_gray2bin.v | 23 - .../fifo/iob_gray2bin/iob_gray2bin.py | 12 - .../hardware/src/iob_gray_counter.v | 61 - .../fifo/iob_gray_counter/iob_gray_counter.py | 29 - lib/hardware/iob_gpio/Makefile | 64 - lib/hardware/iob_gpio/README.md | 63 - lib/hardware/iob_gpio/hardware/fpga/fpga.mk | 14 - .../iob_gpio/hardware/fpga/quartus/Makefile | 17 - .../iob_gpio/hardware/fpga/quartus/build.sh | 16 - .../iob_gpio/hardware/fpga/quartus/gpio.sdc | 6 - .../iob_gpio/hardware/fpga/quartus/gpio.tcl | 73 - .../iob_gpio/hardware/fpga/quartus/gpio.xdc | 0 .../iob_gpio/hardware/fpga/vivado/Makefile | 15 - .../iob_gpio/hardware/fpga/vivado/build.sh | 10 - .../iob_gpio/hardware/fpga/vivado/gpio.tcl | 38 - .../iob_gpio/hardware/fpga/vivado/gpio.xdc | 0 .../iob_gpio/hardware/include/inst.vh | 26 - lib/hardware/iob_gpio/hardware/include/pio.vh | 8 - .../hardware/simulation/icarus/Makefile | 24 - .../hardware/simulation/simulation.mk | 9 - lib/hardware/iob_gpio/iob_gpio.py | 213 -- .../iob_gpio/software/linux/README.md | 24 - .../iob_gpio/software/linux/drivers/driver.mk | 5 - .../software/linux/drivers/iob_gpio_main.c | 304 -- .../iob_gpio/software/linux/iob_gpio.dts | 32 - .../iob_gpio/software/linux/user/Makefile | 26 - .../software/linux/user/iob_gpio_user.c | 102 - lib/hardware/iob_gpio/software/src/iob-gpio.c | 23 - lib/hardware/iob_gpio/software/src/iob-gpio.h | 23 - .../software/src/iob_gpio_swreg_pc_emul.c | 24 - lib/hardware/iob_iobuf/iob_iobuf.py | 98 - .../iob_picorv32/hardware/src/iob_picorv32.v | 98 - .../iob_picorv32/hardware/src/picorv32.v | 2930 ----------------- lib/hardware/iob_picorv32/iob_picorv32.py | 120 - lib/hardware/iob_regfileif/README.md | 131 - lib/hardware/iob_regfileif/config.mk | 31 - .../iob_regfileif/hardware/hardware.mk | 34 - .../iob_regfileif/hardware/include/inst.vh | 29 - .../iob_regfileif/hardware/include/pio.vh | 11 - lib/hardware/iob_regfileif/iob_regfileif.py | 226 -- .../hardware/src/iob_nativebridgeif.v | 26 - .../iob_nativebridgeif_setup.py | 71 - .../software/python/iobnativebridge.py | 334 -- .../software/python/mkregsregfileif.py | 196 -- lib/hardware/iob_system/README.md | 328 -- lib/hardware/iob_system/document/doc_build.mk | 20 - .../iob_system/document/figures/bd.odg | Bin 24528 -> 0 bytes .../document/figures/bd.odg.license | 3 - .../iob_system/document/figures/bd2.odg | Bin 37522 -> 0 bytes .../document/figures/bd2.odg.license | 3 - .../iob_system/document/figures/csr_gen.odg | Bin 22060 -> 0 bytes .../document/figures/csr_gen.odg.license | 3 - .../iob_system/document/figures/inst.odg | Bin 11379 -> 0 bytes .../document/figures/inst.odg.license | 3 - .../iob_system/document/figures/symb.odg | Bin 14150 -> 0 bytes .../document/figures/symb.odg.license | 3 - .../iob_system/document/figures/tbbd.odg | Bin 11002 -> 0 bytes .../document/figures/tbbd.odg.license | 3 - .../iob_system/document/tsrc/bd_desc.tex | 5 - .../iob_system/document/tsrc/benefits.tex | 10 - .../iob_system/document/tsrc/deliverables.tex | 12 - .../iob_system/document/tsrc/features.tex | 14 - .../iob_system/document/tsrc/intro.tex | 9 - .../iob_system/document/tsrc/presentation.tex | 281 -- .../iob_system/document/tsrc/results.tex | 5 - .../iob_system/document/tsrc/title.tex | 6 - .../iob_system/document/tsrc/ug_title.tex | 13 - .../iob_system/hardware/fpga/fpga_build.mk | 15 - .../fpga/quartus/cyclonev_gt_dk/board.tcl | 513 --- .../quartus/cyclonev_gt_dk/cyclonev_gt_dk.py | 547 --- .../quartus/cyclonev_gt_dk/cyclonev_gt_dk.sdc | 6 - .../hardware/fpga/quartus/quartus.sdc | 8 - .../hardware/fpga/quartus/quartus_postmap.tcl | 9 - .../iob_system/hardware/fpga/src/fpga.sdc | 6 - .../vivado/aes_ku040_db_g/aes_ku040_db_g.py | 420 --- .../vivado/aes_ku040_db_g/aes_ku040_db_g.sdc | 128 - .../hardware/fpga/vivado/basys3/basys3.py | 180 - .../hardware/fpga/vivado/basys3/basys3.sdc | 19 - .../hardware/fpga/vivado/vivado.sdc | 10 - .../hardware/fpga/vivado/vivado_premap.tcl | 95 - .../iob_system_cache_system.py | 310 -- .../iob_system_mwrap/iob_system_mwrap.py | 89 - .../iob_system_sim_wrapper.py | 322 -- .../hardware/simulation/sim_build.mk | 36 - .../iob_system/hardware/simulation/src/.empty | 0 .../hardware/simulation/src/iob_system_tb.cpp | 172 - .../hardware/simulation/src/iob_system_tb.v | 183 - .../hardware/syn/genus/syn_build.tcl | 5 - .../iob_system/hardware/syn/src/bsp.vh | 6 - lib/hardware/iob_system/iob_system.py | 505 --- .../iob_system/scripts/iob_system_utils.py | 308 -- .../software/src/iob_system_firmware.S | 21 - .../software/src/iob_system_firmware.c | 72 - .../software/src/iob_system_firmware.lds | 35 - .../software/src/iob_system_system.h | 13 - lib/hardware/iob_system/software/sw_build.mk | 85 - .../submodules/BOOTROM/iob_bootrom.py | 251 -- .../software_templates/src/iob_system_boot.S | 21 - .../software_templates/src/iob_system_boot.c | 66 - .../src/iob_system_boot.lds | 35 - .../src/iob_system_preboot.S | 34 - .../src/iob_system_preboot.lds | 16 - lib/hardware/iob_system/submodules/VEXRISCV | 1 - .../hardware/simulation/src/timer_tb.v | 77 - .../iob_timer/hardware/src/timer_core.v | 48 - lib/hardware/iob_timer/hardware/timer_core.py | 42 - lib/hardware/iob_timer/iob_timer.py | 213 -- .../iob_timer/software/example_firmware.c | 32 - .../iob_timer/software/linux/README.md | 24 - .../software/linux/drivers/driver.mk | 5 - .../software/linux/drivers/iob_timer_main.c | 319 -- .../iob_timer/software/linux/iob_timer.dts | 32 - .../iob_timer/software/linux/user/Makefile | 26 - .../software/linux/user/iob_timer_user.c | 143 - .../iob_timer/software/src/iob_timer.c | 34 - .../iob_timer/software/src/iob_timer.h | 13 - .../software/src/iob_timer_csrs_pc_emul.c | 78 - lib/hardware/iob_uart/LICENSE | 21 - lib/hardware/iob_uart/Makefile | 25 - lib/hardware/iob_uart/README.md | 107 - lib/hardware/iob_uart/config_setup.mk | 21 - lib/hardware/iob_uart/default.nix | 1 - lib/hardware/iob_uart/document/figures/bd.odg | Bin 19297 -> 0 bytes .../iob_uart/document/figures/bd.odg.license | 3 - .../iob_uart/document/figures/inst.odg | Bin 13254 -> 0 bytes .../document/figures/inst.odg.license | 3 - .../iob_uart/document/figures/symb.odg | Bin 12908 -> 0 bytes .../document/figures/symb.odg.license | 3 - .../iob_uart/document/figures/tbbd.odg | Bin 13969 -> 0 bytes .../document/figures/tbbd.odg.license | 3 - lib/hardware/iob_uart/document/pb/Makefile | 13 - lib/hardware/iob_uart/document/pb/pb.pdf | Bin 61639 -> 0 bytes .../iob_uart/document/pb/pb.pdf.license | 3 - .../iob_uart/document/pb/test.expected | 24 - .../document/pb/test.expected.license | 3 - .../iob_uart/document/tsrc/bd_desc.tex | 5 - .../iob_uart/document/tsrc/benefits.tex | 10 - .../iob_uart/document/tsrc/deliverables.tex | 13 - .../iob_uart/document/tsrc/features.tex | 22 - lib/hardware/iob_uart/document/tsrc/intro.tex | 13 - .../iob_uart/document/tsrc/results.tex | 5 - lib/hardware/iob_uart/document/tsrc/title.tex | 6 - .../iob_uart/document/tsrc/ug_title.tex | 13 - lib/hardware/iob_uart/document/ug/Makefile | 17 - lib/hardware/iob_uart/document/ug/csrs.tex | 19 - lib/hardware/iob_uart/document/ug/if.tex | 51 - lib/hardware/iob_uart/document/ug/inst.tex | 14 - lib/hardware/iob_uart/document/ug/revhist.tex | 5 - lib/hardware/iob_uart/document/ug/sim.tex | 8 - lib/hardware/iob_uart/document/ug/synth.tex | 6 - .../iob_uart/document/ug/test.expected | 71 - .../document/ug/test.expected.license | 3 - lib/hardware/iob_uart/document/ug/ug.pdf | Bin 110255 -> 0 bytes .../iob_uart/document/ug/ug.pdf.license | 3 - .../iob_uart/hardware/simulation/sim_build.mk | 9 - .../hardware/simulation/src/iob_uart_tb.v | 175 - .../hardware/simulation/test.expected | 1 - .../hardware/simulation/test.expected.license | 3 - .../iob_uart/hardware/src/uart_core.v | 385 --- lib/hardware/iob_uart/hardware/uart_core.py | 54 - lib/hardware/iob_uart/iob_uart.py | 333 -- lib/hardware/iob_uart/software/src/iob_uart.c | 129 - lib/hardware/iob_uart/software/src/iob_uart.h | 58 - .../software/src/iob_uart_csrs_pc_emul.c | 88 - lib/hardware/memories/axi_ram/axi_ram.py | 127 - .../memories/axi_ram/hardware/src/axi_ram.v | 402 --- .../hardware/simulation/src/iob_ram_2p_tb.v | 137 - .../memories/ram/iob_ram_2p/iob_ram_2p.py | 174 - .../hardware/simulation/src/iob_ram_at2p_tb.v | 124 - .../memories/ram/iob_ram_at2p/iob_ram_at2p.py | 134 - .../hardware/simulation/src/iob_ram_atdp_tb.v | 158 - .../memories/ram/iob_ram_atdp/iob_ram_atdp.py | 170 - .../simulation/src/iob_ram_atdp_be_tb.v | 190 -- .../ram/iob_ram_atdp_be/iob_ram_atdp_be.py | 225 -- .../hardware/simulation/src/iob_ram_sp_tb.v | 103 - .../memories/ram/iob_ram_sp/iob_ram_sp.py | 108 - .../simulation/src/iob_ram_sp_be_tb.v | 117 - .../ram/iob_ram_sp_be/iob_ram_sp_be.py | 138 - .../simulation/src/iob_ram_sp_se_tb.v | 119 - .../ram/iob_ram_sp_se/iob_ram_sp_se.py | 139 - .../hardware/simulation/src/iob_ram_t2p_tb.v | 119 - .../memories/ram/iob_ram_t2p/iob_ram_t2p.py | 131 - .../simulation/src/iob_ram_t2p_be_tb.v | 120 - .../ram/iob_ram_t2p_be/iob_ram_t2p_be.py | 166 - .../simulation/src/iob_ram_t2p_tiled_tb.v | 115 - .../iob_ram_t2p_tiled/iob_ram_t2p_tiled.py | 186 -- .../hardware/simulation/src/iob_ram_tdp_tb.v | 153 - .../memories/ram/iob_ram_tdp/iob_ram_tdp.py | 171 - .../simulation/src/iob_ram_tdp_be_tb.v | 183 - .../ram/iob_ram_tdp_be/iob_ram_tdp_be.py | 139 - .../simulation/src/iob_ram_tdp_be_xil_tb.v | 183 - .../iob_ram_tdp_be_xil/iob_ram_tdp_be_xil.py | 137 - .../hardware/src/iob_regfile_2p.v | 88 - .../regfile/iob_regfile_2p/iob_regfile_2p.py | 28 - .../hardware/src/iob_regfile_at2p.v | 75 - .../iob_regfile_at2p/iob_regfile_at2p.py | 22 - .../simulation/src/iob_regfile_sp_tb.v | 126 - .../hardware/src/iob_regfile_sp.v | 40 - .../regfile/iob_regfile_sp/iob_regfile_sp.py | 29 - .../hardware/simulation/src/iob_rom_2p_tb.v | 115 - .../rom/iob_rom_2p/hardware/src/iob_rom_2p.v | 47 - .../memories/rom/iob_rom_2p/iob_rom_2p.py | 18 - .../hardware/simulation/src/iob_rom_atdp_tb.v | 113 - .../memories/rom/iob_rom_atdp/iob_rom_atdp.py | 132 - .../hardware/simulation/src/iob_rom_sp_tb.v | 86 - .../memories/rom/iob_rom_sp/iob_rom_sp.py | 83 - .../hardware/simulation/src/iob_rom_tdp_tb.v | 107 - .../memories/rom/iob_rom_tdp/iob_rom_tdp.py | 133 - .../shifters/iob_pack/hardware/src/iob_pack.v | 137 - lib/hardware/shifters/iob_pack/iob_pack.py | 18 - .../shifters/iob_piso_reg/iob_piso_reg.py | 104 - .../simulation/src/iob_shift_reg_tb.v | 129 - .../hardware/src/iob_shift_reg.v | 91 - .../shifters/iob_shift_reg/iob_shift_reg.py | 38 - .../iob_sipo_reg/hardware/src/iob_sipo_reg.v | 29 - .../shifters/iob_sipo_reg/iob_sipo_reg.py | 22 - .../iob_unpack/hardware/src/iob_unpack.v | 137 - .../shifters/iob_unpack/iob_unpack.py | 18 - .../hardware/src/iob_f2s_1bit_sync.v | 29 - .../iob_f2s_1bit_sync/iob_f2s_1bit_sync.py | 18 - .../iob_neg2posedge_sync.py | 98 - .../iob_reset_sync/iob_reset_sync.py | 76 - .../synchronizers/iob_sync/iob_sync.py | 98 - lib/scripts/default.nix | 4 +- lib/scripts/test.sh | 49 - .../emb_utils/software/src/iob_emb_utils.c | 10 - .../emb_utils/software/src/iob_emb_utils.h | 7 - lib/software/modules/iob_str/iob_str.py | 13 - .../modules/iob_str/software/src/iob_str.c | 61 - .../modules/iob_str/software/src/iob_str.h | 10 - lib/software/modules/printf/printf.py | 13 - .../modules/printf/software/src/printf.c | 968 ------ .../modules/printf/software/src/printf.h | 125 - .../prng/xoshiro256plusplus/example/Makefile | 33 - .../example/example_xoshiro.c | 42 - .../iob_xoshiro256plusplus.c | 83 - .../iob_xoshiro256plusplus.h | 16 - 424 files changed, 2 insertions(+), 45141 deletions(-) delete mode 100644 lib/Makefile delete mode 100644 lib/README.md delete mode 100644 lib/hardware/altera/alt_iobuf/alt_iobuf.py delete mode 100644 lib/hardware/altera/altddio_in/altddio_in.py delete mode 100644 lib/hardware/altera/altddio_out/altddio_out.py delete mode 100644 lib/hardware/altera/altera_alt_ddr3/altera_alt_ddr3.py delete mode 100644 lib/hardware/altera/altera_clk_buf_altclkctrl/altera_clk_buf_altclkctrl.py delete mode 100644 lib/hardware/altera/altera_ddio_out_clkbuf/altera_ddio_out_clkbuf.py delete mode 100644 lib/hardware/amd/xilinx_axi_interconnect/xilinx_axi_interconnect.py delete mode 100644 lib/hardware/amd/xilinx_clock_wizard/hardware/src/clock_wizard.v delete mode 100644 lib/hardware/amd/xilinx_clock_wizard/xilinx_clock_wizard.py delete mode 100644 lib/hardware/amd/xilinx_ddr4_ctrl/hardware/src/ddr.xdc delete mode 100644 lib/hardware/amd/xilinx_ddr4_ctrl/xilinx_ddr4_ctrl.py delete mode 100644 lib/hardware/amd/xilinx_ibufg/xilinx_ibufg.py delete mode 100644 lib/hardware/amd/xilinx_oddre1/xilinx_oddre1.py delete mode 100644 lib/hardware/arith_logic/counter/iob_counter/iob_counter.py delete mode 100644 lib/hardware/arith_logic/counter/iob_counter_ld/iob_counter_ld.py delete mode 100644 lib/hardware/arith_logic/counter/iob_modcnt/iob_modcnt.py delete mode 100644 lib/hardware/arith_logic/div/iob_div_pipe/hardware/modules/iob_div_slice.py delete mode 100644 lib/hardware/arith_logic/div/iob_div_pipe/hardware/simulation/src/iob_div_pipe_tb.v delete mode 100755 lib/hardware/arith_logic/div/iob_div_pipe/iob_div_pipe.py delete mode 100644 lib/hardware/arith_logic/div/iob_div_subshift/hardware/simulation/src/iob_div_subshift_tb.v delete mode 100755 lib/hardware/arith_logic/div/iob_div_subshift/iob_div_subshift.py delete mode 100644 lib/hardware/arith_logic/div/iob_div_subshift_frac/hardware/simulation/src/iob_div_subshift_frac_tb.v delete mode 100755 lib/hardware/arith_logic/div/iob_div_subshift_frac/iob_div_subshift_frac.py delete mode 100644 lib/hardware/arith_logic/div/iob_div_subshift_signed/iob_div_subshift_signed.py delete mode 100644 lib/hardware/arith_logic/iob_add/hardware/src/iob_add.v delete mode 100644 lib/hardware/arith_logic/iob_add/iob_add.py delete mode 100644 lib/hardware/arith_logic/iob_add2/iob_add2.py delete mode 100644 lib/hardware/arith_logic/iob_ctls/hardware/simulation/src/iob_ctls_tb.v delete mode 100644 lib/hardware/arith_logic/iob_ctls/hardware/src/iob_ctls.v delete mode 100644 lib/hardware/arith_logic/iob_ctls/iob_ctls.py delete mode 100644 lib/hardware/arith_logic/iob_diff/hardware/src/iob_diff.v delete mode 100644 lib/hardware/arith_logic/iob_diff/iob_diff.py delete mode 100644 lib/hardware/arith_logic/iob_edge_detect/hardware/src/iob_edge_detect.v delete mode 100644 lib/hardware/arith_logic/iob_edge_detect/iob_edge_detect.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_add/hardware/src/iob_fp_add.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_add/iob_fp_add.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_clz/iob_fp_clz.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_cmp/iob_fp_cmp.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_div/hardware/src/iob_fp_div.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_div/iob_fp_div.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_dq/iob_fp_dq.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_float2int/hardware/src/iob_fp_float2int.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_float2int/iob_fp_float2int.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/hardware/src/iob_fp_float2uint.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/iob_fp_float2uint.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_fpu/hardware/src/iob_fp_fpu.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_fpu/iob_fp_fpu.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_int2float/hardware/src/iob_fp_int2float.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_int2float/iob_fp_int2float.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_minmax/iob_fp_minmax.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_mul/hardware/src/iob_fp_mul.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_mul/iob_fp_mul.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_round/iob_fp_round.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_special/iob_fp_special.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_sqrt/iob_fp_sqrt.py delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/hardware/src/iob_fp_uint2float.v delete mode 100755 lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/iob_fp_uint2float.py delete mode 100644 lib/hardware/arith_logic/iob_functions/hardware/src/iob_functions.vs delete mode 100644 lib/hardware/arith_logic/iob_functions/iob_functions.py delete mode 100644 lib/hardware/arith_logic/iob_int_sqrt/iob_int_sqrt.py delete mode 100644 lib/hardware/arith_logic/iob_prio_enc/hardware/simulation/src/iob_prio_enc_tb.v delete mode 100644 lib/hardware/arith_logic/iob_prio_enc/iob_prio_enc.py delete mode 100644 lib/hardware/arith_logic/iob_xor/iob_xor.py delete mode 100644 lib/hardware/buses/apb2iob/apb2iob.py delete mode 100644 lib/hardware/buses/apb2iob/document/ARM_AMBA3_APB.pdf delete mode 100644 lib/hardware/buses/apb2iob/document/ARM_AMBA3_APB.pdf.license delete mode 100644 lib/hardware/buses/apb2iob/document/ARM_AMBA3_APBv2_0.pdf delete mode 100644 lib/hardware/buses/apb2iob/document/ARM_AMBA3_APBv2_0.pdf.license delete mode 100644 lib/hardware/buses/apb2iob/hardware/src/apb2iob.v delete mode 100644 lib/hardware/buses/arbiter/arbiter.py delete mode 100644 lib/hardware/buses/arbiter/hardware/src/arbiter.v delete mode 100644 lib/hardware/buses/arbiter/hardware/src/priority_encoder.v delete mode 100644 lib/hardware/buses/axi2axil/axi2axil.py delete mode 100644 lib/hardware/buses/axi2iob/axi2iob.py delete mode 100644 lib/hardware/buses/axi2iob/hardware/src/axi2iob.v delete mode 100644 lib/hardware/buses/axi_interconnect/axi_interconnect.py delete mode 100644 lib/hardware/buses/axi_interconnect/hardware/src/axi_interconnect.v delete mode 100644 lib/hardware/buses/axi_interconnect_wrapper/axi_interconnect_wrapper.py delete mode 100644 lib/hardware/buses/axil2iob/axil2iob.py delete mode 100644 lib/hardware/buses/axil2iob/hardware/src/axil2iob.v delete mode 100644 lib/hardware/buses/axil_tasks/axil_tasks.vh delete mode 100644 lib/hardware/buses/axis2axi/axis2axi.py delete mode 100644 lib/hardware/buses/axis2axi/hardware/simulation/src/axidelay.v delete mode 100644 lib/hardware/buses/axis2axi/hardware/simulation/src/axis2axi_tb.v delete mode 100644 lib/hardware/buses/axis2axi/hardware/src/axis2axi.v delete mode 100644 lib/hardware/buses/axis2axi/submodules/axis2axi_in/axis2axi_in.py delete mode 100644 lib/hardware/buses/axis2axi/submodules/axis2axi_in/hardware/src/axis2axi_in.v delete mode 100644 lib/hardware/buses/axis2axi/submodules/axis2axi_out/axis2axi_out.py delete mode 100644 lib/hardware/buses/axis2axi/submodules/axis2axi_out/hardware/src/axis2axi_out.v delete mode 100644 lib/hardware/buses/axis2axi/waves.gtkw delete mode 100644 lib/hardware/buses/axis2axi/waves.gtkw.license delete mode 100644 lib/hardware/buses/axis2fifo/axis2fifo.py delete mode 100644 lib/hardware/buses/axis2fifo/hardware/src/axis2fifo.v delete mode 100644 lib/hardware/buses/axis2fifo/waves.gtkw delete mode 100644 lib/hardware/buses/axis2fifo/waves.gtkw.license delete mode 100644 lib/hardware/buses/axis_tasks/axis_tasks.py delete mode 100644 lib/hardware/buses/axis_tasks/hardware/src/axis_tasks.vs delete mode 100644 lib/hardware/buses/bus_width_converter/bus_width_converter.py delete mode 100644 lib/hardware/buses/fifo2axis/fifo2axis.py delete mode 100644 lib/hardware/buses/fifo2axis/hardware/src/fifo2axis.v delete mode 100644 lib/hardware/buses/fifo2axis/waves.gtkw delete mode 100644 lib/hardware/buses/fifo2axis/waves.gtkw.license delete mode 100644 lib/hardware/buses/iob2apb/iob2apb.py delete mode 100644 lib/hardware/buses/iob2axi/hardware/simulation/src/iob2axi_tb.disable.v delete mode 100644 lib/hardware/buses/iob2axi/hardware/src/iob2axi.v delete mode 100644 lib/hardware/buses/iob2axi/hardware/src/iob2axi_rd.v delete mode 100644 lib/hardware/buses/iob2axi/hardware/src/iob2axi_wr.v delete mode 100644 lib/hardware/buses/iob2axi/iob2axi.py delete mode 100644 lib/hardware/buses/iob2axi/waves.gtkw delete mode 100644 lib/hardware/buses/iob2axi/waves.gtkw.license delete mode 100644 lib/hardware/buses/iob2axil/hardware/src/iob2axil.v delete mode 100644 lib/hardware/buses/iob2axil/iob2axil.py delete mode 100644 lib/hardware/buses/iob_asym_converter/hardware/src/iob_asym_converter.v delete mode 100644 lib/hardware/buses/iob_asym_converter/iob_asym_converter.py delete mode 100644 lib/hardware/buses/iob_axil_split/iob_axil_split.py delete mode 100644 lib/hardware/buses/iob_axistream_in/hardware/src/iob_axistream_in.v delete mode 100755 lib/hardware/buses/iob_axistream_in/iob_axistream_in.py delete mode 100644 lib/hardware/buses/iob_axistream_in/software/linux/Readme.md delete mode 100644 lib/hardware/buses/iob_axistream_in/software/linux/drivers/driver.mk delete mode 100644 lib/hardware/buses/iob_axistream_in/software/linux/drivers/iob_axistream_in_main.c delete mode 100644 lib/hardware/buses/iob_axistream_in/software/linux/iob_axistream_in.dts delete mode 100644 lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.c delete mode 100644 lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.h delete mode 100644 lib/hardware/buses/iob_axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c delete mode 100644 lib/hardware/buses/iob_axistream_out/hardware/src/iob_axistream_out.v delete mode 100755 lib/hardware/buses/iob_axistream_out/iob_axistream_out.py delete mode 100644 lib/hardware/buses/iob_axistream_out/software/linux/Readme.md delete mode 100644 lib/hardware/buses/iob_axistream_out/software/linux/drivers/driver.mk delete mode 100644 lib/hardware/buses/iob_axistream_out/software/linux/drivers/iob_axistream_out_main.c delete mode 100644 lib/hardware/buses/iob_axistream_out/software/linux/iob_axistream_out.dts delete mode 100644 lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.c delete mode 100644 lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.h delete mode 100644 lib/hardware/buses/iob_axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c delete mode 100644 lib/hardware/buses/iob_bus_demux/hardware/src/iob_bus_demux.v delete mode 100644 lib/hardware/buses/iob_bus_demux/iob_bus_demux.py delete mode 100644 lib/hardware/buses/iob_demux/iob_demux.py delete mode 100644 lib/hardware/buses/iob_iob2wishbone/hardware/src/iob_iob2wishbone.v delete mode 100644 lib/hardware/buses/iob_iob2wishbone/iob_iob2wishbone.py delete mode 100644 lib/hardware/buses/iob_merge/iob_merge.py delete mode 100644 lib/hardware/buses/iob_mux/iob_mux.py delete mode 100644 lib/hardware/buses/iob_reverse/iob_reverse.py delete mode 100644 lib/hardware/buses/iob_split/iob_split.py delete mode 100644 lib/hardware/buses/iob_tasks/document/Makefile delete mode 100644 lib/hardware/buses/iob_tasks/document/README.md delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_read.drom delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_read.drom.license delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_read.png delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_read.png.license delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_write.drom delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_write.drom.license delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_write.png delete mode 100644 lib/hardware/buses/iob_tasks/document/iob_if_write.png.license delete mode 100644 lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.cpp delete mode 100644 lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.h delete mode 100644 lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.vs delete mode 100644 lib/hardware/buses/iob_tasks/iob_tasks.py delete mode 100644 lib/hardware/buses/iob_wishbone2iob/iob_wishbone2iob.py delete mode 100644 lib/hardware/clocks_resets/iob_clkbuf/iob_clkbuf.py delete mode 100644 lib/hardware/clocks_resets/iob_clkmux/iob_clkmux.py delete mode 100644 lib/hardware/clocks_resets/iob_clock/iob_clock.py delete mode 100644 lib/hardware/clocks_resets/iob_nco/hardware/iob_nco_sync.py delete mode 100644 lib/hardware/clocks_resets/iob_nco/hardware/simulation/src/iob_nco_tb.v delete mode 100644 lib/hardware/clocks_resets/iob_nco/iob_nco.py delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/example_firmware.c delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/linux/README.md delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/linux/drivers/driver.mk delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/linux/drivers/iob_nco_main.c delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/linux/iob_nco.dts delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/linux/user/Makefile delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/linux/user/iob_nco_user.c delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.c delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.h delete mode 100644 lib/hardware/clocks_resets/iob_nco/software/src/iob_nco_csrs_pc_emul.c delete mode 100644 lib/hardware/clocks_resets/iob_pulse_gen/hardware/simulation/src/iob_pulse_gen_tb.v delete mode 100644 lib/hardware/clocks_resets/iob_pulse_gen/iob_pulse_gen.py delete mode 100644 lib/hardware/fifo/iob_bfifo/hardware/src/iob_bfifo.v delete mode 100644 lib/hardware/fifo/iob_bfifo/iob_bfifo.py delete mode 100644 lib/hardware/fifo/iob_fifo_async/hardware/simulation/src/iob_fifo_async_tb.v delete mode 100644 lib/hardware/fifo/iob_fifo_async/hardware/src/iob_fifo_async.v delete mode 100644 lib/hardware/fifo/iob_fifo_async/iob_fifo_async.py delete mode 100644 lib/hardware/fifo/iob_fifo_sync/hardware/simulation/src/iob_fifo_sync_tb.v delete mode 100644 lib/hardware/fifo/iob_fifo_sync/hardware/src/iob_fifo_sync.v delete mode 100644 lib/hardware/fifo/iob_fifo_sync/iob_fifo_sync.py delete mode 100644 lib/hardware/fifo/iob_gray2bin/hardware/src/iob_gray2bin.v delete mode 100644 lib/hardware/fifo/iob_gray2bin/iob_gray2bin.py delete mode 100644 lib/hardware/fifo/iob_gray_counter/hardware/src/iob_gray_counter.v delete mode 100644 lib/hardware/fifo/iob_gray_counter/iob_gray_counter.py delete mode 100644 lib/hardware/iob_gpio/Makefile delete mode 100644 lib/hardware/iob_gpio/README.md delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/fpga.mk delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/quartus/Makefile delete mode 100755 lib/hardware/iob_gpio/hardware/fpga/quartus/build.sh delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.sdc delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.tcl delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.xdc delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/vivado/Makefile delete mode 100755 lib/hardware/iob_gpio/hardware/fpga/vivado/build.sh delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/vivado/gpio.tcl delete mode 100644 lib/hardware/iob_gpio/hardware/fpga/vivado/gpio.xdc delete mode 100644 lib/hardware/iob_gpio/hardware/include/inst.vh delete mode 100644 lib/hardware/iob_gpio/hardware/include/pio.vh delete mode 100644 lib/hardware/iob_gpio/hardware/simulation/icarus/Makefile delete mode 100644 lib/hardware/iob_gpio/hardware/simulation/simulation.mk delete mode 100755 lib/hardware/iob_gpio/iob_gpio.py delete mode 100644 lib/hardware/iob_gpio/software/linux/README.md delete mode 100644 lib/hardware/iob_gpio/software/linux/drivers/driver.mk delete mode 100644 lib/hardware/iob_gpio/software/linux/drivers/iob_gpio_main.c delete mode 100644 lib/hardware/iob_gpio/software/linux/iob_gpio.dts delete mode 100644 lib/hardware/iob_gpio/software/linux/user/Makefile delete mode 100644 lib/hardware/iob_gpio/software/linux/user/iob_gpio_user.c delete mode 100644 lib/hardware/iob_gpio/software/src/iob-gpio.c delete mode 100644 lib/hardware/iob_gpio/software/src/iob-gpio.h delete mode 100644 lib/hardware/iob_gpio/software/src/iob_gpio_swreg_pc_emul.c delete mode 100644 lib/hardware/iob_iobuf/iob_iobuf.py delete mode 100644 lib/hardware/iob_picorv32/hardware/src/iob_picorv32.v delete mode 100644 lib/hardware/iob_picorv32/hardware/src/picorv32.v delete mode 100644 lib/hardware/iob_picorv32/iob_picorv32.py delete mode 100644 lib/hardware/iob_regfileif/README.md delete mode 100644 lib/hardware/iob_regfileif/config.mk delete mode 100755 lib/hardware/iob_regfileif/hardware/hardware.mk delete mode 100644 lib/hardware/iob_regfileif/hardware/include/inst.vh delete mode 100644 lib/hardware/iob_regfileif/hardware/include/pio.vh delete mode 100755 lib/hardware/iob_regfileif/iob_regfileif.py delete mode 100644 lib/hardware/iob_regfileif/nativebridgeif_wrappper/hardware/src/iob_nativebridgeif.v delete mode 100755 lib/hardware/iob_regfileif/nativebridgeif_wrappper/iob_nativebridgeif_setup.py delete mode 100755 lib/hardware/iob_regfileif/software/python/iobnativebridge.py delete mode 100755 lib/hardware/iob_regfileif/software/python/mkregsregfileif.py delete mode 100644 lib/hardware/iob_system/README.md delete mode 100644 lib/hardware/iob_system/document/doc_build.mk delete mode 100644 lib/hardware/iob_system/document/figures/bd.odg delete mode 100644 lib/hardware/iob_system/document/figures/bd.odg.license delete mode 100644 lib/hardware/iob_system/document/figures/bd2.odg delete mode 100644 lib/hardware/iob_system/document/figures/bd2.odg.license delete mode 100644 lib/hardware/iob_system/document/figures/csr_gen.odg delete mode 100644 lib/hardware/iob_system/document/figures/csr_gen.odg.license delete mode 100644 lib/hardware/iob_system/document/figures/inst.odg delete mode 100644 lib/hardware/iob_system/document/figures/inst.odg.license delete mode 100644 lib/hardware/iob_system/document/figures/symb.odg delete mode 100644 lib/hardware/iob_system/document/figures/symb.odg.license delete mode 100644 lib/hardware/iob_system/document/figures/tbbd.odg delete mode 100644 lib/hardware/iob_system/document/figures/tbbd.odg.license delete mode 100644 lib/hardware/iob_system/document/tsrc/bd_desc.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/benefits.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/deliverables.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/features.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/intro.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/presentation.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/results.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/title.tex delete mode 100644 lib/hardware/iob_system/document/tsrc/ug_title.tex delete mode 100644 lib/hardware/iob_system/hardware/fpga/fpga_build.mk delete mode 100644 lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/board.tcl delete mode 100644 lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.py delete mode 100644 lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.sdc delete mode 100644 lib/hardware/iob_system/hardware/fpga/quartus/quartus.sdc delete mode 100644 lib/hardware/iob_system/hardware/fpga/quartus/quartus_postmap.tcl delete mode 100644 lib/hardware/iob_system/hardware/fpga/src/fpga.sdc delete mode 100644 lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.py delete mode 100644 lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.sdc delete mode 100644 lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.py delete mode 100644 lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.sdc delete mode 100644 lib/hardware/iob_system/hardware/fpga/vivado/vivado.sdc delete mode 100644 lib/hardware/iob_system/hardware/fpga/vivado/vivado_premap.tcl delete mode 100644 lib/hardware/iob_system/hardware/modules/iob_system_cache_system/iob_system_cache_system.py delete mode 100644 lib/hardware/iob_system/hardware/modules/iob_system_mwrap/iob_system_mwrap.py delete mode 100644 lib/hardware/iob_system/hardware/modules/iob_system_sim_wrapper/iob_system_sim_wrapper.py delete mode 100644 lib/hardware/iob_system/hardware/simulation/sim_build.mk delete mode 100644 lib/hardware/iob_system/hardware/simulation/src/.empty delete mode 100644 lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.cpp delete mode 100644 lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.v delete mode 100644 lib/hardware/iob_system/hardware/syn/genus/syn_build.tcl delete mode 100644 lib/hardware/iob_system/hardware/syn/src/bsp.vh delete mode 100755 lib/hardware/iob_system/iob_system.py delete mode 100644 lib/hardware/iob_system/scripts/iob_system_utils.py delete mode 100644 lib/hardware/iob_system/software/src/iob_system_firmware.S delete mode 100644 lib/hardware/iob_system/software/src/iob_system_firmware.c delete mode 100644 lib/hardware/iob_system/software/src/iob_system_firmware.lds delete mode 100644 lib/hardware/iob_system/software/src/iob_system_system.h delete mode 100644 lib/hardware/iob_system/software/sw_build.mk delete mode 100644 lib/hardware/iob_system/submodules/BOOTROM/iob_bootrom.py delete mode 100644 lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.S delete mode 100644 lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.c delete mode 100644 lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.lds delete mode 100644 lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.S delete mode 100644 lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.lds delete mode 160000 lib/hardware/iob_system/submodules/VEXRISCV delete mode 100644 lib/hardware/iob_timer/hardware/simulation/src/timer_tb.v delete mode 100644 lib/hardware/iob_timer/hardware/src/timer_core.v delete mode 100644 lib/hardware/iob_timer/hardware/timer_core.py delete mode 100755 lib/hardware/iob_timer/iob_timer.py delete mode 100644 lib/hardware/iob_timer/software/example_firmware.c delete mode 100644 lib/hardware/iob_timer/software/linux/README.md delete mode 100644 lib/hardware/iob_timer/software/linux/drivers/driver.mk delete mode 100644 lib/hardware/iob_timer/software/linux/drivers/iob_timer_main.c delete mode 100644 lib/hardware/iob_timer/software/linux/iob_timer.dts delete mode 100644 lib/hardware/iob_timer/software/linux/user/Makefile delete mode 100644 lib/hardware/iob_timer/software/linux/user/iob_timer_user.c delete mode 100644 lib/hardware/iob_timer/software/src/iob_timer.c delete mode 100644 lib/hardware/iob_timer/software/src/iob_timer.h delete mode 100644 lib/hardware/iob_timer/software/src/iob_timer_csrs_pc_emul.c delete mode 100644 lib/hardware/iob_uart/LICENSE delete mode 100644 lib/hardware/iob_uart/Makefile delete mode 100644 lib/hardware/iob_uart/README.md delete mode 100644 lib/hardware/iob_uart/config_setup.mk delete mode 120000 lib/hardware/iob_uart/default.nix delete mode 100644 lib/hardware/iob_uart/document/figures/bd.odg delete mode 100644 lib/hardware/iob_uart/document/figures/bd.odg.license delete mode 100644 lib/hardware/iob_uart/document/figures/inst.odg delete mode 100644 lib/hardware/iob_uart/document/figures/inst.odg.license delete mode 100644 lib/hardware/iob_uart/document/figures/symb.odg delete mode 100644 lib/hardware/iob_uart/document/figures/symb.odg.license delete mode 100644 lib/hardware/iob_uart/document/figures/tbbd.odg delete mode 100644 lib/hardware/iob_uart/document/figures/tbbd.odg.license delete mode 100644 lib/hardware/iob_uart/document/pb/Makefile delete mode 100644 lib/hardware/iob_uart/document/pb/pb.pdf delete mode 100644 lib/hardware/iob_uart/document/pb/pb.pdf.license delete mode 100644 lib/hardware/iob_uart/document/pb/test.expected delete mode 100644 lib/hardware/iob_uart/document/pb/test.expected.license delete mode 100644 lib/hardware/iob_uart/document/tsrc/bd_desc.tex delete mode 100644 lib/hardware/iob_uart/document/tsrc/benefits.tex delete mode 100644 lib/hardware/iob_uart/document/tsrc/deliverables.tex delete mode 100644 lib/hardware/iob_uart/document/tsrc/features.tex delete mode 100644 lib/hardware/iob_uart/document/tsrc/intro.tex delete mode 100644 lib/hardware/iob_uart/document/tsrc/results.tex delete mode 100644 lib/hardware/iob_uart/document/tsrc/title.tex delete mode 100644 lib/hardware/iob_uart/document/tsrc/ug_title.tex delete mode 100644 lib/hardware/iob_uart/document/ug/Makefile delete mode 100644 lib/hardware/iob_uart/document/ug/csrs.tex delete mode 100644 lib/hardware/iob_uart/document/ug/if.tex delete mode 100644 lib/hardware/iob_uart/document/ug/inst.tex delete mode 100644 lib/hardware/iob_uart/document/ug/revhist.tex delete mode 100644 lib/hardware/iob_uart/document/ug/sim.tex delete mode 100644 lib/hardware/iob_uart/document/ug/synth.tex delete mode 100644 lib/hardware/iob_uart/document/ug/test.expected delete mode 100644 lib/hardware/iob_uart/document/ug/test.expected.license delete mode 100644 lib/hardware/iob_uart/document/ug/ug.pdf delete mode 100644 lib/hardware/iob_uart/document/ug/ug.pdf.license delete mode 100644 lib/hardware/iob_uart/hardware/simulation/sim_build.mk delete mode 100644 lib/hardware/iob_uart/hardware/simulation/src/iob_uart_tb.v delete mode 100644 lib/hardware/iob_uart/hardware/simulation/test.expected delete mode 100644 lib/hardware/iob_uart/hardware/simulation/test.expected.license delete mode 100644 lib/hardware/iob_uart/hardware/src/uart_core.v delete mode 100644 lib/hardware/iob_uart/hardware/uart_core.py delete mode 100755 lib/hardware/iob_uart/iob_uart.py delete mode 100644 lib/hardware/iob_uart/software/src/iob_uart.c delete mode 100644 lib/hardware/iob_uart/software/src/iob_uart.h delete mode 100644 lib/hardware/iob_uart/software/src/iob_uart_csrs_pc_emul.c delete mode 100644 lib/hardware/memories/axi_ram/axi_ram.py delete mode 100644 lib/hardware/memories/axi_ram/hardware/src/axi_ram.v delete mode 100644 lib/hardware/memories/ram/iob_ram_2p/hardware/simulation/src/iob_ram_2p_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_2p/iob_ram_2p.py delete mode 100644 lib/hardware/memories/ram/iob_ram_at2p/hardware/simulation/src/iob_ram_at2p_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_at2p/iob_ram_at2p.py delete mode 100644 lib/hardware/memories/ram/iob_ram_atdp/hardware/simulation/src/iob_ram_atdp_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_atdp/iob_ram_atdp.py delete mode 100644 lib/hardware/memories/ram/iob_ram_atdp_be/hardware/simulation/src/iob_ram_atdp_be_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_atdp_be/iob_ram_atdp_be.py delete mode 100644 lib/hardware/memories/ram/iob_ram_sp/hardware/simulation/src/iob_ram_sp_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_sp/iob_ram_sp.py delete mode 100644 lib/hardware/memories/ram/iob_ram_sp_be/hardware/simulation/src/iob_ram_sp_be_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_sp_be/iob_ram_sp_be.py delete mode 100644 lib/hardware/memories/ram/iob_ram_sp_se/hardware/simulation/src/iob_ram_sp_se_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_sp_se/iob_ram_sp_se.py delete mode 100644 lib/hardware/memories/ram/iob_ram_t2p/hardware/simulation/src/iob_ram_t2p_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_t2p/iob_ram_t2p.py delete mode 100644 lib/hardware/memories/ram/iob_ram_t2p_be/hardware/simulation/src/iob_ram_t2p_be_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_t2p_be/iob_ram_t2p_be.py delete mode 100644 lib/hardware/memories/ram/iob_ram_t2p_tiled/hardware/simulation/src/iob_ram_t2p_tiled_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_t2p_tiled/iob_ram_t2p_tiled.py delete mode 100644 lib/hardware/memories/ram/iob_ram_tdp/hardware/simulation/src/iob_ram_tdp_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_tdp/iob_ram_tdp.py delete mode 100644 lib/hardware/memories/ram/iob_ram_tdp_be/hardware/simulation/src/iob_ram_tdp_be_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_tdp_be/iob_ram_tdp_be.py delete mode 100644 lib/hardware/memories/ram/iob_ram_tdp_be_xil/hardware/simulation/src/iob_ram_tdp_be_xil_tb.v delete mode 100644 lib/hardware/memories/ram/iob_ram_tdp_be_xil/iob_ram_tdp_be_xil.py delete mode 100644 lib/hardware/memories/regfile/iob_regfile_2p/hardware/src/iob_regfile_2p.v delete mode 100644 lib/hardware/memories/regfile/iob_regfile_2p/iob_regfile_2p.py delete mode 100644 lib/hardware/memories/regfile/iob_regfile_at2p/hardware/src/iob_regfile_at2p.v delete mode 100644 lib/hardware/memories/regfile/iob_regfile_at2p/iob_regfile_at2p.py delete mode 100644 lib/hardware/memories/regfile/iob_regfile_sp/hardware/simulation/src/iob_regfile_sp_tb.v delete mode 100644 lib/hardware/memories/regfile/iob_regfile_sp/hardware/src/iob_regfile_sp.v delete mode 100644 lib/hardware/memories/regfile/iob_regfile_sp/iob_regfile_sp.py delete mode 100644 lib/hardware/memories/rom/iob_rom_2p/hardware/simulation/src/iob_rom_2p_tb.v delete mode 100644 lib/hardware/memories/rom/iob_rom_2p/hardware/src/iob_rom_2p.v delete mode 100644 lib/hardware/memories/rom/iob_rom_2p/iob_rom_2p.py delete mode 100644 lib/hardware/memories/rom/iob_rom_atdp/hardware/simulation/src/iob_rom_atdp_tb.v delete mode 100644 lib/hardware/memories/rom/iob_rom_atdp/iob_rom_atdp.py delete mode 100644 lib/hardware/memories/rom/iob_rom_sp/hardware/simulation/src/iob_rom_sp_tb.v delete mode 100644 lib/hardware/memories/rom/iob_rom_sp/iob_rom_sp.py delete mode 100644 lib/hardware/memories/rom/iob_rom_tdp/hardware/simulation/src/iob_rom_tdp_tb.v delete mode 100644 lib/hardware/memories/rom/iob_rom_tdp/iob_rom_tdp.py delete mode 100644 lib/hardware/shifters/iob_pack/hardware/src/iob_pack.v delete mode 100644 lib/hardware/shifters/iob_pack/iob_pack.py delete mode 100644 lib/hardware/shifters/iob_piso_reg/iob_piso_reg.py delete mode 100644 lib/hardware/shifters/iob_shift_reg/hardware/simulation/src/iob_shift_reg_tb.v delete mode 100644 lib/hardware/shifters/iob_shift_reg/hardware/src/iob_shift_reg.v delete mode 100644 lib/hardware/shifters/iob_shift_reg/iob_shift_reg.py delete mode 100644 lib/hardware/shifters/iob_sipo_reg/hardware/src/iob_sipo_reg.v delete mode 100644 lib/hardware/shifters/iob_sipo_reg/iob_sipo_reg.py delete mode 100644 lib/hardware/shifters/iob_unpack/hardware/src/iob_unpack.v delete mode 100644 lib/hardware/shifters/iob_unpack/iob_unpack.py delete mode 100644 lib/hardware/synchronizers/iob_f2s_1bit_sync/hardware/src/iob_f2s_1bit_sync.v delete mode 100644 lib/hardware/synchronizers/iob_f2s_1bit_sync/iob_f2s_1bit_sync.py delete mode 100644 lib/hardware/synchronizers/iob_neg2posedge_sync/iob_neg2posedge_sync.py delete mode 100644 lib/hardware/synchronizers/iob_reset_sync/iob_reset_sync.py delete mode 100644 lib/hardware/synchronizers/iob_sync/iob_sync.py delete mode 100755 lib/scripts/test.sh delete mode 100644 lib/software/modules/emb_utils/software/src/iob_emb_utils.c delete mode 100644 lib/software/modules/emb_utils/software/src/iob_emb_utils.h delete mode 100644 lib/software/modules/iob_str/iob_str.py delete mode 100644 lib/software/modules/iob_str/software/src/iob_str.c delete mode 100644 lib/software/modules/iob_str/software/src/iob_str.h delete mode 100644 lib/software/modules/printf/printf.py delete mode 100644 lib/software/modules/printf/software/src/printf.c delete mode 100644 lib/software/modules/printf/software/src/printf.h delete mode 100644 lib/software/modules/prng/xoshiro256plusplus/example/Makefile delete mode 100644 lib/software/modules/prng/xoshiro256plusplus/example/example_xoshiro.c delete mode 100644 lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.c delete mode 100644 lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca400712b..3b4ed4532 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,21 +88,6 @@ jobs: - name: no init mem and ext mem run: make fpga-run BOARD=aes_ku040_db_g INIT_MEM=0 USE_EXTMEM=1 - lib: - runs-on: self-hosted - timeout-minutes: 20 - if: ${{ !cancelled() }} - needs: [ cyclonev ] - - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Clean untracked files and directories (like old submodules) - run: git clean -ffdx - - name: run LIB test - run: nix-shell --run "make -C lib sim-test" - doc: runs-on: self-hosted timeout-minutes: 60 diff --git a/.gitmodules b/.gitmodules index 239d11589..93e6adf10 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,3 @@ # # SPDX-License-Identifier: MIT -[submodule "submodules/VEXRISCV"] - path = lib/hardware/iob_system/submodules/VEXRISCV - url = git@github.com:IObundle/iob-vexriscv.git diff --git a/lib/Makefile b/lib/Makefile deleted file mode 100644 index 272fec78b..000000000 --- a/lib/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# (c) 2022-Present IObundle, Lda, all rights reserved -# -# This makefile simulates the hardware modules in this repo -# - - -# Used by test.sh -LIB_DIR:=. -export LIB_DIR - -PROJECT_ROOT=.. - -# Default lib module to setup. Can be overriden by the user. -CORE ?=iob_ctls - - -all: sim-test - -setup: - nix-shell --run "py2hwsw $(CORE) setup --project_root $(PROJECT_ROOT) --build_dir '$(BUILD_DIR)' --no_verilog_lint" - -sim-build: - nix-shell --run "scripts/test.sh build $(CORE)" - -sim-run: - nix-shell --run "VCD=$(VCD) scripts/test.sh $(CORE)" - -sim-test: - nix-shell --run "scripts/test.sh test" - -sim-clean: - nix-shell --run "scripts/test.sh clean" - -print-attr: - nix-shell --run "VCD=$(VCD) SETUP_ARGS='print_attr' scripts/test.sh $(CORE)" - - -.PHONY: all setup sim-build sim-run sim-test sim-clean - - -# Install board server and client -board_server_install: - sudo cp scripts/board_client.py /usr/local/bin/ && \ - sudo cp scripts/board_server.py /usr/local/bin/ && \ - sudo cp scripts/board_server.service /etc/systemd/system/ && \ - sudo systemctl daemon-reload && \ - sudo systemctl enable board_server && \ - sudo systemctl restart board_server - -board_server_uninstall: - sudo systemctl stop board_server && \ - sudo systemctl disable board_server && \ - sudo rm /usr/local/bin/board_client.py && \ - sudo rm /usr/local/bin/board_server.py && \ - sudo rm /etc/systemd/system/board_server.service && \ - sudo systemctl daemon-reload - -board_server_status: - sudo systemctl status board_server - -.PHONY: board_server_install board_server_uninstall board_server_status - - -clean: - nix-shell --run "py2hwsw $(CORE) clean --build_dir '$(BUILD_DIR)'" - @rm -rf ../*.summary ../*.rpt - @find . -name \*~ -delete - -.PHONY: clean diff --git a/lib/README.md b/lib/README.md deleted file mode 100644 index acfd523ba..000000000 --- a/lib/README.md +++ /dev/null @@ -1,65 +0,0 @@ - - -# iob-lib -This repository contains a set of Python scripts, Verilog, and C sources to -simplify the development of subsystem IP cores. - -It is used as a submodule in the [IOb-SoC](https://github.com/IObundle/iob-soc) -RISC-V-based SoC, and associated projects. - -## Code Style -#### Python Code -[![Recommended python code style: -black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -- Python format workflow: - - install [black](https://black.readthedocs.io/en/stable/) - - run black manually: - - `make python-format` or `./scripts/sw_format.py black` - - (optional): [integrate with your preferred - IDE](https://black.readthedocs.io/en/stable/integrations/editors.html) - - black formatting for other repositories: - - call `sw_format.py black` script in LIB submodule from the repository - top level: - ```make - # repository top level Makefile - format: - @./lib/scripts/sw_format.py black - ``` -#### C/C++ Code -- Recommended C/C++ code style: [LLVM](https://llvm.org/docs/CodingStandards.html) -- C/C++ format workflow: - - install [clang-format](https://black.readthedocs.io/en/stable/) - - run clang-format manually: - - `make c-format` or `./scripts/sw_format.py clang` - - (optional) [integrate with your preferred - IDE](https://clang.llvm.org/docs/ClangFormat.html#vim-integration) - - C/C++ formatting for other repositories: - - copy `.clang-format` to new repository top level - - call `sw_format.py clang` script in LIB submodule from the repository - top level: - ```make - # repository top level Makefile - format: - @./lib/scripts/sw_format.py clang - ``` - -## Tests -Currently tests are automated for the memory modules in the `test.mk` makefile. -Run tests for all memory modules with the command: -``` -make -f test.mk test -``` - -## How to use Nix -Instead of manually installing the dependencies for each IObundle repository project, you can use [nix-shell](https://nixos.org/manual/nix/stable/command-ref/nix-shell.html). This allows you to run your project in a [Nix](https://nixos.org/) environment with all dependencies available except for Vivado and Quartus. The packages installed by the `nix-shell` are defined in [`scripts/default.nix`](https://github.com/IObundle/iob-lib/blob/python-setup/scripts/default.nix). - -To [install Nix](https://nixos.org/download.html#nix-install-linux) the recommended command is: -- `sh <(curl -L https://nixos.org/nix/install) --daemon` - -Then, in the repository you can run `nix-shell` from the root directory to install the required packages. - -To delete all the unused old Nix packages you can run: `nix-collect-garbage -d`. It is recommended to run it inside the nix-shell environment currently used to prevent deleting packages commonly used and having to install them again. diff --git a/lib/hardware/altera/alt_iobuf/alt_iobuf.py b/lib/hardware/altera/alt_iobuf/alt_iobuf.py deleted file mode 100644 index cf6261ac4..000000000 --- a/lib/hardware/altera/alt_iobuf/alt_iobuf.py +++ /dev/null @@ -1,65 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "ports": [ - { - "name": "in_i", - "descr": "Input port", - "signals": [ - { - "name": "in", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "en_i", - "descr": "Input port", - "signals": [ - { - "name": "en", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "out_o", - "descr": "Output port", - "signals": [ - { - "name": "out", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "in_out_io", - "descr": "In/Output port", - "signals": [ - { - "name": "in_out", - "width": 1, - "direction": "inout", - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": f""" - assign in_out_io = en_i ? in_i : 1'bz; - assign out_o = in_out_io; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/altera/altddio_in/altddio_in.py b/lib/hardware/altera/altddio_in/altddio_in.py deleted file mode 100644 index 99a1bbe90..000000000 --- a/lib/hardware/altera/altddio_in/altddio_in.py +++ /dev/null @@ -1,77 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "data_i", - "descr": "Input port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_l_o", - "descr": "Output port", - "signals": [ - { - "name": "data_l", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - { - "name": "data_h_o", - "descr": "Output port", - "signals": [ - { - "name": "data_h", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": f""" - always @(posedge clk_i) - data_h_o <= data_i; - always @(negedge clk_i) - data_l_o <= data_i; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/altera/altddio_out/altddio_out.py b/lib/hardware/altera/altddio_out/altddio_out.py deleted file mode 100644 index 40d23dd4f..000000000 --- a/lib/hardware/altera/altddio_out/altddio_out.py +++ /dev/null @@ -1,94 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "1", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "data_l_i", - "descr": "Input port", - "signals": [ - { - "name": "data_l", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_h_i", - "descr": "Input port", - "signals": [ - { - "name": "data_h", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "data_l_i_reg", - "descr": "data_l_i_reg wire", - "signals": [ - {"name": "data_l_i_reg", "width": "DATA_W"}, - ], - }, - { - "name": "data_h_i_reg", - "descr": "data_h_i_reg wire", - "signals": [ - {"name": "data_h_i_reg", "width": "DATA_W"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - always @(posedge clk_i) - data_h_i_reg <= data_h_i; - always @(negedge clk_i) - data_l_i_reg <= data_l_i; - assign data_o = clk_i ? data_h_i_reg : data_l_i_reg; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/altera/altera_alt_ddr3/altera_alt_ddr3.py b/lib/hardware/altera/altera_alt_ddr3/altera_alt_ddr3.py deleted file mode 100644 index cf55fe7b3..000000000 --- a/lib/hardware/altera/altera_alt_ddr3/altera_alt_ddr3.py +++ /dev/null @@ -1,206 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - # Number of slave interfaces (number of masters to connect to) - N_SLAVES = ( - int(py_params_dict["num_slaves"]) if "num_slaves" in py_params_dict else 1 - ) - - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "AXI_ID_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI ID bus width", - }, - { - "name": "AXI_LEN_W", - "type": "P", - "val": "0", - "min": "1", - "max": "8", - "descr": "AXI burst length width", - }, - { - "name": "AXI_ADDR_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI address bus width", - }, - { - "name": "AXI_DATA_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI data bus width", - }, - ], - "ports": [ - { - "name": "clk_rst_i", - "descr": "Clock and reset", - "signals": [ - {"name": "clk", "direction": "input", "width": "1"}, - {"name": "resetn", "direction": "input", "width": "1"}, - ], - }, - { - "name": "general", - "descr": "", - "signals": [ - {"name": "rzqin", "direction": "input", "width": "1"}, - {"name": "pll_locked", "direction": "output", "width": "1"}, - {"name": "init_done", "direction": "output", "width": "1"}, - ], - }, - { - "name": "ddr3", - "descr": "External DDR3 memory interface", - "signals": [ - {"name": "ddr3b_a", "direction": "output", "width": "14"}, - {"name": "ddr3b_ba", "direction": "output", "width": "3"}, - {"name": "ddr3b_rasn", "direction": "output", "width": "1"}, - {"name": "ddr3b_casn", "direction": "output", "width": "1"}, - {"name": "ddr3b_wen", "direction": "output", "width": "1"}, - {"name": "ddr3b_dm", "direction": "output", "width": "2"}, - {"name": "ddr3b_dq", "direction": "inout", "width": "16"}, - {"name": "ddr3b_clk_n", "direction": "output", "width": "1"}, - {"name": "ddr3b_clk_p", "direction": "output", "width": "1"}, - {"name": "ddr3b_cke", "direction": "output", "width": "1"}, - {"name": "ddr3b_csn", "direction": "output", "width": "1"}, - {"name": "ddr3b_dqs_n", "direction": "inout", "width": "2"}, - {"name": "ddr3b_dqs_p", "direction": "inout", "width": "2"}, - {"name": "ddr3b_odt", "direction": "output", "width": "1"}, - {"name": "ddr3b_resetn", "direction": "output", "width": "1"}, - ], - }, - ], - } - for i in range(N_SLAVES): - attributes_dict["ports"] += [ - { - "name": f"s{i}_axi_s", - "interface": { - "type": "axi", - "subtype": "slave", - "port_prefix": f"s{i}_", - "ID_W": "AXI_ID_W", - "LEN_W": "AXI_LEN_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - }, - "descr": f"Slave {i} interface", - }, - ] - attributes_dict["snippets"] = [ - { - "verilog_code": """ - alt_ddr3 ddr3_ctrl ( - .clk_clk (clk_i), - .reset_reset_n(resetn_i), - .oct_rzqin (rzqin_i), - - .memory_mem_a (ddr3b_a_o), - .memory_mem_ba (ddr3b_ba_o), - .memory_mem_ck (ddr3b_clk_p_o), - .memory_mem_ck_n (ddr3b_clk_n_o), - .memory_mem_cke (ddr3b_cke_o), - .memory_mem_cs_n (ddr3b_csn_o), - .memory_mem_dm (ddr3b_dm_o), - .memory_mem_ras_n (ddr3b_rasn_o), - .memory_mem_cas_n (ddr3b_casn_o), - .memory_mem_we_n (ddr3b_wen_o), - .memory_mem_reset_n(ddr3b_resetn_o), - .memory_mem_dq (ddr3b_dq_io), - .memory_mem_dqs (ddr3b_dqs_p_io), - .memory_mem_dqs_n (ddr3b_dqs_n_io), - .memory_mem_odt (ddr3b_odt_o), - -""", - }, - ] - - for i in range(N_SLAVES): - attributes_dict["snippets"][-1][ - "verilog_code" - ] += f""" - // - // External memory connection {i} - // - - //Write address - .axi_bridge_0_s{i}_awid (s{i}_axi_awid_i), - .axi_bridge_0_s{i}_awaddr (s{i}_axi_awaddr_i), - .axi_bridge_0_s{i}_awlen (s{i}_axi_awlen_i), - .axi_bridge_0_s{i}_awsize (s{i}_axi_awsize_i), - .axi_bridge_0_s{i}_awburst(s{i}_axi_awburst_i), - .axi_bridge_0_s{i}_awlock (s{i}_axi_awlock_i), - .axi_bridge_0_s{i}_awcache(s{i}_axi_awcache_i), - .axi_bridge_0_s{i}_awprot (s{i}_axi_awprot_i), - .axi_bridge_0_s{i}_awvalid(s{i}_axi_awvalid_i), - .axi_bridge_0_s{i}_awready(s{i}_axi_awready_o), - - //Write data - .axi_bridge_0_s{i}_wdata (s{i}_axi_wdata_i), - .axi_bridge_0_s{i}_wstrb (s{i}_axi_wstrb_i), - .axi_bridge_0_s{i}_wlast (s{i}_axi_wlast_i), - .axi_bridge_0_s{i}_wvalid (s{i}_axi_wvalid_i), - .axi_bridge_0_s{i}_wready (s{i}_axi_wready_o), - - //Write respons{i} - .axi_bridge_0_s{i}_bid (s{i}_axi_bid_o), - .axi_bridge_0_s{i}_bresp (s{i}_axi_bresp_o), - .axi_bridge_0_s{i}_bvalid (s{i}_axi_bvalid_o), - .axi_bridge_0_s{i}_bready (s{i}_axi_bready_i), - - //Read address - .axi_bridge_0_s{i}_arid (s{i}_axi_arid_i), - .axi_bridge_0_s{i}_araddr (s{i}_axi_araddr_i), - .axi_bridge_0_s{i}_arlen (s{i}_axi_arlen_i), - .axi_bridge_0_s{i}_arsize (s{i}_axi_arsize_i), - .axi_bridge_0_s{i}_arburst(s{i}_axi_arburst_i), - .axi_bridge_0_s{i}_arlock (s{i}_axi_arlock_i), - .axi_bridge_0_s{i}_arcache(s{i}_axi_arcache_i), - .axi_bridge_0_s{i}_arprot (s{i}_axi_arprot_i), - .axi_bridge_0_s{i}_arvalid(s{i}_axi_arvalid_i), - .axi_bridge_0_s{i}_arready(s{i}_axi_arready_o), - - //Read data - .axi_bridge_0_s{i}_rid (s{i}_axi_rid_o), - .axi_bridge_0_s{i}_rdata (s{i}_axi_rdata_o), - .axi_bridge_0_s{i}_rresp (s{i}_axi_rresp_o), - .axi_bridge_0_s{i}_rlast (s{i}_axi_rlast_o), - .axi_bridge_0_s{i}_rvalid (s{i}_axi_rvalid_o), - .axi_bridge_0_s{i}_rready (s{i}_axi_rready_i), - -""" - attributes_dict["snippets"][-1][ - "verilog_code" - ] += """ - .mem_if_ddr3_emif_0_pll_sharing_pll_mem_clk (), - .mem_if_ddr3_emif_0_pll_sharing_pll_write_clk (), - .mem_if_ddr3_emif_0_pll_sharing_pll_locked (pll_locked_o), - .mem_if_ddr3_emif_0_pll_sharing_pll_write_clk_pre_phy_clk(), - .mem_if_ddr3_emif_0_pll_sharing_pll_addr_cmd_clk (), - .mem_if_ddr3_emif_0_pll_sharing_pll_avl_clk (), - .mem_if_ddr3_emif_0_pll_sharing_pll_config_clk (), - .mem_if_ddr3_emif_0_pll_sharing_pll_mem_phy_clk (), - .mem_if_ddr3_emif_0_pll_sharing_afi_phy_clk (), - .mem_if_ddr3_emif_0_pll_sharing_pll_avl_phy_clk (), - .mem_if_ddr3_emif_0_status_local_init_done (init_done_o), - .mem_if_ddr3_emif_0_status_local_cal_success (), - .mem_if_ddr3_emif_0_status_local_cal_fail () - ); -""" - - return attributes_dict diff --git a/lib/hardware/altera/altera_clk_buf_altclkctrl/altera_clk_buf_altclkctrl.py b/lib/hardware/altera/altera_clk_buf_altclkctrl/altera_clk_buf_altclkctrl.py deleted file mode 100644 index d56820102..000000000 --- a/lib/hardware/altera/altera_clk_buf_altclkctrl/altera_clk_buf_altclkctrl.py +++ /dev/null @@ -1,31 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "ports": [ - { - "name": "io", - "descr": "", - "signals": [ - {"name": "clkin", "direction": "input", "width": "1"}, - {"name": "clkout", "direction": "output", "width": "1"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - clk_buf_altclkctrl_0 clk_buf ( - .inclk (clkin_i), - .outclk(clkout_o) - ); -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/altera/altera_ddio_out_clkbuf/altera_ddio_out_clkbuf.py b/lib/hardware/altera/altera_ddio_out_clkbuf/altera_ddio_out_clkbuf.py deleted file mode 100644 index 6a2e0e4da..000000000 --- a/lib/hardware/altera/altera_ddio_out_clkbuf/altera_ddio_out_clkbuf.py +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "ports": [ - { - "name": "io", - "descr": "", - "signals": [ - {"name": "aclr", "direction": "input", "width": "1"}, - {"name": "data_h", "direction": "input", "width": "1"}, - {"name": "data_l", "direction": "input", "width": "1"}, - {"name": "clk", "direction": "input", "width": "1"}, - {"name": "data", "direction": "output", "width": "1"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - ddio_out_clkbuf ddio_out_clkbuf_inst ( - .aclr (aclr_i), - .datain_h(data_h_i), - .datain_l(data_l_i), - .outclock(clk_i), - .dataout (data_o) - ); -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/amd/xilinx_axi_interconnect/xilinx_axi_interconnect.py b/lib/hardware/amd/xilinx_axi_interconnect/xilinx_axi_interconnect.py deleted file mode 100644 index 43a8bd84e..000000000 --- a/lib/hardware/amd/xilinx_axi_interconnect/xilinx_axi_interconnect.py +++ /dev/null @@ -1,253 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - # Number of slave interfaces (number of masters to connect to) - N_SLAVES = ( - int(py_params_dict["num_slaves"]) if "num_slaves" in py_params_dict else 1 - ) - # Number of master interfaces (number of slaves to connect to) - N_MASTERS = ( - int(py_params_dict["num_masters"]) if "num_masters" in py_params_dict else 1 - ) - - attributes_dict = { - "version": "0.1", - # - # AXI Parameters - # - "confs": [ - { - "name": "AXI_ID_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI ID bus width", - }, - { - "name": "AXI_LEN_W", - "type": "P", - "val": "0", - "min": "1", - "max": "8", - "descr": "AXI burst length width", - }, - { - "name": "AXI_ADDR_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI address bus width", - }, - { - "name": "AXI_DATA_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI data bus width", - }, - ], - # - # Ports - # - "ports": [ - { - "name": "clk_rst_s", - "descr": "Clock and reset inputs", - "interface": { - "type": "clk_rst", - "subtype": "slave", - }, - }, - ], - } - for i in range(N_SLAVES): - attributes_dict["ports"] += [ - { - "name": f"s{i}_clk_rst", - "descr": f"Slave {i} clock reset interface", - "signals": [ - {"name": f"s{i}_clk", "direction": "input", "width": "1"}, - {"name": f"s{i}_arstn", "direction": "output", "width": "1"}, - ], - }, - { - "name": f"s{i}_axi_s", - "interface": { - "type": "axi", - "subtype": "slave", - "port_prefix": f"s{i}_", - "ID_W": "AXI_ID_W", - "LEN_W": "AXI_LEN_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - }, - "descr": f"Slave {i} interface", - }, - ] - for i in range(N_MASTERS): - attributes_dict["ports"] += [ - { - "name": f"m{i}_clk_rst", - "descr": f"Master {i} clock reset output interface", - "signals": [ - {"name": f"m{i}_clk", "direction": "input", "width": "1"}, - {"name": f"m{i}_arstn", "direction": "output", "width": "1"}, - ], - }, - { - "name": f"m{i}_axi_m", - "interface": { - "type": "axi", - "subtype": "master", - "port_prefix": f"m{i}_", - "ID_W": "AXI_ID_W", - "LEN_W": "AXI_LEN_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1, - }, - "descr": f"Master {i} axi interface", - }, - ] - # - # Snippets - # - attributes_dict["snippets"] = [ - { - "verilog_code": """ - axi_interconnect_0 axi_interconnect_inst ( -""", - }, - ] - - for i in range(N_SLAVES): - attributes_dict["snippets"][-1][ - "verilog_code" - ] += f""" - // - // Slave interface {i} - // - .S{i:02d}_AXI_ARESET_OUT_N(s{i}_arstn_o), - .S{i:02d}_AXI_ACLK (s{i}_clk_i), - - //Write address - .S{i:02d}_AXI_AWID (s{i}_axi_awid_i[0]), - .S{i:02d}_AXI_AWADDR (s{i}_axi_awaddr_i), - .S{i:02d}_AXI_AWLEN (s{i}_axi_awlen_i), - .S{i:02d}_AXI_AWSIZE (s{i}_axi_awsize_i), - .S{i:02d}_AXI_AWBURST(s{i}_axi_awburst_i), - .S{i:02d}_AXI_AWLOCK (s{i}_axi_awlock_i[0]), - .S{i:02d}_AXI_AWCACHE(s{i}_axi_awcache_i), - .S{i:02d}_AXI_AWPROT (s{i}_axi_awprot_i), - .S{i:02d}_AXI_AWQOS (s{i}_axi_awqos_i), - .S{i:02d}_AXI_AWVALID(s{i}_axi_awvalid_i), - .S{i:02d}_AXI_AWREADY(s{i}_axi_awready_o), - - //Write data - .S{i:02d}_AXI_WDATA (s{i}_axi_wdata_i), - .S{i:02d}_AXI_WSTRB (s{i}_axi_wstrb_i), - .S{i:02d}_AXI_WLAST (s{i}_axi_wlast_i), - .S{i:02d}_AXI_WVALID(s{i}_axi_wvalid_i), - .S{i:02d}_AXI_WREADY(s{i}_axi_wready_o), - - //Write response - .S{i:02d}_AXI_BID (s{i}_axi_bid_o[0]), - .S{i:02d}_AXI_BRESP (s{i}_axi_bresp_o), - .S{i:02d}_AXI_BVALID(s{i}_axi_bvalid_o), - .S{i:02d}_AXI_BREADY(s{i}_axi_bready_i), - - //Read address - .S{i:02d}_AXI_ARID (s{i}_axi_arid_i[0]), - .S{i:02d}_AXI_ARADDR (s{i}_axi_araddr_i), - .S{i:02d}_AXI_ARLEN (s{i}_axi_arlen_i), - .S{i:02d}_AXI_ARSIZE (s{i}_axi_arsize_i), - .S{i:02d}_AXI_ARBURST(s{i}_axi_arburst_i), - .S{i:02d}_AXI_ARLOCK (s{i}_axi_arlock_i[0]), - .S{i:02d}_AXI_ARCACHE(s{i}_axi_arcache_i), - .S{i:02d}_AXI_ARPROT (s{i}_axi_arprot_i), - .S{i:02d}_AXI_ARQOS (s{i}_axi_arqos_i), - .S{i:02d}_AXI_ARVALID(s{i}_axi_arvalid_i), - .S{i:02d}_AXI_ARREADY(s{i}_axi_arready_o), - - //Read data - .S{i:02d}_AXI_RID (s{i}_axi_rid_o[0]), - .S{i:02d}_AXI_RDATA (s{i}_axi_rdata_o), - .S{i:02d}_AXI_RRESP (s{i}_axi_rresp_o), - .S{i:02d}_AXI_RLAST (s{i}_axi_rlast_o), - .S{i:02d}_AXI_RVALID(s{i}_axi_rvalid_o), - .S{i:02d}_AXI_RREADY(s{i}_axi_rready_i), -""" - - for i in range(N_MASTERS): - attributes_dict["snippets"][-1][ - "verilog_code" - ] += f""" - // - // Master interface {i} - // - - .M{i:02d}_AXI_ARESET_OUT_N(m{i}_arstn_o), - .M{i:02d}_AXI_ACLK (m{i}_clk_i), - - //Write address - .M{i:02d}_AXI_AWID (m{i}_axi_awid_o), - .M{i:02d}_AXI_AWADDR (m{i}_axi_awaddr_o), - .M{i:02d}_AXI_AWLEN (m{i}_axi_awlen_o), - .M{i:02d}_AXI_AWSIZE (m{i}_axi_awsize_o), - .M{i:02d}_AXI_AWBURST(m{i}_axi_awburst_o), - .M{i:02d}_AXI_AWLOCK (m{i}_axi_awlock_o), - .M{i:02d}_AXI_AWCACHE(m{i}_axi_awcache_o), - .M{i:02d}_AXI_AWPROT (m{i}_axi_awprot_o), - .M{i:02d}_AXI_AWQOS (m{i}_axi_awqos_o), - .M{i:02d}_AXI_AWVALID(m{i}_axi_awvalid_o), - .M{i:02d}_AXI_AWREADY(m{i}_axi_awready_i), - - //Write data - .M{i:02d}_AXI_WDATA (m{i}_axi_wdata_o), - .M{i:02d}_AXI_WSTRB (m{i}_axi_wstrb_o), - .M{i:02d}_AXI_WLAST (m{i}_axi_wlast_o), - .M{i:02d}_AXI_WVALID(m{i}_axi_wvalid_o), - .M{i:02d}_AXI_WREADY(m{i}_axi_wready_i), - - //Write response - .M{i:02d}_AXI_BID (m{i}_axi_bid_i), - .M{i:02d}_AXI_BRESP (m{i}_axi_bresp_i), - .M{i:02d}_AXI_BVALID(m{i}_axi_bvalid_i), - .M{i:02d}_AXI_BREADY(m{i}_axi_bready_o), - - //Read address - .M{i:02d}_AXI_ARID (m{i}_axi_arid_o), - .M{i:02d}_AXI_ARADDR (m{i}_axi_araddr_o), - .M{i:02d}_AXI_ARLEN (m{i}_axi_arlen_o), - .M{i:02d}_AXI_ARSIZE (m{i}_axi_arsize_o), - .M{i:02d}_AXI_ARBURST(m{i}_axi_arburst_o), - .M{i:02d}_AXI_ARLOCK (m{i}_axi_arlock_o), - .M{i:02d}_AXI_ARCACHE(m{i}_axi_arcache_o), - .M{i:02d}_AXI_ARPROT (m{i}_axi_arprot_o), - .M{i:02d}_AXI_ARQOS (m{i}_axi_arqos_o), - .M{i:02d}_AXI_ARVALID(m{i}_axi_arvalid_o), - .M{i:02d}_AXI_ARREADY(m{i}_axi_arready_i), - - //Read data - .M{i:02d}_AXI_RID (m{i}_axi_rid_i), - .M{i:02d}_AXI_RDATA (m{i}_axi_rdata_i), - .M{i:02d}_AXI_RRESP (m{i}_axi_rresp_i), - .M{i:02d}_AXI_RLAST (m{i}_axi_rlast_i), - .M{i:02d}_AXI_RVALID(m{i}_axi_rvalid_i), - .M{i:02d}_AXI_RREADY(m{i}_axi_rready_o), -""" - attributes_dict["snippets"][-1][ - "verilog_code" - ] += """ - .INTERCONNECT_ACLK (clk_i), - .INTERCONNECT_ARESETN(~arst_i) - ); -""" - - return attributes_dict diff --git a/lib/hardware/amd/xilinx_clock_wizard/hardware/src/clock_wizard.v b/lib/hardware/amd/xilinx_clock_wizard/hardware/src/clock_wizard.v deleted file mode 100644 index 231cde1c8..000000000 --- a/lib/hardware/amd/xilinx_clock_wizard/hardware/src/clock_wizard.v +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ps / 1ps - -module clock_wizard #( - parameter OUTPUT_PER = 10, - parameter INPUT_PER = 4 -) ( // Clock in ports - // Clock out ports - output clk_out1, - output arst_out1, - input clk_in1_p, - input clk_in1_n, - input arst_i -); - - wire clk_in1_clock_wizard; - - IBUFDS clkin1_ibufds ( - .O (clk_in1_clock_wizard), - .I (clk_in1_p), - .IB(clk_in1_n) - ); - - wire clk_out1_clock_wizard; - wire clk_out2_clock_wizard; - wire clk_out3_clock_wizard; - wire clk_out4_clock_wizard; - wire clk_out5_clock_wizard; - wire clk_out6_clock_wizard; - wire clk_out7_clock_wizard; - - wire [15:0] do_unused; - wire drdy_unused; - wire psdone_unused; - wire locked_int; - wire clkfbout_clock_wizard; - wire clkfboutb_unused; - wire clkout0b_unused; - wire clkout1_unused; - wire clkout1b_unused; - wire clkfbstopped_unused; - wire clkinstopped_unused; - - - PLLE3_ADV #( - .COMPENSATION ("AUTO"), - .STARTUP_WAIT ("FALSE"), - .DIVCLK_DIVIDE (1), - .CLKFBOUT_MULT (4), - .CLKFBOUT_PHASE (0.000), - .CLKOUT0_DIVIDE (4 * OUTPUT_PER / INPUT_PER), - .CLKOUT0_PHASE (0.000), - .CLKOUT0_DUTY_CYCLE(0.500), - .CLKIN_PERIOD (INPUT_PER) - ) plle3_adv_inst ( - .CLKFBOUT (clkfbout_clock_wizard), - .CLKOUT0 (clk_out1_clock_wizard), - .CLKOUT0B (clkout0b_unused), - .CLKOUT1 (clkout1_unused), - .CLKOUT1B (clkout1b_unused), - // Input clock control - .CLKFBIN (clkfbout_clock_wizard), - .CLKIN (clk_in1_clock_wizard), - // Ports for dynamic reconfiguration - .DADDR (7'h0), - .DCLK (1'b0), - .DEN (1'b0), - .DI (16'h0), - .DO (do_unused), - .DRDY (drdy_unused), - .DWE (1'b0), - .CLKOUTPHYEN(1'b0), - .CLKOUTPHY (), - // Other control and status signals - .LOCKED (locked_int), - .PWRDWN (1'b0), - .RST (arst_i) - ); - - - assign arst_out1 = ~locked_int; - - BUFG clkout1_buf ( - .O(clk_out1), - .I(clk_out1_clock_wizard) - ); - -endmodule diff --git a/lib/hardware/amd/xilinx_clock_wizard/xilinx_clock_wizard.py b/lib/hardware/amd/xilinx_clock_wizard/xilinx_clock_wizard.py deleted file mode 100644 index 207b2bd71..000000000 --- a/lib/hardware/amd/xilinx_clock_wizard/xilinx_clock_wizard.py +++ /dev/null @@ -1,91 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "OUTPUT_PER", - "type": "P", - "val": "0", - "min": "1", - "max": "NA", - "descr": "", - }, - { - "name": "INPUT_PER", - "type": "P", - "val": "0", - "min": "1", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_rst_i", - "descr": "clock and reset inputs", - "signals": [ - {"name": "clk_p", "direction": "input", "width": "1"}, - {"name": "clk_n", "direction": "input", "width": "1"}, - {"name": "arst", "direction": "input", "width": "1"}, - ], - }, - { - "name": "clk_rst_o", - "descr": "clock and reset outputs", - "signals": [ - {"name": "clk_out1", "direction": "output", "width": "1"}, - {"name": "rst_out1", "direction": "output", "width": "1"}, - ], - }, - ], - "wires": [ - { - "name": "reset_sync_clk_rst", - "descr": "Reset synchronizer inputs", - "signals": [ - {"name": "clk_out1"}, - {"name": "arst_out", "width": "1"}, - ], - }, - { - "name": "reset_sync_rst_out", - "descr": "Reset synchronizer output", - "signals": [ - {"name": "rst_out1"}, - ], - }, - ], - } - attributes_dict["blocks"] = [ - { - "core_name": "iob_reset_sync", - "instance_name": "rst_sync", - "connect": { - "clk_rst_s": "reset_sync_clk_rst", - "arst_o": "reset_sync_rst_out", - }, - }, - ] - attributes_dict["snippets"] = [ - { - "verilog_code": """ - clock_wizard #( - .OUTPUT_PER(OUTPUT_PER), - .INPUT_PER (INPUT_PER) - ) clock_wizard_inst ( - .clk_in1_p(clk_p_i), - .clk_in1_n(clk_n_i), - .arst_i(arst_i), - .clk_out1 (clk_out1_o), - .arst_out1(arst_out) - ); -""", - }, - ] - - return attributes_dict diff --git a/lib/hardware/amd/xilinx_ddr4_ctrl/hardware/src/ddr.xdc b/lib/hardware/amd/xilinx_ddr4_ctrl/hardware/src/ddr.xdc deleted file mode 100644 index 3fb1f36ea..000000000 --- a/lib/hardware/amd/xilinx_ddr4_ctrl/hardware/src/ddr.xdc +++ /dev/null @@ -1,245 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -## DDR4 Interface -set_property PACKAGE_PIN AA25 [get_ports {c0_ddr4_ck_t_o}] -set_property PACKAGE_PIN AB25 [get_ports {c0_ddr4_ck_c_o}] -set_property IOSTANDARD DIFF_SSTL12_DCI [ get_ports {c0_ddr4_ck_t_o} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_ck_t_o} ] -set_property IOSTANDARD DIFF_SSTL12_DCI [ get_ports {c0_ddr4_ck_c_o} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_ck_c_o} ] - -set_property PACKAGE_PIN AA24 [get_ports {c0_ddr4_adr_o[0]}] -set_property PACKAGE_PIN AB24 [get_ports {c0_ddr4_adr_o[1]}] -set_property PACKAGE_PIN AB26 [get_ports {c0_ddr4_adr_o[2]}] -set_property PACKAGE_PIN AC26 [get_ports {c0_ddr4_adr_o[3]}] -set_property PACKAGE_PIN AA22 [get_ports {c0_ddr4_adr_o[4]}] -set_property PACKAGE_PIN AB22 [get_ports {c0_ddr4_adr_o[5]}] -set_property PACKAGE_PIN Y23 [get_ports {c0_ddr4_adr_o[6]}] -set_property PACKAGE_PIN AA23 [get_ports {c0_ddr4_adr_o[7]}] -set_property PACKAGE_PIN AC23 [get_ports {c0_ddr4_adr_o[8]}] -set_property PACKAGE_PIN AC24 [get_ports {c0_ddr4_adr_o[9]}] -set_property PACKAGE_PIN W23 [get_ports {c0_ddr4_adr_o[10]}] -set_property PACKAGE_PIN W24 [get_ports {c0_ddr4_adr_o[11]}] -set_property PACKAGE_PIN W25 [get_ports {c0_ddr4_adr_o[12]}] -set_property PACKAGE_PIN W26 [get_ports {c0_ddr4_adr_o[13]}] - -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[0]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[1]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[2]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[3]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[4]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[5]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[6]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[7]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[8]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[9]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[10]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[11]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[12]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[13]}] - -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[0]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[1]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[2]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[3]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[4]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[5]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[6]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[7]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[8]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[9]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[10]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[11]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[12]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[13]}] - -set_property PACKAGE_PIN F22 [get_ports {c0_ddr4_dqs_t_io[3]}] -set_property PACKAGE_PIN F23 [get_ports {c0_ddr4_dqs_c_io[3]}] -set_property PACKAGE_PIN K26 [get_ports {c0_ddr4_dqs_t_io[2]}] -set_property PACKAGE_PIN J26 [get_ports {c0_ddr4_dqs_c_io[2]}] -set_property PACKAGE_PIN T19 [get_ports {c0_ddr4_dqs_t_io[1]}] -set_property PACKAGE_PIN T20 [get_ports {c0_ddr4_dqs_c_io[1]}] -set_property PACKAGE_PIN R22 [get_ports {c0_ddr4_dqs_t_io[0]}] -set_property PACKAGE_PIN R23 [get_ports {c0_ddr4_dqs_c_io[0]}] - -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_t_io[3]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_c_io[3]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_t_io[2]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_c_io[2]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_t_io[1]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_c_io[1]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_t_io[0]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dqs_c_io[0]}] - -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_t_io[3]}] -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_c_io[3]}] -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_t_io[2]}] -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_c_io[2]}] -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_t_io[1]}] -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_c_io[1]}] -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_t_io[0]}] -set_property IOSTANDARD DIFF_POD12_DCI [get_ports {c0_ddr4_dqs_c_io[0]}] - -set_property PACKAGE_PIN E25 [get_ports {c0_ddr4_dm_dbi_n_io[3]}] -set_property PACKAGE_PIN N23 [get_ports {c0_ddr4_dm_dbi_n_io[2]}] -set_property PACKAGE_PIN R18 [get_ports {c0_ddr4_dm_dbi_n_io[1]}] -set_property PACKAGE_PIN T23 [get_ports {c0_ddr4_dm_dbi_n_io[0]}] - -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dm_dbi_n_io[3]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dm_dbi_n_io[2]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dm_dbi_n_io[1]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dm_dbi_n_io[0]}] - -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dm_dbi_n_io[3]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dm_dbi_n_io[2]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dm_dbi_n_io[1]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dm_dbi_n_io[0]}] - -set_property PACKAGE_PIN G24 [get_ports {c0_ddr4_dq_io[31]}] -set_property PACKAGE_PIN H24 [get_ports {c0_ddr4_dq_io[30]}] -set_property PACKAGE_PIN J25 [get_ports {c0_ddr4_dq_io[29]}] -set_property PACKAGE_PIN J24 [get_ports {c0_ddr4_dq_io[28]}] -set_property PACKAGE_PIN F25 [get_ports {c0_ddr4_dq_io[27]}] -set_property PACKAGE_PIN G25 [get_ports {c0_ddr4_dq_io[26]}] -set_property PACKAGE_PIN G26 [get_ports {c0_ddr4_dq_io[25]}] -set_property PACKAGE_PIN H26 [get_ports {c0_ddr4_dq_io[24]}] -set_property PACKAGE_PIN L25 [get_ports {c0_ddr4_dq_io[23]}] -set_property PACKAGE_PIN M25 [get_ports {c0_ddr4_dq_io[22]}] -set_property PACKAGE_PIN M22 [get_ports {c0_ddr4_dq_io[21]}] -set_property PACKAGE_PIN N22 [get_ports {c0_ddr4_dq_io[20]}] -set_property PACKAGE_PIN L24 [get_ports {c0_ddr4_dq_io[19]}] -set_property PACKAGE_PIN M24 [get_ports {c0_ddr4_dq_io[18]}] -set_property PACKAGE_PIN M26 [get_ports {c0_ddr4_dq_io[17]}] -set_property PACKAGE_PIN N26 [get_ports {c0_ddr4_dq_io[16]}] -set_property PACKAGE_PIN U21 [get_ports {c0_ddr4_dq_io[15]}] -set_property PACKAGE_PIN U20 [get_ports {c0_ddr4_dq_io[14]}] -set_property PACKAGE_PIN R20 [get_ports {c0_ddr4_dq_io[13]}] -set_property PACKAGE_PIN P20 [get_ports {c0_ddr4_dq_io[12]}] -set_property PACKAGE_PIN P19 [get_ports {c0_ddr4_dq_io[11]}] -set_property PACKAGE_PIN P18 [get_ports {c0_ddr4_dq_io[10]}] -set_property PACKAGE_PIN R21 [get_ports {c0_ddr4_dq_io[9]}] -set_property PACKAGE_PIN P21 [get_ports {c0_ddr4_dq_io[8]}] -set_property PACKAGE_PIN R25 [get_ports {c0_ddr4_dq_io[7]}] -set_property PACKAGE_PIN P25 [get_ports {c0_ddr4_dq_io[6]}] -set_property PACKAGE_PIN P24 [get_ports {c0_ddr4_dq_io[5]}] -set_property PACKAGE_PIN P23 [get_ports {c0_ddr4_dq_io[4]}] -set_property PACKAGE_PIN R26 [get_ports {c0_ddr4_dq_io[3]}] -set_property PACKAGE_PIN P26 [get_ports {c0_ddr4_dq_io[2]}] -set_property PACKAGE_PIN U22 [get_ports {c0_ddr4_dq_io[1]}] -set_property PACKAGE_PIN T22 [get_ports {c0_ddr4_dq_io[0]}] - -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[31]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[30]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[29]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[28]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[27]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[26]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[25]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[24]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[23]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[22]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[21]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[20]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[19]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[18]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[17]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[16]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[15]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[14]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[13]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[12]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[11]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[10]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[9]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[8]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[7]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[6]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[5]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[4]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[3]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[2]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[1]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_dq_io[0]}] - -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[31]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[30]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[29]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[28]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[27]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[26]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[25]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[24]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[23]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[22]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[21]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[20]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[19]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[18]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[17]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[16]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[15]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[14]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[13]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[12]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[11]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[10]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[9]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[8]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[7]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[6]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[5]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[4]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[3]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[2]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[1]}] -set_property IOSTANDARD POD12_DCI [get_ports {c0_ddr4_dq_io[0]}] - -## WE_N Signal -set_property PACKAGE_PIN Y25 [get_ports {c0_ddr4_adr_o[14]}] -## CAS_N Signal -set_property PACKAGE_PIN Y26 [get_ports {c0_ddr4_adr_o[15]}] -## RAS_N Signal -set_property PACKAGE_PIN U26 [get_ports {c0_ddr4_adr_o[16]}] - -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[14]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[15]}] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports {c0_ddr4_adr_o[16]}] - -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[14]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[15]}] -set_property IOSTANDARD SSTL12_DCI [get_ports {c0_ddr4_adr_o[16]}] - -set_property PACKAGE_PIN T24 [get_ports {c0_ddr4_act_n_o}] -set_property IOSTANDARD SSTL12_DCI [ get_ports {c0_ddr4_act_n_o} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_act_n_o} ] - -set_property PACKAGE_PIN T25 [get_ports c0_ddr4_reset_n_o] -set_property IOSTANDARD LVCMOS12 [ get_ports {c0_ddr4_reset_n_o} ] -set_property DRIVE 8 [get_ports c0_ddr4_reset_n_o] - -set_property PACKAGE_PIN U25 [get_ports {c0_ddr4_odt_o}] -set_property IOSTANDARD SSTL12_DCI [ get_ports {c0_ddr4_odt_o} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_odt_o} ] - -set_property PACKAGE_PIN V22 [get_ports {c0_ddr4_cs_n_o}] -set_property IOSTANDARD SSTL12_DCI [ get_ports {c0_ddr4_cs_n_o} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_cs_n_o} ] - -set_property PACKAGE_PIN V23 [get_ports {c0_ddr4_cke_o}] -set_property IOSTANDARD SSTL12_DCI [ get_ports {c0_ddr4_cke_o} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_cke_o} ] - -set_property PACKAGE_PIN V26 [get_ports {c0_ddr4_ba_o[0]}] -set_property IOSTANDARD SSTL12_DCI [ get_ports {c0_ddr4_ba_o[0]} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_ba_o[0]} ] - -set_property PACKAGE_PIN U24 [get_ports {c0_ddr4_ba_o[1]}] -set_property IOSTANDARD SSTL12_DCI [ get_ports {c0_ddr4_ba_o[1]} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_ba_o[1]} ] - -set_property PACKAGE_PIN V24 [get_ports {c0_ddr4_bg_o}] -set_property IOSTANDARD SSTL12_DCI [ get_ports {c0_ddr4_bg_o} ] -set_property OUTPUT_IMPEDANCE RDRV_40_40 [ get_ports {c0_ddr4_bg_o} ] - diff --git a/lib/hardware/amd/xilinx_ddr4_ctrl/xilinx_ddr4_ctrl.py b/lib/hardware/amd/xilinx_ddr4_ctrl/xilinx_ddr4_ctrl.py deleted file mode 100644 index 746d1a7d8..000000000 --- a/lib/hardware/amd/xilinx_ddr4_ctrl/xilinx_ddr4_ctrl.py +++ /dev/null @@ -1,195 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - # - # AXI Parameters - # - { - "name": "AXI_ID_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI ID bus width", - }, - { - "name": "AXI_LEN_W", - "type": "P", - "val": "0", - "min": "1", - "max": "8", - "descr": "AXI burst length width", - }, - { - "name": "AXI_ADDR_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI address bus width", - }, - { - "name": "AXI_DATA_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI data bus width", - }, - ], - "ports": [ - # - # Ports - # - { - "name": "clk_rst_i", - "signals": [ - {"name": "clk_p", "direction": "input", "width": "1"}, - {"name": "clk_n", "direction": "input", "width": "1"}, - {"name": "arst", "direction": "input", "width": "1"}, - ], - }, - { - "name": "ui_clk_o", - "signals": [ - {"name": "clkout", "direction": "output", "width": "1"}, - ], - }, - { - "name": "axi_clk_rst", - "descr": "", - "signals": [ - {"name": "axi_clk", "direction": "output", "width": "1"}, - {"name": "axi_clk_rst", "direction": "output", "width": "1"}, - {"name": "axi_arstn", "direction": "input", "width": "1"}, - ], - }, - { - "name": "axi_s", - "interface": { - "type": "axi", - "subtype": "slave", - "ID_W": "AXI_ID_W", - "LEN_W": "AXI_LEN_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1, - }, - "descr": "AXI interface", - }, - { - "name": "ddr4", - "descr": "DDR4 interface", - "signals": [ - {"name": "ddr4_act_n", "direction": "output", "width": "1"}, - {"name": "ddr4_adr", "direction": "output", "width": "17"}, - {"name": "ddr4_ba", "direction": "output", "width": "2"}, - {"name": "ddr4_bg", "direction": "output", "width": "1"}, - {"name": "ddr4_cke", "direction": "output", "width": "1"}, - {"name": "ddr4_odt", "direction": "output", "width": "1"}, - {"name": "ddr4_cs_n", "direction": "output", "width": "1"}, - {"name": "ddr4_ck_t", "direction": "output", "width": "1"}, - {"name": "ddr4_ck_c", "direction": "output", "width": "1"}, - {"name": "ddr4_reset_n", "direction": "output", "width": "1"}, - {"name": "ddr4_dm_dbi_n", "direction": "inout", "width": "4"}, - {"name": "ddr4_dq", "direction": "inout", "width": "32"}, - {"name": "ddr4_dqs_c", "direction": "inout", "width": "4"}, - {"name": "ddr4_dqs_t", "direction": "inout", "width": "4"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - ddr4_0 ddr4_ctrl ( - .sys_rst (arst_i), - .c0_sys_clk_p(clk_p_i), - .c0_sys_clk_n(clk_n_i), - - .dbg_clk(), - .dbg_bus(), - - //USER LOGIC CLOCK - .addn_ui_clkout1 (clkout_o), - - //AXI INTERFACE (slave) - .c0_ddr4_ui_clk (axi_clk_o), - .c0_ddr4_ui_clk_sync_rst(axi_clk_rst_o), - .c0_ddr4_aresetn (axi_arstn_i), - - //address write - .c0_ddr4_s_axi_awid (axi_awid_i), - .c0_ddr4_s_axi_awaddr (axi_awaddr_i), - .c0_ddr4_s_axi_awlen (axi_awlen_i), - .c0_ddr4_s_axi_awsize (axi_awsize_i), - .c0_ddr4_s_axi_awburst(axi_awburst_i), - .c0_ddr4_s_axi_awlock (axi_awlock_i), - .c0_ddr4_s_axi_awprot (axi_awprot_i), - .c0_ddr4_s_axi_awcache(axi_awcache_i), - .c0_ddr4_s_axi_awqos (axi_awqos_i), - .c0_ddr4_s_axi_awvalid(axi_awvalid_i), - .c0_ddr4_s_axi_awready(axi_awready_o), - - //write - .c0_ddr4_s_axi_wvalid(axi_wvalid_i), - .c0_ddr4_s_axi_wready(axi_wready_o), - .c0_ddr4_s_axi_wdata (axi_wdata_i), - .c0_ddr4_s_axi_wstrb (axi_wstrb_i), - .c0_ddr4_s_axi_wlast (axi_wlast_i), - - //write response - .c0_ddr4_s_axi_bready(axi_bready_i), - .c0_ddr4_s_axi_bid (axi_bid_o), - .c0_ddr4_s_axi_bresp (axi_bresp_o), - .c0_ddr4_s_axi_bvalid(axi_bvalid_o), - - //address read - .c0_ddr4_s_axi_arid (axi_arid_i), - .c0_ddr4_s_axi_araddr (axi_araddr_i), - .c0_ddr4_s_axi_arlen (axi_arlen_i), - .c0_ddr4_s_axi_arsize (axi_arsize_i), - .c0_ddr4_s_axi_arburst(axi_arburst_i), - .c0_ddr4_s_axi_arlock (axi_arlock_i), - .c0_ddr4_s_axi_arcache(axi_arcache_i), - .c0_ddr4_s_axi_arprot (axi_arprot_i), - .c0_ddr4_s_axi_arqos (axi_arqos_i), - .c0_ddr4_s_axi_arvalid(axi_arvalid_i), - .c0_ddr4_s_axi_arready(axi_arready_o), - - //read - .c0_ddr4_s_axi_rready(axi_rready_i), - .c0_ddr4_s_axi_rid (axi_rid_o), - .c0_ddr4_s_axi_rdata (axi_rdata_o), - .c0_ddr4_s_axi_rresp (axi_rresp_o), - .c0_ddr4_s_axi_rlast (axi_rlast_o), - .c0_ddr4_s_axi_rvalid(axi_rvalid_o), - - //DDR4 INTERFACE (master of external DDR4 module) - .c0_ddr4_act_n (ddr4_act_n_o), - .c0_ddr4_adr (ddr4_adr_o), - .c0_ddr4_ba (ddr4_ba_o), - .c0_ddr4_bg (ddr4_bg_o), - .c0_ddr4_cke (ddr4_cke_o), - .c0_ddr4_odt (ddr4_odt_o), - .c0_ddr4_cs_n (ddr4_cs_n_o), - .c0_ddr4_ck_t (ddr4_ck_t_o), - .c0_ddr4_ck_c (ddr4_ck_c_o), - .c0_ddr4_reset_n (ddr4_reset_n_o), - .c0_ddr4_dm_dbi_n (ddr4_dm_dbi_n_io), - .c0_ddr4_dq (ddr4_dq_io), - .c0_ddr4_dqs_c (ddr4_dqs_c_io), - .c0_ddr4_dqs_t (ddr4_dqs_t_io), - .c0_init_calib_complete() - ); -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/amd/xilinx_ibufg/xilinx_ibufg.py b/lib/hardware/amd/xilinx_ibufg/xilinx_ibufg.py deleted file mode 100644 index 7a74b5993..000000000 --- a/lib/hardware/amd/xilinx_ibufg/xilinx_ibufg.py +++ /dev/null @@ -1,31 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "ports": [ - { - "name": "io", - "descr": "IBUFG io", - "signals": [ - {"name": "i", "direction": "input", "width": "1"}, - {"name": "o", "direction": "output", "width": "1"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - IBUFG ibufg_inst ( - .I(i_i), - .O(o_o) - ); -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/amd/xilinx_oddre1/xilinx_oddre1.py b/lib/hardware/amd/xilinx_oddre1/xilinx_oddre1.py deleted file mode 100644 index d32df989e..000000000 --- a/lib/hardware/amd/xilinx_oddre1/xilinx_oddre1.py +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "ports": [ - { - "name": "io", - "descr": "ODDRE1 io", - "signals": [ - {"name": "q", "direction": "output", "width": "1"}, - {"name": "c", "direction": "input", "width": "1"}, - {"name": "d1", "direction": "input", "width": "1"}, - {"name": "d2", "direction": "input", "width": "1"}, - {"name": "sr", "direction": "input", "width": "1"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - ODDRE1 ODDRE1_inst ( - .Q (q_o), - .C (c_i), - .D1(d1_i), - .D2(d2_i), - .SR(sr_i) - ); -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/counter/iob_counter/iob_counter.py b/lib/hardware/arith_logic/counter/iob_counter/iob_counter.py deleted file mode 100644 index f60244183..000000000 --- a/lib/hardware/arith_logic/counter/iob_counter/iob_counter.py +++ /dev/null @@ -1,96 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "1", - "max": "NA", - "descr": "", - }, - { - "name": "RST_VAL", - "type": "P", - "val": "{DATA_W{1'b0}}", - "min": "0", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "en_rst_i", - "descr": "Enable and Synchronous reset interface", - "signals": [ - { - "name": "en", - "direction": "input", - "width": 1, - "descr": "Enable input", - }, - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - {"name": "data", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "wires": [ - { - "name": "data_int", - "descr": "data_int wire", - "signals": [ - {"name": "data_int", "width": "DATA_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "reg0", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "RST_VAL", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "en_rst_i": "en_rst_i", - "data_i": "data_int", - "data_o": "data_o", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign data_int = data_o + 1'b1; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/counter/iob_counter_ld/iob_counter_ld.py b/lib/hardware/arith_logic/counter/iob_counter_ld/iob_counter_ld.py deleted file mode 100644 index 5083547b9..000000000 --- a/lib/hardware/arith_logic/counter/iob_counter_ld/iob_counter_ld.py +++ /dev/null @@ -1,110 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "1", - "max": "NA", - "descr": "", - }, - { - "name": "RST_VAL", - "type": "P", - "val": "{DATA_W{1'b0}}", - "min": "0", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "en_rst_i", - "descr": "Enable and Synchronous reset interface", - "signals": [ - { - "name": "en", - "direction": "input", - "width": 1, - "descr": "Enable input", - }, - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "ld_i", - "descr": "Input port", - "signals": [ - {"name": "ld", "width": 1, "direction": "input"}, - ], - }, - { - "name": "ld_val_i", - "descr": "Input port", - "signals": [ - {"name": "ld_val", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - {"name": "data", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "wires": [ - { - "name": "data_int", - "descr": "data wire", - "signals": [ - {"name": "data_int", "width": "DATA_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "reg0", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "RST_VAL", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "en_rst_i": "en_rst_i", - "data_i": "data_int", - "data_o": "data_o", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign data_int = ld_i ? ld_val_i : data_o + 1'b1; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/counter/iob_modcnt/iob_modcnt.py b/lib/hardware/arith_logic/counter/iob_modcnt/iob_modcnt.py deleted file mode 100644 index ea4c26e18..000000000 --- a/lib/hardware/arith_logic/counter/iob_modcnt/iob_modcnt.py +++ /dev/null @@ -1,112 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "1", - "max": "NA", - "descr": "", - }, - { - "name": "RST_VAL", - "type": "P", - "val": "{DATA_W{1'b0}}", - "min": "0", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "en_rst_i", - "descr": "Enable and Synchronous reset interface", - "signals": [ - { - "name": "en", - "direction": "input", - "width": 1, - "descr": "Enable input", - }, - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "mod_i", - "descr": "Input port", - "signals": [ - {"name": "mod", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - {"name": "data", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "wires": [ - { - "name": "ld_count", - "descr": "ld_count wire", - "signals": [ - {"name": "ld_count", "width": 1}, - ], - }, - { - "name": "ld_val", - "descr": "ld_val wire", - "signals": [ - {"name": "ld_val", "width": "DATA_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_counter_ld", - "instance_name": "cnt0", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "RST_VAL", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "en_rst_i": "en_rst_i", - "ld_i": "ld_count", - "ld_val_i": "ld_val", - "data_o": "data_o", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign ld_count = (data_o >= mod_i); - assign ld_val = {DATA_W{1'b0}}; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/div/iob_div_pipe/hardware/modules/iob_div_slice.py b/lib/hardware/arith_logic/div/iob_div_pipe/hardware/modules/iob_div_slice.py deleted file mode 100644 index 27fc9c9a7..000000000 --- a/lib/hardware/arith_logic/div/iob_div_pipe/hardware/modules/iob_div_slice.py +++ /dev/null @@ -1,159 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "STAGE", - "type": "P", - "val": "1", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "OUTPUT_REG", - "type": "P", - "val": "1", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "dividend_i", - "descr": "Input port", - "signals": [ - { - "name": "dividend", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "divisor_i", - "descr": "Input port", - "signals": [ - { - "name": "divisor", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "quotient_i", - "descr": "Input port", - "signals": [ - { - "name": "quotient", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "dividend_o", - "descr": "Output port", - "signals": [ - { - "name": "dividend", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - { - "name": "divisor_o", - "descr": "Output port", - "signals": [ - { - "name": "divisor", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - { - "name": "quotient_o", - "descr": "Output port", - "signals": [ - { - "name": "quotient", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - ], - "wires": [ - { - "name": "sub_sign", - "descr": "sub_sign wire", - "signals": [ - {"name": "sub_sign", "width": 1}, - ], - }, - { - "name": "sub_res", - "descr": "sub_res wire", - "signals": [ - {"name": "sub_res", "width": "2*DATA_W-STAGE"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - assign sub_res = {{DATA_W{1'b0}}, dividend_i} - {{STAGE{1'b0}}, divisor_i, {(DATA_W-STAGE){1'b0}}}; - assign sub_sign = sub_res[2*DATA_W-STAGE]; - - generate - if (OUTPUT_REG) begin - always @(posedge clk_i) begin - dividend_o <= (sub_sign) ? dividend_i : sub_res[DATA_W-1:0]; - quotient_o <= quotient_i << 1 | {{(DATA_W - 1) {1'b0}}, ~sub_sign}; - divisor_o <= divisor_i; - end - end else begin - always @* begin - dividend_o = (sub_sign) ? dividend_i : sub_res[DATA_W-1:0]; - quotient_o = quotient_i << 1 | {{(DATA_W - 1) {1'b0}}, ~sub_sign}; - divisor_o = divisor_i; - end - end - endgenerate - - """, - }, - ], - } - return attributes_dict diff --git a/lib/hardware/arith_logic/div/iob_div_pipe/hardware/simulation/src/iob_div_pipe_tb.v b/lib/hardware/arith_logic/div/iob_div_pipe/hardware/simulation/src/iob_div_pipe_tb.v deleted file mode 100644 index c3d9d5e44..000000000 --- a/lib/hardware/arith_logic/div/iob_div_pipe/hardware/simulation/src/iob_div_pipe_tb.v +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define CLK_FREQ (100000000) - -module iob_div_pipe_tb; - - parameter clk_frequency = `CLK_FREQ; - parameter clk_period = 1e9/clk_frequency; //ns - - parameter DATA_W = 32; - parameter OPERS_PER_STAGE = 8; - - parameter TEST_SZ = 100; - - reg clk; - - wire [DATA_W-1:0] dividend; - wire [DATA_W-1:0] divisor; - wire [DATA_W-1:0] quotient; - wire [DATA_W-1:0] remainder; - - wire [DATA_W-1:0] q; - wire [DATA_W-1:0] r; - - reg [DATA_W-1:0] dividend_in [0:TEST_SZ-1]; - reg [DATA_W-1:0] divisor_in [0:TEST_SZ-1]; - reg [DATA_W-1:0] quotient_out [0:TEST_SZ-1]; - reg [DATA_W-1:0] remainder_out [0:TEST_SZ-1]; - - integer i, j; - integer fp; - - iob_div_pipe # ( - .DATA_W(DATA_W), - .OPERS_PER_STAGE(OPERS_PER_STAGE) - ) - uut ( - .clk_i(clk), - - .dividend_i(dividend), - .divisor_i(divisor), - - .quotient_o(quotient), - .remainder_o(remainder) - ); - - initial begin - -`ifdef VCD - $dumpfile("div_pipe.vcd"); - $dumpvars(); -`endif - - clk = 0; - - j=0; - - // generate test data - for (i=0; i < TEST_SZ; i=i+1) begin - dividend_in[i] = $random%(2**(DATA_W-1)); - divisor_in[i] = $random%(2**(DATA_W-1)); - quotient_out[i] = dividend_in[i] / divisor_in[i]; - remainder_out[i] = dividend_in[i] % divisor_in[i]; - end - - #((TEST_SZ+DATA_W/OPERS_PER_STAGE)*clk_period); - - #clk_period; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - - fp = $fopen("test.log", "w"); - $fdisplay(fp, "Test passed!"); - - #(5 * clk_period) $finish(); - end - - always - #(clk_period/2) clk = ~clk; - - always @ (posedge clk) begin - j <= j+1; - end - - // assign inputs - assign dividend = dividend_in[j]; - assign divisor = divisor_in[j]; - - // show expected results - assign q = quotient_out[j]; - assign r = remainder_out[j]; - - always @ (negedge clk) begin - if(j >= DATA_W/OPERS_PER_STAGE && (quotient != quotient_out[j-DATA_W/OPERS_PER_STAGE] || - remainder != remainder_out[j-DATA_W/OPERS_PER_STAGE])) begin - $display("Test failed at %d", $time); - $finish; - end - end - -endmodule diff --git a/lib/hardware/arith_logic/div/iob_div_pipe/iob_div_pipe.py b/lib/hardware/arith_logic/div/iob_div_pipe/iob_div_pipe.py deleted file mode 100755 index d6e524c95..000000000 --- a/lib/hardware/arith_logic/div/iob_div_pipe/iob_div_pipe.py +++ /dev/null @@ -1,136 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "OPERS_PER_STAGE", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "OPERS_PER_STAGE width", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - { - "name": "clk", - "direction": "input", - "width": 1, - "descr": "Clock", - }, - ], - }, - { - "name": "div", - "descr": "Division interface", - "signals": [ - { - "name": "dividend", - "direction": "input", - "width": "DATA_W", - "descr": "", - }, - { - "name": "divisor", - "direction": "input", - "width": "DATA_W", - "descr": "", - }, - { - "name": "quotient", - "direction": "output", - "width": "DATA_W", - "descr": "", - }, - { - "name": "remainder", - "direction": "output", - "width": "DATA_W", - "descr": "", - }, - ], - }, - ], - "wires": [ - { - "name": "dividend_int", - "descr": "dividend_int wire", - "signals": [ - {"name": "dividend_int", "width": "(DATA_W+1)*DATA_W"}, - ], - }, - { - "name": "divisor_int", - "descr": "divisor_int wire", - "signals": [ - {"name": "divisor_int", "width": "(DATA_W+1)*DATA_W"}, - ], - }, - { - "name": "quotient_int", - "descr": "quotient_int wire", - "signals": [ - {"name": "quotient_int", "width": "(DATA_W+1)*DATA_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_div_slice", - "instantiate": False, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign dividend_int[DATA_W-1:0] = dividend_i; - assign divisor_int[DATA_W-1:0] = divisor_i; - assign quotient_int[DATA_W-1:0] = {DATA_W{1'b0}}; - - genvar k; - generate - for (k = 1; k <= DATA_W; k = k + 1) begin : div_slice_array_el - iob_div_slice #( - .DATA_W (DATA_W), - .STAGE (k), - .OUTPUT_REG(!(k % OPERS_PER_STAGE)) - ) uut ( - .clk_i(clk_i), - - .dividend_i(dividend_int[k*DATA_W-1-:DATA_W]), - .divisor_i (divisor_int[k*DATA_W-1-:DATA_W]), - .quotient_i(quotient_int[k*DATA_W-1-:DATA_W]), - - .dividend_o(dividend_int[(k+1)*DATA_W-1-:DATA_W]), - .divisor_o (divisor_int[(k+1)*DATA_W-1-:DATA_W]), - .quotient_o(quotient_int[(k+1)*DATA_W-1-:DATA_W]) - ); - end - endgenerate - - assign quotient_o = quotient_int[(DATA_W+1)*DATA_W-1-:DATA_W]; - assign remainder_o = dividend_int[(DATA_W+1)*DATA_W-1-:DATA_W]; - - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/div/iob_div_subshift/hardware/simulation/src/iob_div_subshift_tb.v b/lib/hardware/arith_logic/div/iob_div_subshift/hardware/simulation/src/iob_div_subshift_tb.v deleted file mode 100644 index dc8354c77..000000000 --- a/lib/hardware/arith_logic/div/iob_div_subshift/hardware/simulation/src/iob_div_subshift_tb.v +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define CLK_FREQ (100000000) - -module iob_div_subshift_tb; - - parameter clk_frequency = `CLK_FREQ; - parameter clk_period = 1e9/clk_frequency; //ns - parameter DATA_W = 8; - parameter TEST_SZ = 1000; - - reg clk = 0; - reg rst = 0; - reg start = 0; - wire done; - - //data - reg [DATA_W-1:0] dividend [0:TEST_SZ-1]; - reg [DATA_W-1:0] divisor [0:TEST_SZ-1]; - reg [DATA_W-1:0] quotient [0:TEST_SZ-1]; - reg [DATA_W-1:0] remainder [0:TEST_SZ-1]; - - //core outputs - wire [DATA_W-1:0] quotient_out; - wire [DATA_W-1:0] remainder_out; - - integer i; - integer fp; - - - initial begin - -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // generate test data - for (i=0; i < TEST_SZ; i=i+1) begin - dividend[i] = $random; - divisor[i] = $random; - quotient[i] = dividend[i] / divisor[i]; - remainder[i] = dividend[i] % divisor[i]; - end - - //reset pulse - #100 rst = 1; - @(posedge clk) #1 rst = 0; - - //compute divisions - for (i=0; i < TEST_SZ; i=i+1) begin - //pulse start - @(posedge clk) #1 start = 1; - @(posedge clk) #1 start = 0; - - //wait for done - @(posedge clk) #1; - while(!done) - @(posedge clk) #1; - - //verify results - if(quotient_out != quotient[i] || remainder_out != remainder[i]) - $display ("%d / %d = %d with rem %d but got %d with rem %d", dividend[i], divisor[i], quotient[i], remainder[i], quotient_out, remainder_out); - else begin - fp = $fopen("test.log", "w"); - $fdisplay(fp, "Test passed!"); - end - end - - #clk_period; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - - #(5 * clk_period) $finish(); - - end - - //clock - always #(clk_period/2) clk = ~clk; - - //instantiate unsigned divider - iob_div_subshift - # ( - .DATA_W(DATA_W) - ) - uut - ( - .clk_i(clk), - .arst_i(rst), - .cke_i(1'b1), - .start_i(start), - .done_o(done), - - .dividend_i(dividend[i]), - .divisor_i(divisor[i]), - .quotient_o(quotient_out), - .remainder_o(remainder_out) - ); - -endmodule diff --git a/lib/hardware/arith_logic/div/iob_div_subshift/iob_div_subshift.py b/lib/hardware/arith_logic/div/iob_div_subshift/iob_div_subshift.py deleted file mode 100755 index f6939e985..000000000 --- a/lib/hardware/arith_logic/div/iob_div_subshift/iob_div_subshift.py +++ /dev/null @@ -1,189 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "status", - "descr": "", - "signals": [ - { - "name": "start", - "direction": "input", - "width": 1, - "descr": "Start signal", - }, - { - "name": "done", - "direction": "output", - "width": 1, - "descr": "Done signal", - }, - ], - }, - { - "name": "div", - "descr": "Division interface", - "signals": [ - { - "name": "dividend", - "direction": "input", - "width": "DATA_W", - "descr": "", - }, - { - "name": "divisor", - "direction": "input", - "width": "DATA_W", - "descr": "", - }, - { - "name": "quotient", - "direction": "output", - "width": "DATA_W", - "descr": "", - }, - { - "name": "remainder", - "direction": "output", - "width": "DATA_W", - "descr": "", - }, - ], - }, - ], - "wires": [ - # { # NOTE: This wire is implicitly create by py2 - # "name": "dqr_reg_nxt", - # "descr": "dqr_reg_nxt wire", - # "signals": [ - # {"name": "dqr_reg_nxt", "width": "2*DATA_W+1"}, - # ], - # }, - { - "name": "dqr_reg", - "descr": "dqr_reg wire", - "signals": [ - {"name": "dqr_reg", "width": "2*DATA_W+1"}, - ], - }, - # { # NOTE: This wire is implicitly create by py2 - # "name": "divisor_reg_nxt", - # "descr": "divisor_reg_nxt wire", - # "signals": [ - # {"name": "divisor_reg_nxt", "width": "DATA_W"}, - # ], - # }, - { - "name": "divisor_reg", - "descr": "divisor_reg wire", - "signals": [ - {"name": "divisor_reg", "width": "DATA_W"}, - ], - }, - { - "name": "subtraend", - "descr": "subtraend wire", - "signals": [ - {"name": "subtraend", "width": "DATA_W"}, - ], - }, - { - "name": "tmp", - "descr": "tmp wire", - "signals": [ - {"name": "tmp", "width": "DATA_W+1"}, - ], - }, - # { # NOTE: This wire is implicitly create by py2 - # "name": "pcnt_nxt", - # "descr": "pcnt_nxt wire", - # "signals": [ - # {"name": "pcnt_nxt", "width": "$clog2(DATA_W+1)"}, - # ], - # }, - { - "name": "pcnt", - "descr": "pcnt wire", - "signals": [ - {"name": "pcnt", "width": "$clog2(DATA_W+1)"}, - ], - }, - { - "name": "last_stage", - "descr": "last_stage wire", - "signals": [ - {"name": "last_stage", "width": "$clog2(DATA_W+1)"}, - ], - }, - { - "name": "done_reg", - "descr": "done_reg wire", - "signals": [ - {"name": "done_reg", "width": 1}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - assign subtraend = dqr_reg[(2*DATA_W)-2-:DATA_W]; - assign quotient_o = dqr_reg[DATA_W-1:0]; - assign remainder_o = dqr_reg[(2*DATA_W)-1:DATA_W]; - assign tmp = {1'b0, subtraend} - {1'b0, divisor_reg}; - assign last_stage = DATA_W + 1; - assign done_o = done_reg; - """, - }, - ], - "comb": { - "verilog_code": """ - pcnt_nxt = pcnt + 1'b1; - dqr_reg_nxt = dqr_reg; - divisor_reg_nxt = divisor_reg; - done_reg = 1'b1; - - if (pcnt == 0) begin //wait for start, load operands and do it - if (!start_i) begin - pcnt_nxt = pcnt; - end else begin - divisor_reg_nxt = divisor_i; - dqr_reg_nxt = {1'b0, {DATA_W{1'b0}}, dividend_i}; - end - end else if (pcnt == last_stage) begin - pcnt_nxt = 0; - end else begin //shift and subtract - done_reg = 1'b0; - if (~tmp[DATA_W]) begin - dqr_reg_nxt = {tmp, dqr_reg[DATA_W-2 : 0], 1'b1}; - end else begin - dqr_reg_nxt = {1'b0, dqr_reg[(2*DATA_W)-2 : 0], 1'b0}; - end - end -""", - }, - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/div/iob_div_subshift_frac/hardware/simulation/src/iob_div_subshift_frac_tb.v b/lib/hardware/arith_logic/div/iob_div_subshift_frac/hardware/simulation/src/iob_div_subshift_frac_tb.v deleted file mode 100644 index 11fcb78f0..000000000 --- a/lib/hardware/arith_logic/div/iob_div_subshift_frac/hardware/simulation/src/iob_div_subshift_frac_tb.v +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define CLK_FREQ (100000000) - -module iob_div_subshift_frac_tb; - - parameter clk_frequency = `CLK_FREQ; - parameter clk_period = 1e9/clk_frequency; //ns - parameter DATA_W = 8; - parameter TEST_SZ = 100; - - reg clk = 0; - reg arst = 0; - reg start = 0; - wire done; - - //data - reg [DATA_W-1:0] dividend [0:TEST_SZ-1]; - reg [DATA_W-1:0] divisor [0:TEST_SZ-1]; - reg [DATA_W-1:0] quotient [0:TEST_SZ-1]; - reg [DATA_W-1:0] remainder [0:TEST_SZ-1]; - - //core outputs - wire [DATA_W-1:0] quotient_out; - wire [DATA_W-1:0] remainder_out; - - integer i; - integer fp; - - initial begin - -`ifdef VCD - $dumpfile("div.vcd"); - $dumpvars(); -`endif - - // generate test data - for (i=0; i < TEST_SZ; i=i+1) begin -// dividend[i] = $random; -// divisor[i] = $random; - dividend[i] = 10; - divisor[i] = 3; - quotient[i] = dividend[i] / divisor[i]; - remainder[i] = dividend[i] % divisor[i]; - end - - //reset pulse - #100 arst = 1; - @(posedge clk) #1 arst = 0; - - //compute divisions - for (i=0; i < TEST_SZ; i=i+1) begin - //pulse start - @(posedge clk) #1 start = 1; - @(posedge clk) #1 start = 0; - - //wait for done - while(!done) - @(posedge clk) #1; - - //verify results - if(quotient_out != quotient[i] || remainder_out != remainder[i]) - $display ("%d / %d = %d with rem %d but got %d with rem %d", dividend[i], divisor[i], quotient[i], remainder[i], quotient_out, remainder_out); - else begin - fp = $fopen("test.log", "w"); - $fdisplay(fp, "Test passed!"); - end - - #1000; - - end - - #clk_period; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - #(5 * clk_period) $finish(); - - end - - //clock - always #(clk_period/2) clk = ~clk; - - //instantiate divider - iob_div_subshift_frac - # ( - .DATA_W(DATA_W) - ) - uut - ( - .clk_i(clk), - .arst_i(arst), - .cke_i(1'b1), - .start_i(start), - .done_o(done), - - .dividend_i(dividend[i]), - .divisor_i(divisor[i]), - .quotient_o(quotient_out), - .remainder_o(remainder_out) - ); - -endmodule diff --git a/lib/hardware/arith_logic/div/iob_div_subshift_frac/iob_div_subshift_frac.py b/lib/hardware/arith_logic/div/iob_div_subshift_frac/iob_div_subshift_frac.py deleted file mode 100755 index b8a83543a..000000000 --- a/lib/hardware/arith_logic/div/iob_div_subshift_frac/iob_div_subshift_frac.py +++ /dev/null @@ -1,204 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "dividend_i", - "descr": "Input port", - "signals": [ - { - "name": "dividend", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "divisor_i", - "descr": "Input port", - "signals": [ - { - "name": "divisor", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "status", - "descr": "", - "signals": [ - { - "name": "start", - "direction": "input", - "width": 1, - "descr": "Start signal", - }, - { - "name": "done", - "direction": "output", - "width": 1, - "descr": "Done signal", - }, - ], - }, - { - "name": "quotient_o", - "descr": "Output port", - "signals": [ - { - "name": "quotient", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - { - "name": "remainder_o", - "descr": "Output port", - "signals": [ - { - "name": "remainder", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "divisor_reg", - "descr": "divisor_reg wire", - "signals": [ - {"name": "divisor_reg", "width": "DATA_W"}, - ], - }, - { - "name": "quotient_int", - "descr": "quotient_int wire", - "signals": [ - {"name": "quotient_int", "width": "DATA_W"}, - ], - }, - { - "name": "incr", - "descr": "incr wire", - "signals": [ - {"name": "incr", "width": 1}, - ], - }, - { - "name": "res_acc", - "descr": "res_acc wire", - "signals": [ - {"name": "res_acc", "width": "DATA_W+1"}, - ], - }, - { - "name": "pc", - "descr": "pc wire", - "signals": [ - {"name": "pc", "width": 2}, - ], - }, - { - "name": "div_frac", - "descr": "Division interface", - "signals": [ - { - "name": "dividend", - }, - { - "name": "divisor", - }, - { - "name": "quotient_int", - }, - { - "name": "remainder", - }, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg", - "instance_name": "divisor_reg0", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "1'b0", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "divisor_i", - "data_o": "divisor_reg", - }, - }, - { - "core_name": "iob_div_subshift", - "instance_name": "div_subshift0", - "parameters": { - "DATA_W": "DATA_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "status": "status", - "div": "div_frac", - }, - }, - ], - "fsm": { - "verilog_code": """ -default_assignments: - incr = 1'b0; - quotient_o = quotient_int + incr; - res_acc_nxt = res_acc + remainder_o; - res_acc_en = 1'b0; - - if (!start_i) begin //wait for div start - pc_nxt = pc; - end - - if (!done_o) begin //wait for div done - pc_nxt = pc; - end - - res_acc_en = 1'b1; - if (res_acc_nxt >= divisor_i) begin - incr = 1'b1; - res_acc_nxt = res_acc + remainder_o - divisor_i; - end - if (!start_i) begin - pc_nxt = pc; - end - else begin - pc_nxt = 1'b1; - end -""", - }, - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/div/iob_div_subshift_signed/iob_div_subshift_signed.py b/lib/hardware/arith_logic/div/iob_div_subshift_signed/iob_div_subshift_signed.py deleted file mode 100644 index 1d4fdc70d..000000000 --- a/lib/hardware/arith_logic/div/iob_div_subshift_signed/iob_div_subshift_signed.py +++ /dev/null @@ -1,227 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "PC_W", - "type": "F", - "val": "$clog2(DATA_W + 5) + 1", - "min": "NA", - "max": "NA", - "descr": "PC width", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "en_i", - "descr": "Input port", - "signals": [ - { - "name": "en", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "sign_i", - "descr": "Input port", - "signals": [ - { - "name": "sign", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "dividend_i", - "descr": "Input port", - "signals": [ - { - "name": "dividend", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "divisor_i", - "descr": "Input port", - "signals": [ - { - "name": "divisor", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "done_o", - "descr": "Output port", - "signals": [ - { - "name": "done", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "quotient_o", - "descr": "Output port", - "signals": [ - { - "name": "quotient", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - { - "name": "remainder_o", - "descr": "Output port", - "signals": [ - { - "name": "remainder", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "rq", - "descr": "rq wire", - "signals": [ - {"name": "rq", "width": "2*DATA_W"}, - ], - }, - { - "name": "divisor_reg", - "descr": "divisor_reg wire", - "signals": [ - {"name": "divisor_reg", "width": "DATA_W"}, - ], - }, - { - "name": "divident_sign", - "descr": "divident_sign wire", - "signals": [ - {"name": "divident_sign", "width": 1}, - ], - }, - { - "name": "divisor_sign", - "descr": "divisor_sign wire", - "signals": [ - {"name": "divisor_sign", "width": 1}, - ], - }, - { - "name": "pc", - "descr": "pc wire", - "signals": [ - {"name": "pc", "width": "PC_W"}, - ], - }, - { - "name": "subtraend", - "descr": "subtraend wire", - "signals": [ - {"name": "subtraend", "width": "DATA_W"}, - ], - }, - { - "name": "tmp", - "descr": "tmp wire", - "signals": [ - {"name": "tmp", "width": "DATA_W+1"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - assign quotient_o = rq[DATA_W-1:0]; - assign remainder_o = rq[2*DATA_W-1:DATA_W]; - assign subtraend = rq[2*DATA_W-2-:DATA_W]; - - always @(posedge clk_i) begin - if (en_i) begin - pc <= pc + 1'b1; - - case (pc) - 0: begin //load operands and result sign - if (sign_i) begin - divisor_reg <= divisor_i; - divisor_sign <= divisor_i[DATA_W-1]; - rq[DATA_W-1:0] <= dividend_i[DATA_W-1] ? -dividend_i : dividend_i; - divident_sign <= dividend_i[DATA_W-1]; - end else begin - divisor_reg <= divisor_i; - divisor_sign <= 1'b0; - rq[DATA_W-1:0] <= dividend_i; - divident_sign <= 1'b0; - end - end // case: 0 - - 1: begin - if (sign_i) divisor_reg <= divisor_reg[DATA_W-1] ? -divisor_reg : divisor_reg; - end - - PC_W'(DATA_W + 2): begin - rq[DATA_W-1:0] <= (divident_sign^divisor_sign)? -{rq[DATA_W-2], rq[DATA_W-2 : 0]}: {rq[DATA_W-2], rq[DATA_W-2 : 0]}; - end - - PC_W'(DATA_W + 3): begin - done_o <= 1'b1; - rq[2*DATA_W-1:DATA_W] <= divident_sign? -rq[2*DATA_W-1 -: DATA_W] : rq[2*DATA_W-1 -: DATA_W]; - end - - PC_W'(DATA_W + 4): pc <= pc; - - default: begin //shift and subtract - tmp = {1'b0, subtraend} - {1'b0, divisor_reg}; - if (~tmp[DATA_W]) rq <= {tmp, rq[DATA_W-2 : 0], 1'b1}; - else rq <= {rq[2*DATA_W-1 : 0], 1'b0}; - end - endcase - - end else begin // if (en) - rq <= 0; - done_o <= 1'b0; - pc <= 0; - end - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_add/hardware/src/iob_add.v b/lib/hardware/arith_logic/iob_add/hardware/src/iob_add.v deleted file mode 100644 index 3b5f1a45b..000000000 --- a/lib/hardware/arith_logic/iob_add/hardware/src/iob_add.v +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_add2 - #( - parameter W = 21, - parameter N = 21 //must be at least 2 -) ( - input [N*W-1:0] in_i, - output [W-1:0] sum_o, - output carry_o -); - - wire [(N-1)*W:0] sum; - - genvar i; - generate - - endgenerate - - generate - //first adder - if (N==2) begin: g_N2 - iob_add2 #(.W(W)) - adder( - .in1_i(in_i[0 +: W]), - .in2_i(in_i[W +: W]), - .sum_o(sum_o), - .carry_o(carry_o) - ); - end else begin: g_N - iob_add2 #(.W(W)) - adder( - .in1_i(in_i[0 +: W]), - .in2_i(in_i[W +: (N-1)*W]), - .sum_o(sum[0 +: (N-1)*W]), - .carry_o() - ); - end - - //intermediate adders - if (N>3) begin: g_Ngt3 - for(i=1; i<(N-1); i=i+1) begin: adder - iob_add2 #(.W(W)) - adder( - .in1_i(in_i[i*W +: W]), - .in2_i(sum[(i-1)*W +: W]), - .sum_o(sum[i*W +: W]), - .carry_o() - ); - end - end - - //last adder - if (N>2) begin: g_Ngt3 - iob_add2 #(.W(W)) - adder( - .in1_i(in_i[(N-1)*W +: W]), - .in2_i(sum[(N-2)*W +: W]), - .sum_o(sum_o), - .carry_o(carry_o) - ); - - endgenerate - - -endmodule diff --git a/lib/hardware/arith_logic/iob_add/iob_add.py b/lib/hardware/arith_logic/iob_add/iob_add.py deleted file mode 100644 index 2fe2b52a3..000000000 --- a/lib/hardware/arith_logic/iob_add/iob_add.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_add2", - "instance_name": "iob_add2_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_add2/iob_add2.py b/lib/hardware/arith_logic/iob_add2/iob_add2.py deleted file mode 100644 index eb08cd373..000000000 --- a/lib/hardware/arith_logic/iob_add2/iob_add2.py +++ /dev/null @@ -1,85 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "in1_i", - "descr": "Input port", - "signals": [ - { - "name": "in1", - "width": "W", - "direction": "input", - }, - ], - }, - { - "name": "in2_i", - "descr": "Input port", - "signals": [ - { - "name": "in2", - "width": "W", - "direction": "input", - }, - ], - }, - { - "name": "sum_o", - "descr": "Output port", - "signals": [ - { - "name": "sum", - "width": "W", - "direction": "output", - }, - ], - }, - { - "name": "carry_o", - "descr": "Output port", - "signals": [ - { - "name": "carry", - "width": 1, - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "sum_int", - "descr": "sum wire", - "signals": [ - {"name": "sum_int", "width": "W+1"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": f""" - assign sum_int = in1_i + in2_i; - assign sum_o = sum_int[W-1:0]; - assign carry_o = sum_int[W]; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_ctls/hardware/simulation/src/iob_ctls_tb.v b/lib/hardware/arith_logic/iob_ctls/hardware/simulation/src/iob_ctls_tb.v deleted file mode 100644 index 9a41a9135..000000000 --- a/lib/hardware/arith_logic/iob_ctls/hardware/simulation/src/iob_ctls_tb.v +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -module iob_ctls_tb; - - localparam W = 8; - - reg [ W-1:0] data_i = 1; - wire [$clog2(W):0] data_o; - - integer i; - integer fd; - - initial begin -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - for (i = 0; i < 2 ** W; i = i + 1) begin - #10 data_i = i; - end - #10 - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end - - iob_ctls #( - .W(W) - ) iob_ctls_inst ( - .data_i (data_i), - .count_o(data_o) - ); - -endmodule - diff --git a/lib/hardware/arith_logic/iob_ctls/hardware/src/iob_ctls.v b/lib/hardware/arith_logic/iob_ctls/hardware/src/iob_ctls.v deleted file mode 100644 index 0a08404d9..000000000 --- a/lib/hardware/arith_logic/iob_ctls/hardware/src/iob_ctls.v +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_ctls #( - parameter W = 21, - parameter MODE = 0, //trailing (0), leading (1) - parameter SYMBOL = 0 //search zeros (0), search ones (1) -) ( - input [ W-1:0] data_i, - output [$clog2(W):0] count_o -); - - //invert if searching zeros or not - wire [W-1:0] data_int1; - generate - if (SYMBOL == 0) begin : g_zeros - assign data_int1 = data_i; - end else begin : g_ones - assign data_int1 = ~data_i; - end - endgenerate - - // reverse if leading symbols or not - wire [W-1:0] data_int2; - generate - if (MODE == 1) begin : g_reverse - iob_reverse #(W) reverse0 ( - .data_i(data_int1), - .data_o(data_int2) - ); - end else begin : g_noreverse - assign data_int2 = data_int1; - end - endgenerate - - //count trailing zeros - iob_prio_enc #( - .W (W + 1), - .MODE("LOW") - ) prio_encoder0 ( - .unencoded_i({1'b1, data_int2}), - .encoded_o (count_o) - ); - -endmodule diff --git a/lib/hardware/arith_logic/iob_ctls/iob_ctls.py b/lib/hardware/arith_logic/iob_ctls/iob_ctls.py deleted file mode 100644 index e78e00dd4..000000000 --- a/lib/hardware/arith_logic/iob_ctls/iob_ctls.py +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_reverse", - "instance_name": "iob_reverse_inst", - }, - { - "core_name": "iob_prio_enc", - "instance_name": "iob_prio_enc_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_diff/hardware/src/iob_diff.v b/lib/hardware/arith_logic/iob_diff/hardware/src/iob_diff.v deleted file mode 100644 index cd3a7a91a..000000000 --- a/lib/hardware/arith_logic/iob_diff/hardware/src/iob_diff.v +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_diff #( - parameter DATA_W = 32, - parameter RST_VAL = 0 -) ( - `include "iob_diff_clk_en_rst_s_port.vs" - - input rst_i, - - input [DATA_W-1:0] data_i, - output [DATA_W-1:0] data_o -); - - wire [DATA_W-1:0] data_i_reg; - iob_reg_r #(DATA_W, RST_VAL) reg0 ( - `include "iob_diff_clk_en_rst_s_s_portmap.vs" - - .rst_i(rst_i), - - .data_i(data_i), - .data_o(data_i_reg) - ); - - assign data_o = data_i - data_i_reg; - -endmodule diff --git a/lib/hardware/arith_logic/iob_diff/iob_diff.py b/lib/hardware/arith_logic/iob_diff/iob_diff.py deleted file mode 100644 index 20fc7a1f6..000000000 --- a/lib/hardware/arith_logic/iob_diff/iob_diff.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_reg_r", - "instance_name": "iob_reg_r_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_edge_detect/hardware/src/iob_edge_detect.v b/lib/hardware/arith_logic/iob_edge_detect/hardware/src/iob_edge_detect.v deleted file mode 100644 index c8185d823..000000000 --- a/lib/hardware/arith_logic/iob_edge_detect/hardware/src/iob_edge_detect.v +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_edge_detect #( - parameter EDGE_TYPE = "rising", // "rising", "falling", "both" - parameter OUT_TYPE = "step" // "step", "pulse" -) ( - `include "iob_edge_detect_clk_en_rst_s_port.vs" - input rst_i, - input bit_i, - output detected_o -); - wire bit_int; - wire bit_int_q; - - generate - if (EDGE_TYPE == "falling") begin : gen_falling - assign bit_int = ~bit_i; - iob_reg_r #( - .DATA_W (1), - .RST_VAL(1'b0) - ) bit_reg ( - `include "iob_edge_detect_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(bit_int), - .data_o(bit_int_q) - ); - - end else begin : gen_default_rising - assign bit_int = bit_i; - - iob_reg_r #( - .DATA_W (1), - .RST_VAL(1'b1) - ) bit_reg ( - `include "iob_edge_detect_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(bit_int), - .data_o(bit_int_q) - ); - end - endgenerate - - generate - if (OUT_TYPE == "pulse") begin : gen_pulse - assign detected_o = bit_int & ~bit_int_q; - end else begin : gen_step - wire detected_prev; - iob_reg_r #( - .DATA_W(1) - ) detected_reg ( - `include "iob_edge_detect_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(detected_o), - .data_o(detected_prev) - ); - assign detected_o = detected_prev | (bit_int & ~bit_int_q); - end - endgenerate - -endmodule diff --git a/lib/hardware/arith_logic/iob_edge_detect/iob_edge_detect.py b/lib/hardware/arith_logic/iob_edge_detect/iob_edge_detect.py deleted file mode 100644 index cd9d50bfd..000000000 --- a/lib/hardware/arith_logic/iob_edge_detect/iob_edge_detect.py +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - # TODO: Remaining ports - ], - "blocks": [ - { - "core_name": "iob_reg_r", - "instance_name": "iob_reg_r_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_add/hardware/src/iob_fp_add.v b/lib/hardware/arith_logic/iob_fp/iob_fp_add/hardware/src/iob_fp_add.v deleted file mode 100755 index 7a21657c2..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_add/hardware/src/iob_fp_add.v +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -// Canonical NAN -`define NAN {1'b0, {EXP_W{1'b1}}, 1'b1, {(DATA_W-EXP_W-2){1'b0}}} - -// Infinite -`define INF(SIGN) {SIGN, {EXP_W{1'b1}}, {(DATA_W-EXP_W-1){1'b0}}} - -module iob_fp_add #( - parameter DATA_W = 32, - parameter EXP_W = 8 - ) - ( - input clk_i, - input rst_i, - - input start_i, - output reg done_o, - - input [DATA_W-1:0] op_a_i, - input [DATA_W-1:0] op_b_i, - - output overflow_o, - output underflow_o, - output exception_o, - - output reg [DATA_W-1:0] res_o - ); - - localparam MAN_W = DATA_W-EXP_W; - localparam BIAS = 2**(EXP_W-1)-1; - localparam EXTRA = 3; - localparam STICKY_BITS = 2*BIAS-1; - - // Special cases -`ifdef SPECIAL_CASES - wire op_a_nan, op_a_inf, op_a_zero, op_a_sub; - iob_fp_special #( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - special_op_a - ( - .data_i (op_a_i), - - .nan_o (op_a_nan), - .infinite_o (op_a_inf), - .zero_o (op_a_zero), - .sub_normal_o (op_a_sub) - ); - - wire op_b_nan, op_b_inf, op_b_zero, op_b_sub; - fp_special #( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - special_op_b - ( - .data_i (op_b_i), - - .nan_o (op_b_nan), - .infinite_o (op_b_inf), - .zero_o (op_b_zero), - .sub_normal_o (op_b_sub) - ); - - wire special = op_a_nan | op_a_inf | op_b_nan | op_b_inf; - wire [DATA_W-1:0] res_special = (op_a_nan | op_b_nan)? `NAN: - (op_a_inf & op_b_inf)? ((op_a_i[DATA_W-1] ^ op_b_i[DATA_W-1])? `NAN: - `INF(op_a_i[DATA_W-1])): - op_b_inf? ((op_a_i[DATA_W-1] ^ op_b_i[DATA_W-1])? `INF(~op_b_i[DATA_W-1]): - `INF(op_b_i[DATA_W-1])): - `INF(op_a_i[DATA_W-1]); -`endif - - // Unpack - //wire comp = (op_a_i[DATA_W-2 -: EXP_W] >= op_b_i[DATA_W-2 -: EXP_W])? 1'b1 : 1'b0; - - reg comp; - always @* - begin - comp = 1'b0; - if(op_a_i[DATA_W-2 -: EXP_W] == op_b_i[DATA_W-2 -: EXP_W]) begin - if(op_a_i[MAN_W-2:0] > op_b_i[MAN_W-2:0]) - comp = 1'b1; - end - if(op_a_i[DATA_W-2 -: EXP_W] > op_b_i[DATA_W-2 -: EXP_W]) - comp = 1'b1; - end - - wire [MAN_W-1:0] A_Mantissa = comp? {1'b1, op_a_i[MAN_W-2:0]} : {1'b1, op_b_i[MAN_W-2:0]}; - wire [EXP_W-1:0] A_Exponent = comp? op_a_i[DATA_W-2 -: EXP_W] : op_b_i[DATA_W-2 -: EXP_W]; - wire A_sign = comp? op_a_i[DATA_W-1] : op_b_i[DATA_W-1]; - - wire [MAN_W-1:0] B_Mantissa = comp? {1'b1, op_b_i[MAN_W-2:0]} : {1'b1, op_a_i[MAN_W-2:0]}; - wire [EXP_W-1:0] B_Exponent = comp? op_b_i[DATA_W-2 -: EXP_W] : op_a_i[DATA_W-2 -: EXP_W]; - wire B_sign = comp? op_b_i[DATA_W-1] : op_a_i[DATA_W-1]; - - // pipeline stage 1 - reg A_sign_reg; - reg [EXP_W-1:0] A_Exponent_reg; - reg [MAN_W-1:0] A_Mantissa_reg; - - reg B_sign_reg; - reg [EXP_W-1:0] B_Exponent_reg; - reg [MAN_W-1:0] B_Mantissa_reg; - - reg done_int; - always @(posedge clk_i) begin - if (rst_i) begin - A_sign_reg <= 1'b0; - A_Exponent_reg <= {EXP_W{1'b0}}; - A_Mantissa_reg <= {MAN_W{1'b0}}; - - B_sign_reg <= 1'b0; - B_Exponent_reg <= {EXP_W{1'b0}}; - B_Mantissa_reg <= {MAN_W{1'b0}}; - - done_int <= 1'b0; - end else begin - A_sign_reg <= A_sign; - A_Exponent_reg <= A_Exponent; - A_Mantissa_reg <= A_Mantissa; - - B_sign_reg <= B_sign; - B_Exponent_reg <= B_Exponent; - B_Mantissa_reg <= B_Mantissa; - - done_int <= start_i; - end - end - - // Align significants - wire [EXP_W-1:0] diff_Exponent = A_Exponent_reg - B_Exponent_reg; - wire [MAN_W+STICKY_BITS-1:0] B_Mantissa_in = {B_Mantissa_reg, {STICKY_BITS{1'b0}}} >> diff_Exponent; - - // Extra bits - wire guard_bit = B_Mantissa_in[STICKY_BITS-1]; - wire round_bit = B_Mantissa_in[STICKY_BITS-2]; - wire sticky_bit = |B_Mantissa_in[STICKY_BITS-3:0]; - - // pipeline stage 2 - reg A_sign_reg2; - reg [EXP_W-1:0] A_Exponent_reg2; - reg [MAN_W-1:0] A_Mantissa_reg2; - - reg B_sign_reg2; - reg [MAN_W-1:0] B_Mantissa_reg2; - - reg guard_bit_reg; - reg round_bit_reg; - reg sticky_bit_reg; - - reg done_int2; - always @(posedge clk_i) begin - if (rst_i) begin - A_sign_reg2 <= 1'b0; - A_Exponent_reg2 <= {EXP_W{1'b0}}; - A_Mantissa_reg2 <= {MAN_W{1'b0}}; - - B_sign_reg2 <= 1'b0; - B_Mantissa_reg2 <= {MAN_W{1'b0}}; - - guard_bit_reg <= 1'b0; - round_bit_reg <= 1'b0; - sticky_bit_reg <= 1'b0; - - done_int2 <= 1'b0; - end else begin - A_sign_reg2 <= A_sign_reg; - A_Exponent_reg2 <= A_Exponent_reg; - A_Mantissa_reg2 <= A_Mantissa_reg; - - B_sign_reg2 <= B_sign_reg; - B_Mantissa_reg2 <= B_Mantissa_in[STICKY_BITS +: MAN_W]; - - guard_bit_reg <= guard_bit; - round_bit_reg <= round_bit; - sticky_bit_reg <= sticky_bit; - - done_int2 <= done_int; - end - end - - // Addition - wire [MAN_W:0] Temp = (A_sign_reg2 ^ B_sign_reg2)? A_Mantissa_reg2 - B_Mantissa_reg2 : A_Mantissa_reg2 + B_Mantissa_reg2; - wire carry = Temp[MAN_W]; - - // pipeline stage 3 - reg A_sign_reg3; - reg [EXP_W-1:0] A_Exponent_reg3; - - reg [MAN_W+EXTRA-1:0] Temp_reg; - reg carry_reg; - - reg done_int3; - always @(posedge clk_i) begin - if (rst_i) begin - A_sign_reg3 <= 1'b0; - A_Exponent_reg3 <= {EXP_W{1'b0}}; - - Temp_reg <= {(MAN_W+EXTRA){1'b0}}; - carry_reg <= 1'b0; - - done_int3 <= 1'b0; - end else begin - A_sign_reg3 <= A_sign_reg2; - A_Exponent_reg3 <= A_Exponent_reg2; - - Temp_reg <= {Temp[MAN_W-1:0], guard_bit_reg, round_bit_reg, sticky_bit_reg}; - carry_reg <= carry; - - done_int3 <= done_int2; - end - end - - // Normalize - wire [$clog2(MAN_W+EXTRA+1)-1:0] lzc; - iob_fp_clz #( - .DATA_W(MAN_W+EXTRA) - ) - clz0 - ( - .data_i (Temp_reg), - .data_o (lzc) - ); - - wire [MAN_W+EXTRA-1:0] Temp_Mantissa = carry_reg? {1'b1, Temp_reg[MAN_W+EXTRA-1:1]} : Temp_reg << lzc; - wire [EXP_W-1:0] exp_adjust = carry_reg? A_Exponent_reg3 + 1'b1 : A_Exponent_reg3 - {{EXTRA{1'b0}},lzc}; - - // pipeline stage 4 - reg A_sign_reg4; - - reg [EXP_W-1:0] exp_adjust_reg; - reg [MAN_W+EXTRA-1:0] Temp_Mantissa_reg; - - reg done_int4; - always @(posedge clk_i) begin - if (rst_i) begin - A_sign_reg4 <= 1'b0; - - exp_adjust_reg <= {EXP_W{1'b0}}; - Temp_Mantissa_reg <= {(MAN_W+EXTRA){1'b0}}; - - done_int4 <= 1'b0; - end else begin - A_sign_reg4 <= A_sign_reg3; - - exp_adjust_reg <= exp_adjust; - Temp_Mantissa_reg <= {Temp_Mantissa[MAN_W+EXTRA-1:1], Temp_Mantissa[0] | Temp_reg[0]}; - - done_int4 <= done_int3; - end - end - - // Round - wire [MAN_W-1:0] Temp_Mantissa_rnd; - wire [EXP_W-1:0] exp_adjust_rnd; - iob_fp_round #( - .DATA_W (MAN_W), - .EXP_W (EXP_W) - ) - round0 - ( - .exponent_i (exp_adjust_reg), - .mantissa_i (Temp_Mantissa_reg), - - .exponent_rnd_o (exp_adjust_rnd), - .mantissa_rnd_o (Temp_Mantissa_rnd) - ); - - // Pack - wire [MAN_W-2:0] Mantissa = Temp_Mantissa_rnd[MAN_W-2:0]; - wire [EXP_W-1:0] Exponent = exp_adjust_rnd; - wire Sign = A_sign_reg4; - -`ifdef SPECIAL_CASES - wire [DATA_W-1:0] res_in = special? res_special: {Sign, Exponent, Mantissa}; - wire done_in = special? start_i: done_int4; -`else - wire [DATA_W-1:0] res_in = {Sign, Exponent, Mantissa}; - wire done_in = done_int4; -`endif - - // pipeline stage 5 - always @(posedge clk_i) begin - if (rst_i) begin - res_o <= {DATA_W{1'b0}}; - done_o <= 1'b0; - end else begin - res_o <= res_in; - done_o <= done_in; - end - end - - // Not implemented yet! - assign overflow_o = 1'b0; - assign underflow_o = 1'b0; - assign exception_o = 1'b0; - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_add/iob_fp_add.py b/lib/hardware/arith_logic/iob_fp/iob_fp_add/iob_fp_add.py deleted file mode 100755 index 5e51fefd8..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_add/iob_fp_add.py +++ /dev/null @@ -1,132 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "EXP_W", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "Exponent width", - }, - { - "name": "MAN_W", - "type": "F", - "val": "DATA_W-EXP_W", - "min": "NA", - "max": "NA", - "descr": "MAN width", - }, - { - "name": "BIAS", - "type": "F", - "val": "2**(EXP_W-1)-1", - "min": "NA", - "max": "NA", - "descr": "BIAS", - }, - { - "name": "EXTRA", - "type": "F", - "val": "3", - "min": "NA", - "max": "NA", - "descr": "EXTRA", - }, - { - "name": "STICKY_BITS", - "type": "F", - "val": "2*BIAS-1", - "min": "NA", - "max": "NA", - "descr": "STICKY_BITS", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Input port", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "start_i", - "descr": "Input port", - "signals": [ - { - "name": "start", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "op_a_i", - "descr": "Input port", - "signals": [ - { - "name": " op_a", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - { - "name": "data", - "width": "$clog2(DATA_W+1)", - "direction": "output", - }, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_fp_special", - "instance_name": "iob_fp_special_inst", - }, - { - "core_name": "iob_fp_clz", - "instance_name": "iob_fp_clz_inst", - }, - { - "core_name": "iob_fp_round", - "instance_name": "iob_fp_round_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_clz/iob_fp_clz.py b/lib/hardware/arith_logic/iob_fp/iob_fp_clz/iob_fp_clz.py deleted file mode 100755 index ee56e4c9e..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_clz/iob_fp_clz.py +++ /dev/null @@ -1,69 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "BIT_W", - "type": "F", - "val": "$clog2(DATA_W+1)", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "data_i", - "descr": "Input port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - { - "name": "data", - "width": "$clog2(DATA_W+1)", - "direction": "output", - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - integer i; - - always @* begin - data_o = DATA_W[BIT_W-1:0]; - for (i=0; i < DATA_W; i=i+1) begin - if (data_i[i]) begin - data_o = (DATA_W[BIT_W-1:0] - i[BIT_W-1:0] - 1); - end - end - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_cmp/iob_fp_cmp.py b/lib/hardware/arith_logic/iob_fp/iob_fp_cmp/iob_fp_cmp.py deleted file mode 100755 index efe6373ba..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_cmp/iob_fp_cmp.py +++ /dev/null @@ -1,177 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "EXP_W", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Input port", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "start_i", - "descr": "Input port", - "signals": [ - { - "name": "start", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "fn_i", - "descr": "Input port", - "signals": [ - { - "name": "fn", - "width": 2, - "direction": "input", - }, - ], - }, - { - "name": "op_a_i", - "descr": "Input port", - "signals": [ - { - "name": "op_a", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "op_b_i", - "descr": "Input port", - "signals": [ - { - "name": "op_b", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "done_o", - "descr": "Output port", - "signals": [ - { - "name": "done", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "res_o", - "descr": "Output port", - "signals": [ - { - "name": "res", - "width": 1, - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "equal_int", - "descr": "equal wire", - "signals": [ - {"name": "equal_int", "width": 1}, - ], - }, - { - "name": "less_int", - "descr": "less wire", - "signals": [ - {"name": "less_int", "width": 1}, - ], - }, - { - "name": "op_a_nan_int", - "descr": "op_a_nan wire", - "signals": [ - {"name": "op_a_nan_int", "width": 1}, - ], - }, - { - "name": "op_b_nan_int", - "descr": "op_b_nan wire", - "signals": [ - {"name": "op_b_nan_int", "width": 1}, - ], - }, - { - "name": "res_int", - "descr": "res wire", - "signals": [ - {"name": "res_int", "width": 1}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - assign equal_int = (op_a_i == op_b_i)? 1'b1: 1'b0; - assign less_int = (op_a_i[DATA_W-1] ^ op_b_i[DATA_W-1])? (op_a_i[DATA_W-1]? 1'b1: 1'b0):op_a_i[DATA_W-1]? ((op_a_i[DATA_W-2:0] > op_b_i[DATA_W-2:0])? 1'b1: 1'b0):((op_a_i[DATA_W-2:0] > op_b_i[DATA_W-2:0])? 1'b0: 1'b1); - assign op_a_nan_int = &op_a_i[DATA_W-2 -: EXP_W] & |op_a_i[DATA_W-EXP_W-2:0]; - assign op_b_nan_int = &op_b_i[DATA_W-2 -: EXP_W] & |op_b_i[DATA_W-EXP_W-2:0]; - assign res_int = (op_a_nan_int | op_b_nan_int)? 1'b0:fn_i[1]? equal_int:fn_i[0]? less_int:less_int|equal_int; - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - res_o <= 1'b0; - done_o <= 1'b0; - end else begin - res_o <= res_int; - done_o <= start_i; - end - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_div/hardware/src/iob_fp_div.v b/lib/hardware/arith_logic/iob_fp/iob_fp_div/hardware/src/iob_fp_div.v deleted file mode 100755 index 285f68088..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_div/hardware/src/iob_fp_div.v +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -// Canonical NAN -`define NAN {1'b0, {EXP_W{1'b1}}, 1'b1, {(DATA_W-EXP_W-2){1'b0}}} - -// Infinite -`define INF(SIGN) {SIGN, {EXP_W{1'b1}}, {(DATA_W-EXP_W-1){1'b0}}} - -`define SPECIAL_CASES - -module iob_fp_div #( - parameter DATA_W = 32, - parameter EXP_W = 8 - ) - ( - input clk_i, - input rst_i, - - input start_i, - output reg done_o, - - input [DATA_W-1:0] op_a_i, - input [DATA_W-1:0] op_b_i, - - output overflow_o, - output underflow_o, - output exception_o, - - output reg [DATA_W-1:0] res_o - ); - - localparam MAN_W = DATA_W-EXP_W; - localparam BIAS = 2**(EXP_W-1)-1; - localparam EXTRA = 3; - - localparam END_COUNT = 2*MAN_W+EXTRA+3-1; // divider cycle count (2*MAN_W+EXTRA+1) + pipeline stages - 1 - - reg [$clog2(END_COUNT+1)-1:0] counter; - wire cnt_done = (counter == END_COUNT)? 1'b1: 1'b0; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - counter <= END_COUNT; - end else if (start_i) begin - counter <= 0; - end else if (~cnt_done) begin - counter <= counter + 1'b1; - end - end - - // Unpack - wire [MAN_W-1:0] A_Mantissa = {1'b1, op_a_i[MAN_W-2:0]}; - wire [EXP_W-1:0] A_Exponent = op_a_i[DATA_W-2 -: EXP_W]; - wire A_sign = op_a_i[DATA_W-1]; - - wire [MAN_W-1:0] B_Mantissa = {1'b1, op_b_i[MAN_W-2:0]}; - wire [EXP_W-1:0] B_Exponent = op_b_i[DATA_W-2 -: EXP_W]; - wire B_sign = op_b_i[DATA_W-1]; - - // pipeline stage 1 - reg A_sign_reg; - reg [EXP_W-1:0] A_Exponent_reg; - reg [MAN_W-1:0] A_Mantissa_reg; - - reg B_sign_reg; - reg [EXP_W-1:0] B_Exponent_reg; - reg [MAN_W-1:0] B_Mantissa_reg; - - reg done_int; - always @(posedge clk_i) begin - if (rst_i) begin - A_sign_reg <= 1'b0; - A_Exponent_reg <= {EXP_W{1'b0}}; - A_Mantissa_reg <= {MAN_W{1'b0}}; - - B_sign_reg <= 1'b0; - B_Exponent_reg <= {EXP_W{1'b0}}; - B_Mantissa_reg <= {MAN_W{1'b0}}; - - done_int <= 1'b0; - end else begin - A_sign_reg <= A_sign; - A_Exponent_reg <= A_Exponent; - A_Mantissa_reg <= A_Mantissa; - - B_sign_reg <= B_sign; - B_Exponent_reg <= B_Exponent; - B_Mantissa_reg <= B_Mantissa; - - done_int <= start_i; - end - end - - // Special cases -`ifdef SPECIAL_CASES - wire [DATA_W-1:0] op_a_reg = {A_sign_reg,A_Exponent_reg,A_Mantissa_reg[MAN_W-2:0]}; - wire op_a_nan, op_a_inf, op_a_zero, op_a_sub; - iob_fp_special #( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - special_op_a - ( - .data_i (op_a_reg), - - .nan_o (op_a_nan), - .infinite_o (op_a_inf), - .zero_o (op_a_zero), - .sub_normal_o (op_a_sub) - ); - - wire [DATA_W-1:0] op_b_reg = {B_sign_reg,B_Exponent_reg,B_Mantissa_reg[MAN_W-2:0]}; - wire op_b_nan, op_b_inf, op_b_zero, op_b_sub; - iob_fp_special #( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - special_op_b - ( - .data_i (op_b_reg), - - .nan_o (op_b_nan), - .infinite_o (op_b_inf), - .zero_o (op_b_zero), - .sub_normal_o (op_b_sub) - ); - - wire special = op_a_nan | op_a_zero | op_a_inf | op_b_nan | op_b_inf | op_b_zero; - reg [DATA_W-1:0] res_special; - - always @* - begin - res_special = `NAN; // Majority of special cases are NAN - - if(op_b_inf & !op_a_inf) - res_special = 0; - if(op_a_zero) begin - if(op_b_zero) - res_special = `INF(op_a_i[DATA_W-1] ^ op_b_i[DATA_W-1]); - else - res_special = 0; - end - end - -`endif - - // Division - wire Temp_sign = A_sign_reg ^ B_sign_reg; - wire [EXP_W-1:0] Temp_Exponent = A_Exponent_reg - B_Exponent_reg + BIAS; - wire [2*MAN_W+EXTRA-1:0] Temp_Mantissa; // = A_Mantissa_reg / B_Mantissa_reg; - iob_div_subshift # ( - .DATA_W(2*MAN_W+EXTRA) - ) - div_subshift ( - .clk_i (clk_i), - .cke_i (1'b1), - //.rst_i (rst_i), - .arst_i (1'b0), - - .start_i (start_i), - .done_o (), - - .dividend_i ({1'b0, A_Mantissa, {(MAN_W+EXTRA-1){1'b0}}}), - .divisor_i ({{(MAN_W+EXTRA){1'b0}}, B_Mantissa}), - .quotient_o (Temp_Mantissa), - .remainder_o () - ); - - // pipeline stage 2 - reg Temp_sign_reg; - reg [EXP_W-1:0] Temp_Exponent_reg; - reg [MAN_W+EXTRA-1:0] Temp_Mantissa_reg; - - reg done_int2; - always @(posedge clk_i) begin - if (rst_i) begin - Temp_sign_reg <= 1'b0; - Temp_Exponent_reg <= {EXP_W{1'b0}}; - Temp_Mantissa_reg <= {(MAN_W+EXTRA){1'b0}}; - - done_int2 <= 1'b0; - end else begin - Temp_sign_reg <= Temp_sign; - Temp_Exponent_reg <= Temp_Exponent; - Temp_Mantissa_reg <= Temp_Mantissa[MAN_W+EXTRA-1:0]; - - done_int2 <= done_int; - end - end - - // Normalize - wire [$clog2(MAN_W+EXTRA+1)-1:0] lzc; - iob_fp_clz #( - .DATA_W(MAN_W+EXTRA) - ) - clz0 - ( - .data_i (Temp_Mantissa_reg), - .data_o (lzc) - ); - - wire [MAN_W+EXTRA-1:0] Mantissa_int = Temp_Mantissa_reg << lzc; - wire [EXP_W-1:0] Exponent_int = Temp_Exponent_reg - {{EXTRA{1'b0}},lzc}; - - // pipeline stage 3 - reg Temp_sign_reg2; - - reg [EXP_W-1:0] Exponent_reg; - reg [MAN_W+EXTRA-1:0] Mantissa_reg; - - reg done_int3; - always @(posedge clk_i) begin - if (rst_i) begin - Temp_sign_reg2 <= 1'b0; - - Exponent_reg <= {EXP_W{1'b0}}; - Mantissa_reg <= {(MAN_W+EXTRA){1'b0}}; - - done_int3 <= 1'b0; - end else begin - Temp_sign_reg2 <= Temp_sign_reg; - - Exponent_reg <= Exponent_int; - Mantissa_reg <= {Mantissa_int[MAN_W+EXTRA-1:1], Temp_Mantissa_reg[0]}; - - done_int3 <= done_int2; - end - end - - // Round - wire [MAN_W-1:0] Mantissa_rnd; - wire [EXP_W-1:0] Exponent_rnd; - iob_fp_round #( - .DATA_W (MAN_W), - .EXP_W (EXP_W) - ) - round0 - ( - .exponent_i (Exponent_reg), - .mantissa_i (Mantissa_reg), - - .exponent_rnd_o (Exponent_rnd), - .mantissa_rnd_o (Mantissa_rnd) - ); - - // Pack - wire [MAN_W-2:0] Mantissa = Mantissa_rnd[MAN_W-2:0]; - wire [EXP_W-1:0] Exponent = Exponent_rnd; - wire Sign = Temp_sign_reg2; - -`ifdef SPECIAL_CASES - wire [DATA_W-1:0] res_in = special? res_special: {Sign, Exponent, Mantissa}; - wire done_in = special? start_i: (~start_i & cnt_done); -`else - wire [DATA_W-1:0] res_in = {Sign, Exponent, Mantissa}; - wire done_in = ~start_i & cnt_done; -`endif - - // pipeline stage 4 - always @(posedge clk_i) begin - if (rst_i) begin - res_o <= {DATA_W{1'b0}}; - done_o <= 1'b0; - end else begin - res_o <= res_in; - done_o <= done_in; - end - end - - // Not implemented yet! - assign overflow_o = 1'b0; - assign underflow_o = 1'b0; - assign exception_o = 1'b0; - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_div/iob_fp_div.py b/lib/hardware/arith_logic/iob_fp/iob_fp_div/iob_fp_div.py deleted file mode 100755 index d079ca33e..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_div/iob_fp_div.py +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_fp_special", - "instance_name": "iob_fp_special_inst", - }, - { - "core_name": "iob_div_subshift", - "instance_name": "iob_div_subshift_inst", - }, - { - "core_name": "iob_fp_clz", - "instance_name": "iob_fp_clz_inst", - }, - { - "core_name": "iob_fp_round", - "instance_name": "iob_fp_round_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_dq/iob_fp_dq.py b/lib/hardware/arith_logic/iob_fp/iob_fp_dq/iob_fp_dq.py deleted file mode 100755 index debcfa04b..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_dq/iob_fp_dq.py +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "WIDTH", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "width", - }, - { - "name": "DEPTH", - "type": "P", - "val": 2, - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Input port", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "d_i", - "descr": "Input port", - "signals": [ - { - "name": "d", - "width": "WIDTH", - "direction": "input", - }, - ], - }, - { - "name": "q_o", - "descr": "Output port", - "signals": [ - { - "name": "q", - "width": "WIDTH", - "direction": "output", - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - integer i; - integer j; - reg [WIDTH-1:0] delay_line [DEPTH-1:0]; - always @(posedge clk_i,posedge rst_i) begin - if(rst_i) begin - for (i=0; i < DEPTH; i=i+1) begin - delay_line[i] <= 0; - end - end else begin - delay_line[0] <= d_i; - for (j=1; j < DEPTH; j=j+1) begin - delay_line[j] <= delay_line[j-1]; - end - end - end - - assign q_o = delay_line[DEPTH-1]; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_float2int/hardware/src/iob_fp_float2int.v b/lib/hardware/arith_logic/iob_fp/iob_fp_float2int/hardware/src/iob_fp_float2int.v deleted file mode 100755 index 03a65d16c..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_float2int/hardware/src/iob_fp_float2int.v +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -//This file was automatically generated by the python 'pipeliner' script -//This module has a latency of 2 clocks -`timescale 1ns / 1ps - -module iob_fp_float2int - ( - input clk_i, - input rst_i, - - input start_i, - output done_o, - - input [31:0] op_i, - output [31:0] res_o - ); - - localparam END_COUNT = 1; - - reg [3:0] counter; - assign done_o = (counter == END_COUNT)? 1'b1: 1'b0; - always @(posedge clk_i, posedge rst) begin - if (rst) begin - counter <= END_COUNT; - end else if (start_i) begin - counter <= 0; - end else if (~done_o) begin - counter <= counter + 1'b1; - end - end - - wire [31:0] s_0; - wire [31:0] s_1; - wire [31:0] s_2; - wire [31:0] s_3; - wire [31:0] s_4; - wire [31:0] s_5; - wire [23:0] s_6; - wire [0:0] s_7; - wire [0:0] s_8; - wire [0:0] s_9; - wire [0:0] s_10; - wire [7:0] s_11; - wire [7:0] s_12; - wire [31:0] s_13; - wire [6:0] s_14; - wire [7:0] s_15; - wire [22:0] s_16; - wire [7:0] s_17; - wire [7:0] s_18; - wire [7:0] s_19; - wire [7:0] s_20; - wire [7:0] s_21; - wire [7:0] s_22; - wire [0:0] s_23; - wire [31:0] s_24; - wire [0:0] s_25; - wire [0:0] s_26; - - assign s_0 = s_25?s_1:s_24; - iob_fp_dq #(32, 1) dq_s_1 (clk_i, rst_i, s_1, s_2); - assign s_2 = -s_3; - iob_fp_dq #(32, 1) dq_s_3 (clk_i, rst_i, s_3, s_4); - assign s_4 = s_5 >> s_18; - assign s_5 = {s_6,s_17}; - assign s_6 = {s_7,s_16}; - assign s_7 = s_10?s_8:s_9; - assign s_8 = 1'd0; - assign s_9 = 1'd1; - assign s_10 = s_11 == s_15; - assign s_11 = s_12 - s_14; - assign s_12 = s_13[30:23]; - assign s_13 = op_i; - assign s_14 = 7'd127; - assign s_15 = -8'd127; - assign s_16 = s_13[22:0]; - assign s_17 = 8'd0; - assign s_18 = s_19 - {7'h0,s_23}; - assign s_19 = s_20 - s_21; - assign s_20 = 8'd32; - assign s_21 = s_10?s_22:s_11; - assign s_22 = -8'd126; - assign s_23 = 1'd1; - iob_fp_dq #(32, 1) dq_s_24 (clk_i, rst_i, s_24, s_3); - iob_fp_dq #(1, 2) dq_s_25 (clk_i, rst_i, s_25, s_26); - assign s_26 = s_13[31]; - assign res_o = s_0; - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_float2int/iob_fp_float2int.py b/lib/hardware/arith_logic/iob_fp/iob_fp_float2int/iob_fp_float2int.py deleted file mode 100755 index fc3da87ab..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_float2int/iob_fp_float2int.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_fp_dq", - "instance_name": "iob_fp_dq_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/hardware/src/iob_fp_float2uint.v b/lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/hardware/src/iob_fp_float2uint.v deleted file mode 100755 index feebc15c2..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/hardware/src/iob_fp_float2uint.v +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -//This file was automatically generated by the python 'pipeliner' script -//This module has a latency of 1 clocks -`timescale 1ns / 1ps - -module iob_fp_float2uint - ( - input clk_i, - input rst_i, - - input start_i, - output reg done_o, - - input [31:0] op_i, - output [31:0] res_o - ); - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - done_o <= 1'b0; - end else begin - done_o <= start_i; - end - end - - wire [31:0] s_0; - wire [31:0] s_1; - wire [31:0] s_2; - wire [23:0] s_3; - wire [0:0] s_4; - wire [0:0] s_5; - wire [0:0] s_6; - wire [0:0] s_7; - wire [7:0] s_8; - wire [7:0] s_9; - wire [31:0] s_10; - wire [6:0] s_11; - wire [7:0] s_12; - wire [22:0] s_13; - wire [7:0] s_14; - wire [7:0] s_15; - wire [7:0] s_16; - wire [7:0] s_17; - wire [7:0] s_18; - wire [7:0] s_19; - wire [0:0] s_20; - - iob_fp_dq #(32, 1) dq_s_0 (clk_i, rst_i, s_0, s_1); - assign s_1 = s_2 >> s_15; - assign s_2 = {s_3,s_14}; - assign s_3 = {s_4,s_13}; - assign s_4 = s_7?s_5:s_6; - assign s_5 = 1'd0; - assign s_6 = 1'd1; - assign s_7 = s_8 == s_12; - assign s_8 = s_9 - s_11; - assign s_9 = s_10[30:23]; - assign s_10 = op_i; - assign s_11 = 7'd127; - assign s_12 = -8'd127; - assign s_13 = s_10[22:0]; - assign s_14 = 8'd0; - assign s_15 = s_16 - {7'h0,s_20}; - assign s_16 = s_17 - s_18; - assign s_17 = 8'd32; - assign s_18 = s_7?s_19:s_8; - assign s_19 = -8'd126; - assign s_20 = 1'd1; - assign res_o = s_0; - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/iob_fp_float2uint.py b/lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/iob_fp_float2uint.py deleted file mode 100755 index fc3da87ab..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_float2uint/iob_fp_float2uint.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_fp_dq", - "instance_name": "iob_fp_dq_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_fpu/hardware/src/iob_fp_fpu.v b/lib/hardware/arith_logic/iob_fp/iob_fp_fpu/hardware/src/iob_fp_fpu.v deleted file mode 100755 index 8b42ef434..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_fpu/hardware/src/iob_fp_fpu.v +++ /dev/null @@ -1,409 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -// Function width -`define FUNCT_W 4 - -// -// Function decoder -// -`define FPU_ADD (`FUNCT_W'd0) -`define FPU_SUB (`FUNCT_W'd1) -`define FPU_MUL (`FUNCT_W'd2) -`define FPU_DIV (`FUNCT_W'd3) -`define FPU_MADD (`FUNCT_W'd4) -`define FPU_MSUB (`FUNCT_W'd5) -`define FPU_NMADD (`FUNCT_W'd6) -`define FPU_NMSUB (`FUNCT_W'd7) -`define FPU_SQRT (`FUNCT_W'd8) -`define FPU_MIN_MAX (`FUNCT_W'd9) -`define FPU_CVT_W_X_U (`FUNCT_W'd10) -`define FPU_CVT_W_X (`FUNCT_W'd11) -`define FPU_CMP (`FUNCT_W'd12) -`define FPU_CLASS (`FUNCT_W'd13) -`define FPU_CVT_X_W_U (`FUNCT_W'd14) -`define FPU_CVT_X_W (`FUNCT_W'd15) - -//`define __FULL_FPU__ - -module iob_fp_fpu # ( - parameter DATA_W = 32, - parameter EXP_W = 8 - ) - ( - input clk_i, - input rst_i, - - // Inputs - input start_i, - input [`FUNCT_W-1:0] funct_i, - - input [DATA_W-1:0] rs1_i, - input [DATA_W-1:0] rs2_i, - input [DATA_W-1:0] rs3_i, - - input [DATA_W-1:0] rs1_int_i, - - // Outputs - output reg [DATA_W-1:0] res_o, - output done_o - ); - - reg add, sub; - reg mul; - reg div; - reg fsqrt; - reg min_max; - reg cmp; - reg int2float, uint2float; - reg float2int, float2uint; - reg madd, msub, nmadd, nmsub; - wire any_add = |{add, sub}; - wire any_mul = mul; - wire any_div = div; - wire any_sqrt = fsqrt; - wire any_min_max = min_max; - wire any_cmp = cmp; - wire any_int2float = int2float; - wire any_uint2float = uint2float; - wire any_float2int = float2int; - wire any_float2uint = float2uint; - wire any_madd = |{madd, msub, nmadd, nmsub}; - reg any_add_reg, any_mul_reg, any_div_reg, any_sqrt_reg, any_min_max_reg, any_cmp_reg, any_int2float_reg, any_uint2float_reg, any_float2int_reg, any_float2uint_reg; - - wire [DATA_W-1:0] add_res; - wire [DATA_W-1:0] mul_res; - wire [DATA_W-1:0] div_res; - wire [DATA_W-1:0] sqrt_res; - wire [DATA_W-1:0] min_max_res; - wire cmp_res; - wire [DATA_W-1:0] int2float_res; - wire [DATA_W-1:0] uint2float_res; - wire [DATA_W-1:0] float2int_res; - wire [DATA_W-1:0] float2uint_res; - - wire add_done, mul_done, div_done, sqrt_done, min_max_done, cmp_done, int2float_done, uint2float_done, float2int_done, float2uint_done; - reg done_int; - reg done_reg; - - reg ready_reg; - - wire add_start = any_add & ~(any_add_reg & ~ready_reg); - wire mul_start = any_mul & ~(any_mul_reg & ~ready_reg); - wire div_start = any_div & ~(any_div_reg & ~ready_reg); - wire sqrt_start = any_sqrt & ~(any_sqrt_reg & ~ready_reg); - wire min_max_start = any_min_max & ~(any_min_max_reg & ~ready_reg); - wire cmp_start = any_cmp & ~(any_cmp_reg & ~ready_reg); - wire int2float_start = any_int2float & ~(any_int2float_reg & ~ready_reg); - wire uint2float_start = any_uint2float & ~(any_uint2float_reg & ~ready_reg); - wire float2int_start = any_float2int & ~(any_float2int_reg & ~ready_reg); - wire float2uint_start = any_float2uint & ~(any_float2uint_reg & ~ready_reg); - wire start_int = |{add_start, mul_start, div_start, sqrt_start, min_max_start, cmp_start, int2float_start, uint2float_start, float2int_start, float2uint_start}; - - wire fpu_wait = start_int | ~done_o; - wire ready = done_o & ~done_reg; - - always @* begin - add = 0; - sub = 0; - mul = 0; - div = 0; - madd = 0; - msub = 0; - nmadd = 0; - nmsub = 0; - fsqrt = 0; - min_max = 0; - int2float = 0; - uint2float = 0; - cmp = 0; - //fclass = 0; - float2int = 0; - float2uint = 0; - - if (start_i) begin - case (funct_i) - `FPU_ADD: add = 1; - `FPU_SUB: sub = 1; - `FPU_MUL: mul = 1; - `FPU_DIV: div = 1; -`ifdef __FULL_FPU__ - `FPU_MADD: madd = 1; - `FPU_MSUB: msub = 1; - `FPU_NMADD: nmadd = 1; - `FPU_NMSUB: nmsub = 1; - `FPU_SQRT: fsqrt = 1; - `FPU_MIN_MAX: min_max = 1; - `FPU_CVT_W_X_U: float2uint = 1; - `FPU_CVT_W_X: float2int = 1; - `FPU_CMP: cmp = 1; - //`FPU_CLASS: fclass = 1; - `FPU_CVT_X_W_U: uint2float = 1; - `FPU_CVT_X_W: int2float = 1; -`endif - default:; - endcase - end - end - - always @(posedge clk_i) begin - if (rst_i) begin - any_add_reg <= 1'b0; - any_mul_reg <= 1'b0; - any_div_reg <= 1'b0; - any_sqrt_reg <= 1'b0; - any_min_max_reg <= 1'b0; - any_cmp_reg <= 1'b0; - any_int2float_reg <= 1'b0; - any_uint2float_reg <= 1'b0; - any_float2int_reg <= 1'b0; - any_float2uint_reg <= 1'b0; - end else begin - any_add_reg <= any_add; - any_mul_reg <= any_mul; - any_div_reg <= any_div; - any_sqrt_reg <= any_sqrt; - any_min_max_reg <= any_min_max; - any_cmp_reg <= any_cmp; - any_int2float_reg <= any_int2float; - any_uint2float_reg <= any_uint2float; - any_float2int_reg <= any_float2int; - any_float2uint_reg <= any_float2uint; - end - end - - always @ (posedge clk_i) begin - if (rst_i) begin - ready_reg <= 1'b0; - end else begin - ready_reg <= ready; - end - end - - always @(posedge clk_i) begin - if (rst_i) begin - done_reg <= 1'b1; - end else begin - done_reg <= done_o; - end - end - - wire [DATA_W-1:0] op_a_add = any_madd? mul_res: rs1_i; - wire [DATA_W-1:0] op_b_add_int = any_madd? rs3_i: rs2_i; - wire [DATA_W-1:0] op_b_add = sub? {~op_b_add_int[DATA_W-1], op_b_add_int[DATA_W-2:0]}: op_b_add_int; - - iob_fp_add #( - .DATA_W (DATA_W), - .EXP_W (EXP_W) - ) - fp_add0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .overflow_o(), - .underflow_o(), - .exception_o(), - - .start_i(add_start), - .done_o(add_done), - - .op_a_i(op_a_add), - .op_b_i(op_b_add), - .res_o(add_res) - ); - - iob_fp_mul #( - .DATA_W (DATA_W), - .EXP_W (EXP_W) - ) - fp_mul0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .overflow_o(), - .underflow_o(), - .exception_o(), - - .start_i(mul_start), - .done_o(mul_done), - - .op_a_i(rs1_i), - .op_b_i(rs2_i), - .res_o(mul_res) - ); - - iob_fp_div #( - .DATA_W (DATA_W), - .EXP_W (EXP_W) - ) - fp_div0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .overflow_o(), - .underflow_o(), - .exception_o(), - - .start_i(div_start), - .done_o(div_done), - - .op_a_i(rs1_i), - .op_b_i(rs2_i), - .res_o(div_res) - ); - -`ifdef __FULL_FPU__ - iob_fp_sqrt #( - .DATA_W (DATA_W), - .EXP_W (EXP_W) - ) - fp_sqrt0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .start_i(sqrt_start), - .done_o(sqrt_done), - - .op_i(rs1_i), - .res_o(sqrt_res) - ); - - iob_fp_minmax # ( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - fp_minmax0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .start_i(min_max_start), - .done_o(min_max_done), - - .max_n_min_i(rm[0]), - .op_a_i(rs1_i), - .op_b_i(rs2_i), - .res_o(min_max_res) - ); - - iob_fp_cmp # ( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - fp_cmp0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .start_i(cmp_start), - .done_o(cmp_done), - - .fn_i(rm[1:0]), - .op_a_i(rs1_i), - .op_b_i(rs2_i), - .res_o(cmp_res) - ); - - iob_fp_int2float fp_int2float0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .start_i(int2float_start), - .done_o(int2float_done), - - .op_i(rs1_int_i), - .res_o(int2float_res) - ); - - iob_fp_uint2float fp_uint2float0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .start_i(uint2float_start), - .done_o(uint2float_done), - - .op_i(rs1_int_i), - .res_o(uint2float_res) - ); - - iob_fp_float2int fp_float2int0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .start_i(float2int_start), - .done_o(float2int_done), - - .op_i(rs1_i), - .res_o(float2int_res) - ); - - iob_fp_float2uint fp_float2uint0 - ( - .clk_i(clk_i), - .rst_i(rst_i), - - .start_i(float2uint_start), - .done_o(float2uint_done), - - .op_i(rs1_i), - .res_o(float2uint_res) - ); -`endif - - assign done_o = start_int? 1'b0: done_int; - - always @* begin - res_o = {DATA_W{1'b0}}; - done_int = 1'b1; - - if (any_add) begin - if (nmadd | nmsub) begin - res_o = {~add_res[DATA_W-1], add_res[DATA_W-2:0]}; - end else begin - res_o = add_res; - end - - done_int = add_done; - end else if (any_mul) begin - res_o = mul_res; - done_int = mul_done; - end else if (any_div) begin - res_o = div_res; - done_int = div_done; -`ifdef __FULL_FPU__ - end else if (any_sqrt) begin - res_o = sqrt_res; - done_int = sqrt_done; - end else if (any_min_max) begin - res_o = min_max_res; - done_int = min_max_done; - end else if (any_cmp) begin - res_o = {{(DATA_W-1){1'b0}}, cmp_res}; - done_int = cmp_done; - end else if (any_int2float) begin - res_o = int2float_res; - done_int = int2float_done; - end else if (any_uint2float) begin - res_o = uint2float_res; - done_int = uint2float_done; - end else if (any_float2int) begin - res_o = float2int_res; - done_int = float2int_done; - end else if (any_float2uint) begin - res_o = float2uint_res; - done_int = float2uint_done; -`endif - end - end - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_fpu/iob_fp_fpu.py b/lib/hardware/arith_logic/iob_fp/iob_fp_fpu/iob_fp_fpu.py deleted file mode 100755 index 72471c8d4..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_fpu/iob_fp_fpu.py +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_fp_add", - "instance_name": "iob_fp_add_inst", - }, - { - "core_name": "iob_fp_mul", - "instance_name": "iob_fp_mul_inst", - }, - { - "core_name": "iob_fp_div", - "instance_name": "iob_fp_div_inst", - }, - { - "core_name": "iob_fp_sqrt", - "instance_name": "iob_fp_sqrt_inst", - }, - { - "core_name": "iob_fp_minmax", - "instance_name": "iob_fp_minmax_inst", - }, - { - "core_name": "iob_fp_cmp", - "instance_name": "iob_fp_cmp_inst", - }, - { - "core_name": "iob_fp_int2float", - "instance_name": "iob_fp_int2float_inst", - }, - { - "core_name": "iob_fp_uint2float", - "instance_name": "iob_fp_uint2float_inst", - }, - { - "core_name": "iob_fp_float2int", - "instance_name": "iob_fp_float2int_inst", - }, - { - "core_name": "iob_fp_float2uint", - "instance_name": "iob_fp_float2uint_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_int2float/hardware/src/iob_fp_int2float.v b/lib/hardware/arith_logic/iob_fp/iob_fp_int2float/hardware/src/iob_fp_int2float.v deleted file mode 100755 index bda8ced9e..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_int2float/hardware/src/iob_fp_int2float.v +++ /dev/null @@ -1,862 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -//This file was automatically generated by the python 'pipeliner' script -//This module has a latency of 5 clocks -`timescale 1ns / 1ps - -module iob_fp_int2float - ( - input clk_i, - input rst_i, - - input start_i, - output done_o, - - input [31:0] op_i, - output [31:0] res_o - ); - - localparam END_COUNT = 4; - - reg [3:0] counter; - assign done_o = (counter == END_COUNT)? 1'b1: 1'b0; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - counter <= END_COUNT; - end else if (start_i) begin - counter <= 0; - end else if (~done_o) begin - counter <= counter + 1'b1; - end - end - - wire [31:0] s_0; - wire [31:0] s_1; - wire [31:0] s_2; - wire [0:0] s_3; - wire [31:0] s_4; - wire [30:0] s_5; - wire [31:0] s_6; - wire [31:0] s_7; - wire [31:0] s_8; - wire [30:0] s_9; - wire [31:0] s_10; - wire [31:0] s_11; - wire [31:0] s_12; - wire [30:0] s_13; - wire [31:0] s_14; - wire [31:0] s_15; - wire [8:0] s_16; - wire [8:0] s_17; - wire [7:0] s_18; - wire [22:0] s_19; - wire [23:0] s_20; - wire [23:0] s_21; - wire [23:0] s_22; - wire [24:0] s_23; - wire [24:0] s_24; - wire [24:0] s_25; - wire [24:0] s_26; - wire [23:0] s_27; - wire [31:0] s_28; - wire [31:0] s_29; - wire [31:0] s_30; - wire [31:0] s_31; - wire [31:0] s_32; - wire [31:0] s_33; - wire [5:0] s_34; - wire [5:0] s_35; - wire [0:0] s_36; - wire [0:0] s_37; - wire [4:0] s_38; - wire [0:0] s_39; - wire [0:0] s_40; - wire [3:0] s_41; - wire [0:0] s_42; - wire [0:0] s_43; - wire [2:0] s_44; - wire [0:0] s_45; - wire [0:0] s_46; - wire [1:0] s_47; - wire [0:0] s_48; - wire [0:0] s_49; - wire [0:0] s_50; - wire [1:0] s_51; - wire [3:0] s_52; - wire [7:0] s_53; - wire [15:0] s_54; - wire [0:0] s_55; - wire [0:0] s_56; - wire [0:0] s_57; - wire [0:0] s_58; - wire [0:0] s_59; - wire [0:0] s_60; - wire [0:0] s_61; - wire [1:0] s_62; - wire [0:0] s_63; - wire [0:0] s_64; - wire [0:0] s_65; - wire [1:0] s_66; - wire [0:0] s_67; - wire [0:0] s_68; - wire [0:0] s_69; - wire [0:0] s_70; - wire [0:0] s_71; - wire [0:0] s_72; - wire [1:0] s_73; - wire [0:0] s_74; - wire [0:0] s_75; - wire [0:0] s_76; - wire [0:0] s_77; - wire [0:0] s_78; - wire [0:0] s_79; - wire [2:0] s_80; - wire [0:0] s_81; - wire [0:0] s_82; - wire [1:0] s_83; - wire [0:0] s_84; - wire [0:0] s_85; - wire [0:0] s_86; - wire [1:0] s_87; - wire [3:0] s_88; - wire [0:0] s_89; - wire [0:0] s_90; - wire [0:0] s_91; - wire [0:0] s_92; - wire [0:0] s_93; - wire [0:0] s_94; - wire [0:0] s_95; - wire [1:0] s_96; - wire [0:0] s_97; - wire [0:0] s_98; - wire [0:0] s_99; - wire [1:0] s_100; - wire [0:0] s_101; - wire [0:0] s_102; - wire [0:0] s_103; - wire [0:0] s_104; - wire [0:0] s_105; - wire [0:0] s_106; - wire [1:0] s_107; - wire [0:0] s_108; - wire [0:0] s_109; - wire [0:0] s_110; - wire [0:0] s_111; - wire [0:0] s_112; - wire [2:0] s_113; - wire [0:0] s_114; - wire [0:0] s_115; - wire [1:0] s_116; - wire [1:0] s_117; - wire [1:0] s_118; - wire [0:0] s_119; - wire [3:0] s_120; - wire [0:0] s_121; - wire [0:0] s_122; - wire [2:0] s_123; - wire [0:0] s_124; - wire [0:0] s_125; - wire [1:0] s_126; - wire [0:0] s_127; - wire [0:0] s_128; - wire [0:0] s_129; - wire [1:0] s_130; - wire [3:0] s_131; - wire [7:0] s_132; - wire [0:0] s_133; - wire [0:0] s_134; - wire [0:0] s_135; - wire [0:0] s_136; - wire [0:0] s_137; - wire [0:0] s_138; - wire [0:0] s_139; - wire [1:0] s_140; - wire [0:0] s_141; - wire [0:0] s_142; - wire [0:0] s_143; - wire [1:0] s_144; - wire [0:0] s_145; - wire [0:0] s_146; - wire [0:0] s_147; - wire [0:0] s_148; - wire [0:0] s_149; - wire [0:0] s_150; - wire [1:0] s_151; - wire [0:0] s_152; - wire [0:0] s_153; - wire [0:0] s_154; - wire [0:0] s_155; - wire [0:0] s_156; - wire [0:0] s_157; - wire [2:0] s_158; - wire [0:0] s_159; - wire [0:0] s_160; - wire [1:0] s_161; - wire [0:0] s_162; - wire [0:0] s_163; - wire [0:0] s_164; - wire [1:0] s_165; - wire [3:0] s_166; - wire [0:0] s_167; - wire [0:0] s_168; - wire [0:0] s_169; - wire [0:0] s_170; - wire [0:0] s_171; - wire [0:0] s_172; - wire [0:0] s_173; - wire [1:0] s_174; - wire [0:0] s_175; - wire [0:0] s_176; - wire [0:0] s_177; - wire [1:0] s_178; - wire [0:0] s_179; - wire [0:0] s_180; - wire [0:0] s_181; - wire [0:0] s_182; - wire [0:0] s_183; - wire [0:0] s_184; - wire [1:0] s_185; - wire [0:0] s_186; - wire [0:0] s_187; - wire [0:0] s_188; - wire [0:0] s_189; - wire [0:0] s_190; - wire [2:0] s_191; - wire [0:0] s_192; - wire [0:0] s_193; - wire [1:0] s_194; - wire [1:0] s_195; - wire [1:0] s_196; - wire [3:0] s_197; - wire [0:0] s_198; - wire [0:0] s_199; - wire [2:0] s_200; - wire [2:0] s_201; - wire [2:0] s_202; - wire [0:0] s_203; - wire [4:0] s_204; - wire [0:0] s_205; - wire [0:0] s_206; - wire [3:0] s_207; - wire [0:0] s_208; - wire [0:0] s_209; - wire [2:0] s_210; - wire [0:0] s_211; - wire [0:0] s_212; - wire [1:0] s_213; - wire [0:0] s_214; - wire [0:0] s_215; - wire [0:0] s_216; - wire [1:0] s_217; - wire [3:0] s_218; - wire [7:0] s_219; - wire [15:0] s_220; - wire [0:0] s_221; - wire [0:0] s_222; - wire [0:0] s_223; - wire [0:0] s_224; - wire [0:0] s_225; - wire [0:0] s_226; - wire [0:0] s_227; - wire [1:0] s_228; - wire [0:0] s_229; - wire [0:0] s_230; - wire [0:0] s_231; - wire [1:0] s_232; - wire [0:0] s_233; - wire [0:0] s_234; - wire [0:0] s_235; - wire [0:0] s_236; - wire [0:0] s_237; - wire [0:0] s_238; - wire [1:0] s_239; - wire [0:0] s_240; - wire [0:0] s_241; - wire [0:0] s_242; - wire [0:0] s_243; - wire [0:0] s_244; - wire [0:0] s_245; - wire [2:0] s_246; - wire [0:0] s_247; - wire [0:0] s_248; - wire [1:0] s_249; - wire [0:0] s_250; - wire [0:0] s_251; - wire [0:0] s_252; - wire [1:0] s_253; - wire [3:0] s_254; - wire [0:0] s_255; - wire [0:0] s_256; - wire [0:0] s_257; - wire [0:0] s_258; - wire [0:0] s_259; - wire [0:0] s_260; - wire [0:0] s_261; - wire [1:0] s_262; - wire [0:0] s_263; - wire [0:0] s_264; - wire [0:0] s_265; - wire [1:0] s_266; - wire [0:0] s_267; - wire [0:0] s_268; - wire [0:0] s_269; - wire [0:0] s_270; - wire [0:0] s_271; - wire [0:0] s_272; - wire [1:0] s_273; - wire [0:0] s_274; - wire [0:0] s_275; - wire [0:0] s_276; - wire [0:0] s_277; - wire [0:0] s_278; - wire [2:0] s_279; - wire [0:0] s_280; - wire [0:0] s_281; - wire [1:0] s_282; - wire [1:0] s_283; - wire [1:0] s_284; - wire [0:0] s_285; - wire [3:0] s_286; - wire [0:0] s_287; - wire [0:0] s_288; - wire [2:0] s_289; - wire [0:0] s_290; - wire [0:0] s_291; - wire [1:0] s_292; - wire [0:0] s_293; - wire [0:0] s_294; - wire [0:0] s_295; - wire [1:0] s_296; - wire [3:0] s_297; - wire [7:0] s_298; - wire [0:0] s_299; - wire [0:0] s_300; - wire [0:0] s_301; - wire [0:0] s_302; - wire [0:0] s_303; - wire [0:0] s_304; - wire [0:0] s_305; - wire [1:0] s_306; - wire [0:0] s_307; - wire [0:0] s_308; - wire [0:0] s_309; - wire [1:0] s_310; - wire [0:0] s_311; - wire [0:0] s_312; - wire [0:0] s_313; - wire [0:0] s_314; - wire [0:0] s_315; - wire [0:0] s_316; - wire [1:0] s_317; - wire [0:0] s_318; - wire [0:0] s_319; - wire [0:0] s_320; - wire [0:0] s_321; - wire [0:0] s_322; - wire [0:0] s_323; - wire [2:0] s_324; - wire [0:0] s_325; - wire [0:0] s_326; - wire [1:0] s_327; - wire [0:0] s_328; - wire [0:0] s_329; - wire [0:0] s_330; - wire [1:0] s_331; - wire [3:0] s_332; - wire [0:0] s_333; - wire [0:0] s_334; - wire [0:0] s_335; - wire [0:0] s_336; - wire [0:0] s_337; - wire [0:0] s_338; - wire [0:0] s_339; - wire [1:0] s_340; - wire [0:0] s_341; - wire [0:0] s_342; - wire [0:0] s_343; - wire [1:0] s_344; - wire [0:0] s_345; - wire [0:0] s_346; - wire [0:0] s_347; - wire [0:0] s_348; - wire [0:0] s_349; - wire [0:0] s_350; - wire [1:0] s_351; - wire [0:0] s_352; - wire [0:0] s_353; - wire [0:0] s_354; - wire [0:0] s_355; - wire [0:0] s_356; - wire [2:0] s_357; - wire [0:0] s_358; - wire [0:0] s_359; - wire [1:0] s_360; - wire [1:0] s_361; - wire [1:0] s_362; - wire [3:0] s_363; - wire [0:0] s_364; - wire [0:0] s_365; - wire [2:0] s_366; - wire [2:0] s_367; - wire [2:0] s_368; - wire [4:0] s_369; - wire [0:0] s_370; - wire [0:0] s_371; - wire [3:0] s_372; - wire [3:0] s_373; - wire [3:0] s_374; - wire [0:0] s_375; - wire [23:0] s_376; - wire [0:0] s_377; - wire [0:0] s_378; - wire [0:0] s_379; - wire [0:0] s_380; - wire [0:0] s_381; - wire [0:0] s_382; - wire [0:0] s_383; - wire [0:0] s_384; - wire [0:0] s_385; - wire [5:0] s_386; - wire [0:0] s_387; - wire [0:0] s_388; - wire [0:0] s_389; - wire [23:0] s_390; - wire [0:0] s_391; - wire [31:0] s_392; - wire [8:0] s_393; - wire [0:0] s_394; - wire [7:0] s_395; - wire [7:0] s_396; - wire [7:0] s_397; - wire [7:0] s_398; - wire [7:0] s_399; - wire [7:0] s_400; - wire [6:0] s_401; - wire [22:0] s_402; - wire [0:0] s_403; - wire [0:0] s_404; - wire [7:0] s_405; - wire [0:0] s_406; - wire [0:0] s_407; - wire [0:0] s_408; - wire [23:0] s_409; - wire [0:0] s_410; - wire [0:0] s_411; - - assign s_0 = s_411?s_1:s_6; - iob_fp_dq #(32, 5) dq_s_1 (clk_i, rst_i, s_1, s_2); - assign s_2 = {s_3,s_5}; - assign s_3 = s_4[31]; - assign s_4 = op_i; - assign s_5 = 31'd2143289344; - assign s_6 = s_410?s_7:s_10; - iob_fp_dq #(32, 5) dq_s_7 (clk_i, rst_i, s_7, s_8); - assign s_8 = {s_3,s_9}; - assign s_9 = 31'd2139095040; - assign s_10 = s_408?s_11:s_14; - iob_fp_dq #(32, 5) dq_s_11 (clk_i, rst_i, s_11, s_12); - assign s_12 = {s_3,s_13}; - assign s_13 = 31'd0; - assign s_14 = s_403?s_15:s_392; - assign s_15 = {s_16,s_19}; - iob_fp_dq #(9, 5) dq_s_16 (clk_i, rst_i, s_16, s_17); - assign s_17 = {s_3,s_18}; - assign s_18 = 8'd0; - assign s_19 = s_20[22:0]; - iob_fp_dq #(24, 1) dq_s_20 (clk_i, rst_i, s_20, s_21); - assign s_21 = s_391?s_22:s_390; - assign s_22 = s_23[24:1]; - assign s_23 = s_377?s_24:{1'b0,s_376}; - iob_fp_dq #(25, 1) dq_s_24 (clk_i, rst_i, s_24, s_25); - assign s_25 = s_26 + {24'h0,s_375}; - assign s_26 = {1'b0,s_27}; - assign s_27 = s_28[31:8]; - iob_fp_dq #(32, 1) dq_s_28 (clk_i, rst_i, s_28, s_29); - assign s_29 = s_30 << s_34; - iob_fp_dq #(32, 1) dq_s_30 (clk_i, rst_i, s_30, s_31); - iob_fp_dq #(32, 1) dq_s_31 (clk_i, rst_i, s_31, s_32); - assign s_32 = s_3?s_33:s_4; - assign s_33 = -s_4; - iob_fp_dq #(6, 1) dq_s_34 (clk_i, rst_i, s_34, s_35); - assign s_35 = {s_36,s_369}; - assign s_36 = s_37 & s_203; - assign s_37 = s_38[4]; - assign s_38 = {s_39,s_197}; - assign s_39 = s_40 & s_119; - assign s_40 = s_41[3]; - assign s_41 = {s_42,s_113}; - assign s_42 = s_43 & s_79; - assign s_43 = s_44[2]; - assign s_44 = {s_45,s_73}; - assign s_45 = s_46 & s_61; - assign s_46 = s_47[1]; - assign s_47 = {s_48,s_57}; - assign s_48 = s_49 & s_55; - assign s_49 = ~s_50; - assign s_50 = s_51[1]; - assign s_51 = s_52[3:2]; - assign s_52 = s_53[7:4]; - assign s_53 = s_54[15:8]; - assign s_54 = s_31[31:16]; - assign s_55 = ~s_56; - assign s_56 = s_51[0]; - assign s_57 = s_58 & s_60; - assign s_58 = ~s_59; - assign s_59 = s_51[1]; - assign s_60 = s_51[0]; - assign s_61 = s_62[1]; - assign s_62 = {s_63,s_69}; - assign s_63 = s_64 & s_67; - assign s_64 = ~s_65; - assign s_65 = s_66[1]; - assign s_66 = s_52[1:0]; - assign s_67 = ~s_68; - assign s_68 = s_66[0]; - assign s_69 = s_70 & s_72; - assign s_70 = ~s_71; - assign s_71 = s_66[1]; - assign s_72 = s_66[0]; - assign s_73 = {s_74,s_76}; - assign s_74 = s_46 & s_75; - assign s_75 = ~s_61; - assign s_76 = s_46?s_77:s_78; - assign s_77 = s_62[0:0]; - assign s_78 = s_47[0:0]; - assign s_79 = s_80[2]; - assign s_80 = {s_81,s_107}; - assign s_81 = s_82 & s_95; - assign s_82 = s_83[1]; - assign s_83 = {s_84,s_91}; - assign s_84 = s_85 & s_89; - assign s_85 = ~s_86; - assign s_86 = s_87[1]; - assign s_87 = s_88[3:2]; - assign s_88 = s_53[3:0]; - assign s_89 = ~s_90; - assign s_90 = s_87[0]; - assign s_91 = s_92 & s_94; - assign s_92 = ~s_93; - assign s_93 = s_87[1]; - assign s_94 = s_87[0]; - assign s_95 = s_96[1]; - assign s_96 = {s_97,s_103}; - assign s_97 = s_98 & s_101; - assign s_98 = ~s_99; - assign s_99 = s_100[1]; - assign s_100 = s_88[1:0]; - assign s_101 = ~s_102; - assign s_102 = s_100[0]; - assign s_103 = s_104 & s_106; - assign s_104 = ~s_105; - assign s_105 = s_100[1]; - assign s_106 = s_100[0]; - assign s_107 = {s_108,s_110}; - assign s_108 = s_82 & s_109; - assign s_109 = ~s_95; - assign s_110 = s_82?s_111:s_112; - assign s_111 = s_96[0:0]; - assign s_112 = s_83[0:0]; - assign s_113 = {s_114,s_116}; - assign s_114 = s_43 & s_115; - assign s_115 = ~s_79; - assign s_116 = s_43?s_117:s_118; - assign s_117 = s_80[1:0]; - assign s_118 = s_44[1:0]; - assign s_119 = s_120[3]; - assign s_120 = {s_121,s_191}; - assign s_121 = s_122 & s_157; - assign s_122 = s_123[2]; - assign s_123 = {s_124,s_151}; - assign s_124 = s_125 & s_139; - assign s_125 = s_126[1]; - assign s_126 = {s_127,s_135}; - assign s_127 = s_128 & s_133; - assign s_128 = ~s_129; - assign s_129 = s_130[1]; - assign s_130 = s_131[3:2]; - assign s_131 = s_132[7:4]; - assign s_132 = s_54[7:0]; - assign s_133 = ~s_134; - assign s_134 = s_130[0]; - assign s_135 = s_136 & s_138; - assign s_136 = ~s_137; - assign s_137 = s_130[1]; - assign s_138 = s_130[0]; - assign s_139 = s_140[1]; - assign s_140 = {s_141,s_147}; - assign s_141 = s_142 & s_145; - assign s_142 = ~s_143; - assign s_143 = s_144[1]; - assign s_144 = s_131[1:0]; - assign s_145 = ~s_146; - assign s_146 = s_144[0]; - assign s_147 = s_148 & s_150; - assign s_148 = ~s_149; - assign s_149 = s_144[1]; - assign s_150 = s_144[0]; - assign s_151 = {s_152,s_154}; - assign s_152 = s_125 & s_153; - assign s_153 = ~s_139; - assign s_154 = s_125?s_155:s_156; - assign s_155 = s_140[0:0]; - assign s_156 = s_126[0:0]; - assign s_157 = s_158[2]; - assign s_158 = {s_159,s_185}; - assign s_159 = s_160 & s_173; - assign s_160 = s_161[1]; - assign s_161 = {s_162,s_169}; - assign s_162 = s_163 & s_167; - assign s_163 = ~s_164; - assign s_164 = s_165[1]; - assign s_165 = s_166[3:2]; - assign s_166 = s_132[3:0]; - assign s_167 = ~s_168; - assign s_168 = s_165[0]; - assign s_169 = s_170 & s_172; - assign s_170 = ~s_171; - assign s_171 = s_165[1]; - assign s_172 = s_165[0]; - assign s_173 = s_174[1]; - assign s_174 = {s_175,s_181}; - assign s_175 = s_176 & s_179; - assign s_176 = ~s_177; - assign s_177 = s_178[1]; - assign s_178 = s_166[1:0]; - assign s_179 = ~s_180; - assign s_180 = s_178[0]; - assign s_181 = s_182 & s_184; - assign s_182 = ~s_183; - assign s_183 = s_178[1]; - assign s_184 = s_178[0]; - assign s_185 = {s_186,s_188}; - assign s_186 = s_160 & s_187; - assign s_187 = ~s_173; - assign s_188 = s_160?s_189:s_190; - assign s_189 = s_174[0:0]; - assign s_190 = s_161[0:0]; - assign s_191 = {s_192,s_194}; - assign s_192 = s_122 & s_193; - assign s_193 = ~s_157; - assign s_194 = s_122?s_195:s_196; - assign s_195 = s_158[1:0]; - assign s_196 = s_123[1:0]; - assign s_197 = {s_198,s_200}; - assign s_198 = s_40 & s_199; - assign s_199 = ~s_119; - assign s_200 = s_40?s_201:s_202; - assign s_201 = s_120[2:0]; - assign s_202 = s_41[2:0]; - assign s_203 = s_204[4]; - assign s_204 = {s_205,s_363}; - assign s_205 = s_206 & s_285; - assign s_206 = s_207[3]; - assign s_207 = {s_208,s_279}; - assign s_208 = s_209 & s_245; - assign s_209 = s_210[2]; - assign s_210 = {s_211,s_239}; - assign s_211 = s_212 & s_227; - assign s_212 = s_213[1]; - assign s_213 = {s_214,s_223}; - assign s_214 = s_215 & s_221; - assign s_215 = ~s_216; - assign s_216 = s_217[1]; - assign s_217 = s_218[3:2]; - assign s_218 = s_219[7:4]; - assign s_219 = s_220[15:8]; - assign s_220 = s_31[15:0]; - assign s_221 = ~s_222; - assign s_222 = s_217[0]; - assign s_223 = s_224 & s_226; - assign s_224 = ~s_225; - assign s_225 = s_217[1]; - assign s_226 = s_217[0]; - assign s_227 = s_228[1]; - assign s_228 = {s_229,s_235}; - assign s_229 = s_230 & s_233; - assign s_230 = ~s_231; - assign s_231 = s_232[1]; - assign s_232 = s_218[1:0]; - assign s_233 = ~s_234; - assign s_234 = s_232[0]; - assign s_235 = s_236 & s_238; - assign s_236 = ~s_237; - assign s_237 = s_232[1]; - assign s_238 = s_232[0]; - assign s_239 = {s_240,s_242}; - assign s_240 = s_212 & s_241; - assign s_241 = ~s_227; - assign s_242 = s_212?s_243:s_244; - assign s_243 = s_228[0:0]; - assign s_244 = s_213[0:0]; - assign s_245 = s_246[2]; - assign s_246 = {s_247,s_273}; - assign s_247 = s_248 & s_261; - assign s_248 = s_249[1]; - assign s_249 = {s_250,s_257}; - assign s_250 = s_251 & s_255; - assign s_251 = ~s_252; - assign s_252 = s_253[1]; - assign s_253 = s_254[3:2]; - assign s_254 = s_219[3:0]; - assign s_255 = ~s_256; - assign s_256 = s_253[0]; - assign s_257 = s_258 & s_260; - assign s_258 = ~s_259; - assign s_259 = s_253[1]; - assign s_260 = s_253[0]; - assign s_261 = s_262[1]; - assign s_262 = {s_263,s_269}; - assign s_263 = s_264 & s_267; - assign s_264 = ~s_265; - assign s_265 = s_266[1]; - assign s_266 = s_254[1:0]; - assign s_267 = ~s_268; - assign s_268 = s_266[0]; - assign s_269 = s_270 & s_272; - assign s_270 = ~s_271; - assign s_271 = s_266[1]; - assign s_272 = s_266[0]; - assign s_273 = {s_274,s_276}; - assign s_274 = s_248 & s_275; - assign s_275 = ~s_261; - assign s_276 = s_248?s_277:s_278; - assign s_277 = s_262[0:0]; - assign s_278 = s_249[0:0]; - assign s_279 = {s_280,s_282}; - assign s_280 = s_209 & s_281; - assign s_281 = ~s_245; - assign s_282 = s_209?s_283:s_284; - assign s_283 = s_246[1:0]; - assign s_284 = s_210[1:0]; - assign s_285 = s_286[3]; - assign s_286 = {s_287,s_357}; - assign s_287 = s_288 & s_323; - assign s_288 = s_289[2]; - assign s_289 = {s_290,s_317}; - assign s_290 = s_291 & s_305; - assign s_291 = s_292[1]; - assign s_292 = {s_293,s_301}; - assign s_293 = s_294 & s_299; - assign s_294 = ~s_295; - assign s_295 = s_296[1]; - assign s_296 = s_297[3:2]; - assign s_297 = s_298[7:4]; - assign s_298 = s_220[7:0]; - assign s_299 = ~s_300; - assign s_300 = s_296[0]; - assign s_301 = s_302 & s_304; - assign s_302 = ~s_303; - assign s_303 = s_296[1]; - assign s_304 = s_296[0]; - assign s_305 = s_306[1]; - assign s_306 = {s_307,s_313}; - assign s_307 = s_308 & s_311; - assign s_308 = ~s_309; - assign s_309 = s_310[1]; - assign s_310 = s_297[1:0]; - assign s_311 = ~s_312; - assign s_312 = s_310[0]; - assign s_313 = s_314 & s_316; - assign s_314 = ~s_315; - assign s_315 = s_310[1]; - assign s_316 = s_310[0]; - assign s_317 = {s_318,s_320}; - assign s_318 = s_291 & s_319; - assign s_319 = ~s_305; - assign s_320 = s_291?s_321:s_322; - assign s_321 = s_306[0:0]; - assign s_322 = s_292[0:0]; - assign s_323 = s_324[2]; - assign s_324 = {s_325,s_351}; - assign s_325 = s_326 & s_339; - assign s_326 = s_327[1]; - assign s_327 = {s_328,s_335}; - assign s_328 = s_329 & s_333; - assign s_329 = ~s_330; - assign s_330 = s_331[1]; - assign s_331 = s_332[3:2]; - assign s_332 = s_298[3:0]; - assign s_333 = ~s_334; - assign s_334 = s_331[0]; - assign s_335 = s_336 & s_338; - assign s_336 = ~s_337; - assign s_337 = s_331[1]; - assign s_338 = s_331[0]; - assign s_339 = s_340[1]; - assign s_340 = {s_341,s_347}; - assign s_341 = s_342 & s_345; - assign s_342 = ~s_343; - assign s_343 = s_344[1]; - assign s_344 = s_332[1:0]; - assign s_345 = ~s_346; - assign s_346 = s_344[0]; - assign s_347 = s_348 & s_350; - assign s_348 = ~s_349; - assign s_349 = s_344[1]; - assign s_350 = s_344[0]; - assign s_351 = {s_352,s_354}; - assign s_352 = s_326 & s_353; - assign s_353 = ~s_339; - assign s_354 = s_326?s_355:s_356; - assign s_355 = s_340[0:0]; - assign s_356 = s_327[0:0]; - assign s_357 = {s_358,s_360}; - assign s_358 = s_288 & s_359; - assign s_359 = ~s_323; - assign s_360 = s_288?s_361:s_362; - assign s_361 = s_324[1:0]; - assign s_362 = s_289[1:0]; - assign s_363 = {s_364,s_366}; - assign s_364 = s_206 & s_365; - assign s_365 = ~s_285; - assign s_366 = s_206?s_367:s_368; - assign s_367 = s_286[2:0]; - assign s_368 = s_207[2:0]; - assign s_369 = {s_370,s_372}; - assign s_370 = s_37 & s_371; - assign s_371 = ~s_203; - assign s_372 = s_37?s_373:s_374; - assign s_373 = s_204[3:0]; - assign s_374 = s_38[3:0]; - assign s_375 = 1'd1; - iob_fp_dq #(24, 1) dq_s_376 (clk_i, rst_i, s_376, s_27); - assign s_377 = s_378 & s_380; - iob_fp_dq #(1, 1) dq_s_378 (clk_i, rst_i, s_378, s_379); - assign s_379 = s_28[7]; - assign s_380 = s_381 | s_388; - assign s_381 = s_382 | s_384; - iob_fp_dq #(1, 1) dq_s_382 (clk_i, rst_i, s_382, s_383); - assign s_383 = s_28[6]; - iob_fp_dq #(1, 1) dq_s_384 (clk_i, rst_i, s_384, s_385); - assign s_385 = s_386[0] != s_387; - assign s_386 = s_28[5:0]; - assign s_387 = 1'd0; - iob_fp_dq #(1, 1) dq_s_388 (clk_i, rst_i, s_388, s_389); - assign s_389 = s_27[0]; - assign s_390 = s_23[23:0]; - assign s_391 = s_23[24]; - assign s_392 = {s_393,s_402}; - assign s_393 = {s_394,s_395}; - iob_fp_dq #(1, 5) dq_s_394 (clk_i, rst_i, s_394, s_3); - assign s_395 = s_396 + s_401; - iob_fp_dq #(8, 1) dq_s_396 (clk_i, rst_i, s_396, s_397); - assign s_397 = s_398 + {7'h0,s_391}; - iob_fp_dq #(8, 2) dq_s_398 (clk_i, rst_i, s_398, s_399); - assign s_399 = s_400 - {2'h0,s_34}; - assign s_400 = 8'd31; - assign s_401 = 7'd127; - assign s_402 = s_20[22:0]; - assign s_403 = s_404 & s_406; - assign s_404 = s_396 == s_405; - assign s_405 = -8'd126; - assign s_406 = ~s_407; - assign s_407 = s_20[23]; - assign s_408 = s_20 == s_409; - assign s_409 = 24'd0; - assign s_410 = 1'd0; - assign s_411 = 1'd0; - assign res_o = s_0; - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_int2float/iob_fp_int2float.py b/lib/hardware/arith_logic/iob_fp/iob_fp_int2float/iob_fp_int2float.py deleted file mode 100755 index fc3da87ab..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_int2float/iob_fp_int2float.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_fp_dq", - "instance_name": "iob_fp_dq_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_minmax/iob_fp_minmax.py b/lib/hardware/arith_logic/iob_fp/iob_fp_minmax/iob_fp_minmax.py deleted file mode 100755 index bd1f5e38f..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_minmax/iob_fp_minmax.py +++ /dev/null @@ -1,180 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "EXP_W", - "type": "F", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "Exponent width", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Input port", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "start_i", - "descr": "Input port", - "signals": [ - { - "name": "start", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "max_n_min_i", - "descr": "Input port", - "signals": [ - { - "name": "max_n_min", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "op_a_i", - "descr": "Input port", - "signals": [ - { - "name": "op_a", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "op_b_i", - "descr": "Input port", - "signals": [ - { - "name": "op_b", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "done_o", - "descr": "Output port", - "signals": [ - { - "name": "done", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "res_o", - "descr": "Output port", - "signals": [ - { - "name": "res", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "bigger", - "descr": "bigger wire", - "signals": [ - {"name": "bigger", "width": "DATA_W"}, - ], - }, - { - "name": "smaller", - "descr": "smaller wire", - "signals": [ - {"name": "smaller", "width": "DATA_W"}, - ], - }, - { - "name": "op_a_nan", - "descr": "op_a_nan wire", - "signals": [ - {"name": "op_a_nan", "width": 1}, - ], - }, - { - "name": "op_b_nan", - "descr": "op_b_nan wire", - "signals": [ - {"name": "op_b_nan", "width": 1}, - ], - }, - { - "name": "rst_int", - "descr": "rst wire", - "signals": [ - {"name": "rst_int", "width": "DATA_W"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Canonical NAN - `define NAN {1'b0, {EXP_W{1'b1}}, 1'b1, {(DATA_W-EXP_W-2){1'b0}}} - // Infinite - `define INF(SIGN) {SIGN, {EXP_W{1'b1}}, {(DATA_W-EXP_W-1){1'b0}}} - assign bigger = (op_a_i[DATA_W-1] ^ op_b_i[DATA_W-1])? (op_a_i[DATA_W-1]? op_b_i: op_a_i):op_a_i[DATA_W-1]? ((op_a_i[DATA_W-2:0] > op_b_i[DATA_W-2:0])? op_b_i: op_a_i):((op_a_i[DATA_W-2:0] > op_b_i[DATA_W-2:0])? op_a_i: op_b_i); - assign smaller = (op_a_i[DATA_W-1] ^ op_b_i[DATA_W-1])? (op_a_i[DATA_W-1]? op_a_i: op_b_i):op_a_i[DATA_W-1]? ((op_a_i[DATA_W-2:0] > op_b_i[DATA_W-2:0])? op_a_i: op_b_i):((op_a_i[DATA_W-2:0] > op_b_i[DATA_W-2:0])? op_b_i: op_a_i); - assign op_a_nan = &op_a_i[DATA_W-2 -: EXP_W] & |op_a_i[DATA_W-EXP_W-2:0]; - assign op_b_nan = &op_b_i[DATA_W-2 -: EXP_W] & |op_b_i[DATA_W-EXP_W-2:0]; - assign res_int = (op_a_nan & op_b_nan)? `NAN: op_a_nan? op_b_i:op_b_nan? op_a_i: max_n_min_i? bigger: smaller; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - res_o <= {DATA_W{1'b0}}; - done_o <= 1'b0; - end else begin - res_o <= res_int; - done_o <= start_i; - end - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_mul/hardware/src/iob_fp_mul.v b/lib/hardware/arith_logic/iob_fp/iob_fp_mul/hardware/src/iob_fp_mul.v deleted file mode 100755 index b96384af4..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_mul/hardware/src/iob_fp_mul.v +++ /dev/null @@ -1,221 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -// Canonical NAN -`define NAN {1'b0, {EXP_W{1'b1}}, 1'b1, {(DATA_W-EXP_W-2){1'b0}}} - -// Infinite -`define INF(SIGN) {SIGN, {EXP_W{1'b1}}, {(DATA_W-EXP_W-1){1'b0}}} - -module iob_fp_mul #( - parameter DATA_W = 32, - parameter EXP_W = 8 - ) - ( - input clk_i, - input rst_i, - - input start_i, - output reg done_o, - - input [DATA_W-1:0] op_a_i, - input [DATA_W-1:0] op_b_i, - - output overflow_o, - output underflow_o, - output exception_o, - - output reg [DATA_W-1:0] res_o - ); - - localparam MAN_W = DATA_W-EXP_W; - localparam BIAS = 2**(EXP_W-1)-1; - localparam EXTRA = 3; - - // Special cases -`ifdef SPECIAL_CASES - wire op_a_nan, op_a_inf, op_a_zero, op_a_sub; - iob_fp_special #( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - special_op_a - ( - .data_i (op_a_i), - - .nan_o (op_a_nan), - .infinite_o (op_a_inf), - .zero_o (op_a_zero), - .sub_normal_o (op_a_sub) - ); - - wire op_b_nan, op_b_inf, op_b_zero, op_b_sub; - iob_fp_special #( - .DATA_W(DATA_W), - .EXP_W(EXP_W) - ) - special_op_b - ( - .data_i (op_b_i), - - .nan_o (op_b_nan), - .infinite_o (op_b_inf), - .zero_o (op_b_zero), - .sub_normal_o (op_b_sub) - ); - - wire special = op_a_nan | op_a_inf | op_b_nan | op_b_inf; - wire [DATA_W-1:0] res_special = (op_a_nan | op_b_nan)? `NAN: - (op_a_inf & op_b_inf)? `NAN: - (op_a_zero | op_b_zero)? `NAN: - `INF(op_b_i[DATA_W-1] ^ op_b_i[DATA_W-1]); -`endif - - // Unpack - wire [MAN_W-1:0] A_Mantissa = {1'b1, op_a_i[MAN_W-2:0]}; - wire [EXP_W-1:0] A_Exponent = op_a_i[DATA_W-2 -: EXP_W]; - wire A_sign = op_a_i[DATA_W-1]; - - wire [MAN_W-1:0] B_Mantissa = {1'b1, op_b_i[MAN_W-2:0]}; - wire [EXP_W-1:0] B_Exponent = op_b_i[DATA_W-2 -: EXP_W]; - wire B_sign = op_b_i[DATA_W-1]; - - // pipeline stage 1 - reg A_sign_reg; - reg [EXP_W-1:0] A_Exponent_reg; - reg [MAN_W-1:0] A_Mantissa_reg; - - reg B_sign_reg; - reg [EXP_W-1:0] B_Exponent_reg; - reg [MAN_W-1:0] B_Mantissa_reg; - - reg done_int; - always @(posedge clk_i) begin - if (rst_i) begin - A_sign_reg <= 1'b0; - A_Exponent_reg <= {EXP_W{1'b0}}; - A_Mantissa_reg <= {MAN_W{1'b0}}; - - B_sign_reg <= 1'b0; - B_Exponent_reg <= {EXP_W{1'b0}}; - B_Mantissa_reg <= {MAN_W{1'b0}}; - - done_int <= 1'b0; - end else begin - A_sign_reg <= A_sign; - A_Exponent_reg <= A_Exponent; - A_Mantissa_reg <= A_Mantissa; - - B_sign_reg <= B_sign; - B_Exponent_reg <= B_Exponent; - B_Mantissa_reg <= B_Mantissa; - - done_int <= start_i; - end - end - - // Multiplication - wire Temp_sign = A_sign_reg ^ B_sign_reg; - wire [EXP_W-1:0] Temp_Exponent = A_Exponent_reg + B_Exponent_reg - BIAS; - wire [2*MAN_W-1:0] Temp_Mantissa = A_Mantissa_reg * B_Mantissa_reg; - - // pipeline stage 2 - reg Temp_sign_reg; - reg [EXP_W-1:0] Temp_Exponent_reg; - reg [MAN_W+EXTRA:0] Temp_Mantissa_reg; - - reg done_int2; - always @(posedge clk_i) begin - if (rst_i) begin - Temp_sign_reg <= 1'b0; - Temp_Exponent_reg <= {EXP_W{1'b0}}; - Temp_Mantissa_reg <= {(MAN_W+EXTRA+1){1'b0}}; - - done_int2 <= 1'b0; - end else begin - Temp_sign_reg <= Temp_sign; - Temp_Exponent_reg <= Temp_Exponent; - Temp_Mantissa_reg <= Temp_Mantissa[2*MAN_W-1 -: MAN_W+EXTRA+1]; - - done_int2 <= done_int; - end - end - - // Normalize - wire [MAN_W+EXTRA-1:0] Mantissa_int = Temp_Mantissa_reg[MAN_W+EXTRA]? Temp_Mantissa_reg[MAN_W+EXTRA:1] : Temp_Mantissa_reg[MAN_W+EXTRA-1:0]; - wire [EXP_W-1:0] Exponent_int = Temp_Mantissa_reg[MAN_W+EXTRA]? Temp_Exponent_reg + 1'b1 : Temp_Exponent_reg; - - // pipeline stage 3 - reg Temp_sign_reg2; - - reg [EXP_W-1:0] Exponent_reg; - reg [MAN_W+EXTRA-1:0] Mantissa_reg; - - reg done_int3; - always @(posedge clk_i) begin - if (rst_i) begin - Temp_sign_reg2 <= 1'b0; - - Exponent_reg <= {EXP_W{1'b0}}; - Mantissa_reg <= {(MAN_W+EXTRA){1'b0}}; - - done_int3 <= 1'b0; - end else begin - Temp_sign_reg2 <= Temp_sign_reg; - - Exponent_reg <= Exponent_int; - Mantissa_reg <= {Mantissa_int[MAN_W+EXTRA-1:1], Mantissa_int[0] | Temp_Mantissa_reg[0]}; - - done_int3 <= done_int2; - end - end - - // Round - wire [MAN_W-1:0] Mantissa_rnd; - wire [EXP_W-1:0] Exponent_rnd; - iob_fp_round #( - .DATA_W (MAN_W), - .EXP_W (EXP_W) - ) - round0 - ( - .exponent_i (Exponent_reg), - .mantissa_i (Mantissa_reg), - - .exponent_rnd_o (Exponent_rnd), - .mantissa_rnd_o (Mantissa_rnd) - ); - - // Pack - wire [MAN_W-2:0] Mantissa = Mantissa_rnd[MAN_W-2:0]; - wire [EXP_W-1:0] Exponent = Exponent_rnd; - wire Sign = Temp_sign_reg2; - -`ifdef SPECIAL_CASES - wire [DATA_W-1:0] res_in = special? res_special: {Sign, Exponent, Mantissa}; - wire done_in = special? start_i: done_int3; -`else - wire [DATA_W-1:0] res_in = {Sign, Exponent, Mantissa}; - wire done_in = done_int3; -`endif - - // pipeline stage 4 - always @(posedge clk_i) begin - if (rst_i) begin - res_o <= {DATA_W{1'b0}}; - done_o <= 1'b0; - end else begin - res_o <= res_in; - done_o <= done_in; - end - end - - // Not implemented yet! - assign overflow_o = 1'b0; - assign underflow_o = 1'b0; - assign exception_o = 1'b0; - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_mul/iob_fp_mul.py b/lib/hardware/arith_logic/iob_fp/iob_fp_mul/iob_fp_mul.py deleted file mode 100755 index 0b84f4c8c..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_mul/iob_fp_mul.py +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_fp_special", - "instance_name": "iob_fp_special_inst", - }, - { - "core_name": "iob_fp_round", - "instance_name": "iob_fp_round_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_round/iob_fp_round.py b/lib/hardware/arith_logic/iob_fp/iob_fp_round/iob_fp_round.py deleted file mode 100755 index fa0140eae..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_round/iob_fp_round.py +++ /dev/null @@ -1,121 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "24", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "EXP_W", - "type": "F", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "Exponent width", - }, - ], - "ports": [ - { - "name": "exponent_i", - "descr": "Input port", - "signals": [ - { - "name": "exponent", - "width": "EXP_W", - "direction": "input", - }, - ], - }, - { - "name": "mantissa_i", - "descr": "Input port", - "signals": [ - { - "name": "mantissa", - "width": "DATA_W+3", - "direction": "input", - }, - ], - }, - { - "name": "exponent_rnd_o", - "descr": "Output port", - "signals": [ - { - "name": "exponent_rnd", - "width": "EXP_W", - "direction": "output", - }, - ], - }, - { - "name": "mantissa_rnd_o", - "descr": "Output port", - "signals": [ - { - "name": "mantissa_rnd", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "round", - "descr": "round wire", - "signals": [ - {"name": "round", "width": 1}, - ], - }, - { - "name": "mantissa_rnd_int", - "descr": "mantissa_rnd wire", - "signals": [ - {"name": "mantissa_rnd_int", "width": "DATA_W"}, - ], - }, - { - "name": "lzc", - "descr": "lzc wire", - "signals": [ - {"name": "lzc", "width": "$clog2(DATA_W)"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_fp_clz", - "instance_name": "clz0", - "parameters": { - "DATA_W": "DATA_W", - }, - "connect": { - "data_i": "mantissa_rnd_int", - "data_o": "lzc", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign round = ~mantissa_i[2]? 1'b0: ~|mantissa_i[1:0] & ~mantissa_i[3]? 1'b0: 1'b1; - assign mantissa_rnd_int = round? mantissa_i[DATA_W+3-1:3] + 1'b1: mantissa_i[DATA_W+3-1:3]; - assign exponent_rnd_o = exponent_i - {{(EXP_W-$clog2(DATA_W)){1'b0}},lzc}; - assign mantissa_rnd_o = mantissa_rnd_int << lzc; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_special/iob_fp_special.py b/lib/hardware/arith_logic/iob_fp/iob_fp_special/iob_fp_special.py deleted file mode 100755 index 448f1f495..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_special/iob_fp_special.py +++ /dev/null @@ -1,155 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "width", - }, - { - "name": "EXP_W", - "type": "P", - "val": 8, - "min": "NA", - "max": "NA", - "descr": "EXP_W value.", - }, - { - "name": "MAN_W", - "type": "F", - "val": "DATA_W-EXP_W", - "min": "NA", - "max": "NA", - "descr": "MAN_W value.", - }, - ], - "ports": [ - { - "name": "data_i", - "descr": "Input port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "nan_o", - "descr": "Output port", - "signals": [ - { - "name": "nan", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "infinite_o", - "descr": "Output port", - "signals": [ - { - "name": "infinite", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "zero_o", - "descr": "Output port", - "signals": [ - { - "name": "zero", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "sub_normal_o", - "descr": "Output port", - "signals": [ - { - "name": "sub_normal", - "width": 1, - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "sign", - "descr": "sign wire", - "signals": [ - {"name": "sign", "width": 1}, - ], - }, - { - "name": "exponent", - "descr": "exponent wire", - "signals": [ - {"name": "exponent", "width": "EXP_W"}, - ], - }, - { - "name": "mantissa", - "descr": "mantissa wire", - "signals": [ - {"name": "mantissa", "width": "MAN_W-1"}, - ], - }, - { - "name": "exp_all_ones", - "descr": "exp_all_ones wire", - "signals": [ - {"name": "exp_all_ones", "width": 1}, - ], - }, - { - "name": "exp_zero", - "descr": "exp_zero wire", - "signals": [ - {"name": "exp_zero", "width": 1}, - ], - }, - { - "name": "man_zero", - "descr": "man_zero wire", - "signals": [ - {"name": "man_zero", "width": 1}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - assign sign = data_i[DATA_W-1]; - assign exponent = data_i[DATA_W-2 -: EXP_W]; - assign mantissa = data_i[MAN_W-2:0]; - assign exp_all_ones = &exponent; - assign exp_zero = ~|exponent; - assign man_zero = ~|mantissa; - assign nan_o = exp_all_ones & ~man_zero; - assign infinite_o = exp_all_ones & man_zero; - assign zero_o = exp_zero & man_zero; - assign sub_normal_o = exp_zero & ~man_zero; - - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_sqrt/iob_fp_sqrt.py b/lib/hardware/arith_logic/iob_fp/iob_fp_sqrt/iob_fp_sqrt.py deleted file mode 100755 index 4c62577b2..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_sqrt/iob_fp_sqrt.py +++ /dev/null @@ -1,385 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "EXP_W", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "EXP width", - }, - { - "name": "MAN_W", - "type": "F", - "val": "DATA_W-EXP_W", - "min": "NA", - "max": "NA", - "descr": "MAN width", - }, - { - "name": "BIAS", - "type": "F", - "val": "2**(EXP_W-1)-1", - "min": "NA", - "max": "NA", - "descr": "BIAS width", - }, - { - "name": "EXTRA", - "type": "F", - "val": 3, - "min": "NA", - "max": "NA", - "descr": "EXTRA", - }, - { - "name": "END_COUNT", - "type": "F", - "val": "MAN_W+EXTRA-1+4", - "min": "NA", - "max": "NA", - "descr": "END_COUNT", - }, - { - "name": "COUNT_W", - "type": "F", - "val": "$clog2(END_COUNT+1)", - "min": "NA", - "max": "NA", - "descr": "COUNT width", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Input port", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "start_i", - "descr": "Input port", - "signals": [ - { - "name": "start", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "op_i", - "descr": "Input port", - "signals": [ - { - "name": "op", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "done_o", - "descr": "Output port", - "signals": [ - { - "name": "done", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "overflow_o", - "descr": "Output port", - "signals": [ - { - "name": "overflow", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "underflow_o", - "descr": "Output port", - "signals": [ - { - "name": "underflow", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "exception_o", - "descr": "Output port", - "signals": [ - { - "name": "exception", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "res_o", - "descr": "Output port", - "signals": [ - { - "name": "res", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "A_Mantissa", - "descr": "A_Mantissa wire", - "signals": [ - {"name": "A_Mantissa", "width": "MAN_W"}, - ], - }, - { - "name": "A_Exponent", - "descr": "A_Exponent wire", - "signals": [ - {"name": "A_Exponent", "width": "EXP_W"}, - ], - }, - { - "name": "A_sign", - "descr": "A_sign wire", - "signals": [ - {"name": "A_sign", "width": 1}, - ], - }, - { - "name": "Temp_Mantissa", - "descr": "Temp_Mantissa wire", - "signals": [ - {"name": "Temp_Mantissa", "width": "MAN_W+1"}, - ], - }, - { - "name": "Temp_Computed_Exponent", - "descr": "Temp_Computed_Exponent wire", - "signals": [ - {"name": "Temp_Computed_Exponent", "width": "EXP_W"}, - ], - }, - { - "name": "Mantissa", - "descr": "Mantissa wire", - "signals": [ - {"name": "Mantissa", "width": "MAN_W-1"}, - ], - }, - { - "name": "Exponent", - "descr": "Exponent wire", - "signals": [ - {"name": "Exponent", "width": "EXP_W"}, - ], - }, - { - "name": "Sign", - "descr": "Sign wire", - "signals": [ - {"name": "Sign", "width": 1}, - ], - }, - { - "name": "iob_fp_sqrt_int", - "descr": "iob_fp_sqrt_int wire", - "signals": [ - {"name": "iob_fp_sqrt_int", "width": 26}, - ], - }, - { - "name": "Do_start_int", - "descr": "Do_start wire", - "signals": [ - {"name": "Do_start", "width": 1}, - ], - }, - { - "name": "Equal_zero_reg", - "descr": "Equal_zero_reg wire", - "signals": [ - {"name": "Equal_zero_reg", "width": 1}, - ], - }, - { - "name": "A_Exponent_diff_reg", - "descr": "A_Exponent_diff_reg wire", - "signals": [ - {"name": "A_Exponent_diff_reg", "width": "EXP_W"}, - ], - }, - { - "name": "A_sign_reg", - "descr": "A_sign_reg wire", - "signals": [ - {"name": "A_sign_reg", "width": 1}, - ], - }, - { - "name": "A_Exponent_reg", - "descr": "A_Exponent_reg wire", - "signals": [ - {"name": "A_Exponent_reg", "width": "EXP_W"}, - ], - }, - { - "name": "A_Mantissa_reg", - "descr": "A_Mantissa_reg wire", - "signals": [ - {"name": "A_Mantissa_reg", "width": "EXP_W"}, - ], - }, - { - "name": "Temp_Exponent_reg", - "descr": "Temp_Exponent_reg wire", - "signals": [ - {"name": "Temp_Exponent_reg", "width": "EXP_W"}, - ], - }, - { - "name": "Temp_Mantissa_reg", - "descr": "Temp_Mantissa_reg wire", - "signals": [ - {"name": "Temp_Mantissa_reg", "width": "EXP_W"}, - ], - }, - { - "name": "counter", - "descr": "counter wire", - "signals": [ - {"name": "counter", "width": 1}, - ], - }, - { - "name": "done_int", - "descr": "done wire", - "signals": [ - {"name": "done_int", "width": 1}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_int_sqrt", - "instance_name": "int_sqrt", - "parameters": {"DATA_W": "MAN_W+2", "FRACTIONAL_W": "MAN_W"}, - "connect": { - "clk_i": "clk_i", - "rst_i": "rst_i", - "start_i": "Do_start_int", - "done_o": "done_int", - "op_i": "iob_fp_sqrt_int", - "res_o": "Temp_Mantissa", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign iob_fp_sqrt_int=A_Exponent_diff_reg[0] ? {2'b00,A_Mantissa_reg} : {1'b0,A_Mantissa_reg,1'b0}; - assign done_o = (counter == END_COUNT[COUNT_W-1:0])? 1'b1: 1'b0; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - counter <= END_COUNT[COUNT_W-1:0]; - end else if (start_i) begin - counter <= 0; - end else if (~done_o) begin - counter <= counter + 1'b1; - end - end - always @(posedge clk_i) begin - if (rst_i) begin - A_sign_reg <= 1'b0; - A_Exponent_reg <= {EXP_W{1'b0}}; - A_Mantissa_reg <= {MAN_W{1'b0}}; - A_Exponent_diff_reg <= 0; - Equal_zero_reg <= 1'b0; - - Do_start <= 1'b0; - end else begin - if(start_i) begin // This unit is not fully pipelinable, due to the use of int_sqrt, so just register at the start and reuse when needed - A_sign_reg <= A_sign; - A_Exponent_reg <= A_Exponent; - A_Mantissa_reg <= A_Mantissa; - A_Exponent_diff_reg <= A_Exponent - BIAS; - Equal_zero_reg <= (A_Exponent == 0) && (op_i[MAN_W-2:0] == 0); - end - - Do_start <= start_i; - end - end - always @(posedge clk_i) begin - if (rst_i) begin - Temp_Exponent_reg <= {EXP_W{1'b0}}; - Temp_Mantissa_reg <= {(MAN_W-1){1'b0}}; - end else begin - - if(A_sign_reg || Equal_zero_reg) begin - Temp_Exponent_reg <= 0; - Temp_Mantissa_reg <= 0; - end else begin - Temp_Exponent_reg <= BIAS + Temp_Computed_Exponent; - Temp_Mantissa_reg <= A_Exponent_diff_reg[0] ? Temp_Mantissa[MAN_W-2:0] : Temp_Mantissa[MAN_W-1:1]; - end - end - end - // pipeline stage 4 - always @(posedge clk_i) begin - if (rst_i) begin - res_o <= {DATA_W{1'b0}}; - end else begin - res_o <= {Sign, Exponent, Mantissa}; - end - end - - assign overflow_o = 1'b0; - assign underflow_o = 1'b0; - assign exception_o = A_sign_reg; // Cannot perform sqrt of negative numbers - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/hardware/src/iob_fp_uint2float.v b/lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/hardware/src/iob_fp_uint2float.v deleted file mode 100755 index bd5226105..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/hardware/src/iob_fp_uint2float.v +++ /dev/null @@ -1,856 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -//This file was automatically generated by the python 'pipeliner' script -//This module has a latency of 5 clocks -`timescale 1ns / 1ps - -module iob_fp_uint2float - ( - input clk_i, - input rst_i, - - input start_i, - output done_o, - - input [31:0] op_i, - output [31:0] res_o - ); - - localparam END_COUNT = 4; - - reg [3:0] counter; - assign done_o = (counter == END_COUNT)? 1'b1: 1'b0; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - counter <= END_COUNT; - end else if (start_i) begin - counter <= 0; - end else if (~done_o) begin - counter <= counter + 1'b1; - end - end - - wire [31:0] s_0; - wire [31:0] s_1; - wire [31:0] s_2; - wire [0:0] s_3; - wire [30:0] s_4; - wire [31:0] s_5; - wire [31:0] s_6; - wire [31:0] s_7; - wire [30:0] s_8; - wire [31:0] s_9; - wire [31:0] s_10; - wire [31:0] s_11; - wire [30:0] s_12; - wire [31:0] s_13; - wire [31:0] s_14; - wire [8:0] s_15; - wire [8:0] s_16; - wire [7:0] s_17; - wire [22:0] s_18; - wire [23:0] s_19; - wire [23:0] s_20; - wire [23:0] s_21; - wire [24:0] s_22; - wire [24:0] s_23; - wire [24:0] s_24; - wire [24:0] s_25; - wire [23:0] s_26; - wire [31:0] s_27; - wire [31:0] s_28; - wire [31:0] s_29; - wire [31:0] s_30; - wire [31:0] s_31; - wire [5:0] s_32; - wire [5:0] s_33; - wire [0:0] s_34; - wire [0:0] s_35; - wire [4:0] s_36; - wire [0:0] s_37; - wire [0:0] s_38; - wire [3:0] s_39; - wire [0:0] s_40; - wire [0:0] s_41; - wire [2:0] s_42; - wire [0:0] s_43; - wire [0:0] s_44; - wire [1:0] s_45; - wire [0:0] s_46; - wire [0:0] s_47; - wire [0:0] s_48; - wire [1:0] s_49; - wire [3:0] s_50; - wire [7:0] s_51; - wire [15:0] s_52; - wire [0:0] s_53; - wire [0:0] s_54; - wire [0:0] s_55; - wire [0:0] s_56; - wire [0:0] s_57; - wire [0:0] s_58; - wire [0:0] s_59; - wire [1:0] s_60; - wire [0:0] s_61; - wire [0:0] s_62; - wire [0:0] s_63; - wire [1:0] s_64; - wire [0:0] s_65; - wire [0:0] s_66; - wire [0:0] s_67; - wire [0:0] s_68; - wire [0:0] s_69; - wire [0:0] s_70; - wire [1:0] s_71; - wire [0:0] s_72; - wire [0:0] s_73; - wire [0:0] s_74; - wire [0:0] s_75; - wire [0:0] s_76; - wire [0:0] s_77; - wire [2:0] s_78; - wire [0:0] s_79; - wire [0:0] s_80; - wire [1:0] s_81; - wire [0:0] s_82; - wire [0:0] s_83; - wire [0:0] s_84; - wire [1:0] s_85; - wire [3:0] s_86; - wire [0:0] s_87; - wire [0:0] s_88; - wire [0:0] s_89; - wire [0:0] s_90; - wire [0:0] s_91; - wire [0:0] s_92; - wire [0:0] s_93; - wire [1:0] s_94; - wire [0:0] s_95; - wire [0:0] s_96; - wire [0:0] s_97; - wire [1:0] s_98; - wire [0:0] s_99; - wire [0:0] s_100; - wire [0:0] s_101; - wire [0:0] s_102; - wire [0:0] s_103; - wire [0:0] s_104; - wire [1:0] s_105; - wire [0:0] s_106; - wire [0:0] s_107; - wire [0:0] s_108; - wire [0:0] s_109; - wire [0:0] s_110; - wire [2:0] s_111; - wire [0:0] s_112; - wire [0:0] s_113; - wire [1:0] s_114; - wire [1:0] s_115; - wire [1:0] s_116; - wire [0:0] s_117; - wire [3:0] s_118; - wire [0:0] s_119; - wire [0:0] s_120; - wire [2:0] s_121; - wire [0:0] s_122; - wire [0:0] s_123; - wire [1:0] s_124; - wire [0:0] s_125; - wire [0:0] s_126; - wire [0:0] s_127; - wire [1:0] s_128; - wire [3:0] s_129; - wire [7:0] s_130; - wire [0:0] s_131; - wire [0:0] s_132; - wire [0:0] s_133; - wire [0:0] s_134; - wire [0:0] s_135; - wire [0:0] s_136; - wire [0:0] s_137; - wire [1:0] s_138; - wire [0:0] s_139; - wire [0:0] s_140; - wire [0:0] s_141; - wire [1:0] s_142; - wire [0:0] s_143; - wire [0:0] s_144; - wire [0:0] s_145; - wire [0:0] s_146; - wire [0:0] s_147; - wire [0:0] s_148; - wire [1:0] s_149; - wire [0:0] s_150; - wire [0:0] s_151; - wire [0:0] s_152; - wire [0:0] s_153; - wire [0:0] s_154; - wire [0:0] s_155; - wire [2:0] s_156; - wire [0:0] s_157; - wire [0:0] s_158; - wire [1:0] s_159; - wire [0:0] s_160; - wire [0:0] s_161; - wire [0:0] s_162; - wire [1:0] s_163; - wire [3:0] s_164; - wire [0:0] s_165; - wire [0:0] s_166; - wire [0:0] s_167; - wire [0:0] s_168; - wire [0:0] s_169; - wire [0:0] s_170; - wire [0:0] s_171; - wire [1:0] s_172; - wire [0:0] s_173; - wire [0:0] s_174; - wire [0:0] s_175; - wire [1:0] s_176; - wire [0:0] s_177; - wire [0:0] s_178; - wire [0:0] s_179; - wire [0:0] s_180; - wire [0:0] s_181; - wire [0:0] s_182; - wire [1:0] s_183; - wire [0:0] s_184; - wire [0:0] s_185; - wire [0:0] s_186; - wire [0:0] s_187; - wire [0:0] s_188; - wire [2:0] s_189; - wire [0:0] s_190; - wire [0:0] s_191; - wire [1:0] s_192; - wire [1:0] s_193; - wire [1:0] s_194; - wire [3:0] s_195; - wire [0:0] s_196; - wire [0:0] s_197; - wire [2:0] s_198; - wire [2:0] s_199; - wire [2:0] s_200; - wire [0:0] s_201; - wire [4:0] s_202; - wire [0:0] s_203; - wire [0:0] s_204; - wire [3:0] s_205; - wire [0:0] s_206; - wire [0:0] s_207; - wire [2:0] s_208; - wire [0:0] s_209; - wire [0:0] s_210; - wire [1:0] s_211; - wire [0:0] s_212; - wire [0:0] s_213; - wire [0:0] s_214; - wire [1:0] s_215; - wire [3:0] s_216; - wire [7:0] s_217; - wire [15:0] s_218; - wire [0:0] s_219; - wire [0:0] s_220; - wire [0:0] s_221; - wire [0:0] s_222; - wire [0:0] s_223; - wire [0:0] s_224; - wire [0:0] s_225; - wire [1:0] s_226; - wire [0:0] s_227; - wire [0:0] s_228; - wire [0:0] s_229; - wire [1:0] s_230; - wire [0:0] s_231; - wire [0:0] s_232; - wire [0:0] s_233; - wire [0:0] s_234; - wire [0:0] s_235; - wire [0:0] s_236; - wire [1:0] s_237; - wire [0:0] s_238; - wire [0:0] s_239; - wire [0:0] s_240; - wire [0:0] s_241; - wire [0:0] s_242; - wire [0:0] s_243; - wire [2:0] s_244; - wire [0:0] s_245; - wire [0:0] s_246; - wire [1:0] s_247; - wire [0:0] s_248; - wire [0:0] s_249; - wire [0:0] s_250; - wire [1:0] s_251; - wire [3:0] s_252; - wire [0:0] s_253; - wire [0:0] s_254; - wire [0:0] s_255; - wire [0:0] s_256; - wire [0:0] s_257; - wire [0:0] s_258; - wire [0:0] s_259; - wire [1:0] s_260; - wire [0:0] s_261; - wire [0:0] s_262; - wire [0:0] s_263; - wire [1:0] s_264; - wire [0:0] s_265; - wire [0:0] s_266; - wire [0:0] s_267; - wire [0:0] s_268; - wire [0:0] s_269; - wire [0:0] s_270; - wire [1:0] s_271; - wire [0:0] s_272; - wire [0:0] s_273; - wire [0:0] s_274; - wire [0:0] s_275; - wire [0:0] s_276; - wire [2:0] s_277; - wire [0:0] s_278; - wire [0:0] s_279; - wire [1:0] s_280; - wire [1:0] s_281; - wire [1:0] s_282; - wire [0:0] s_283; - wire [3:0] s_284; - wire [0:0] s_285; - wire [0:0] s_286; - wire [2:0] s_287; - wire [0:0] s_288; - wire [0:0] s_289; - wire [1:0] s_290; - wire [0:0] s_291; - wire [0:0] s_292; - wire [0:0] s_293; - wire [1:0] s_294; - wire [3:0] s_295; - wire [7:0] s_296; - wire [0:0] s_297; - wire [0:0] s_298; - wire [0:0] s_299; - wire [0:0] s_300; - wire [0:0] s_301; - wire [0:0] s_302; - wire [0:0] s_303; - wire [1:0] s_304; - wire [0:0] s_305; - wire [0:0] s_306; - wire [0:0] s_307; - wire [1:0] s_308; - wire [0:0] s_309; - wire [0:0] s_310; - wire [0:0] s_311; - wire [0:0] s_312; - wire [0:0] s_313; - wire [0:0] s_314; - wire [1:0] s_315; - wire [0:0] s_316; - wire [0:0] s_317; - wire [0:0] s_318; - wire [0:0] s_319; - wire [0:0] s_320; - wire [0:0] s_321; - wire [2:0] s_322; - wire [0:0] s_323; - wire [0:0] s_324; - wire [1:0] s_325; - wire [0:0] s_326; - wire [0:0] s_327; - wire [0:0] s_328; - wire [1:0] s_329; - wire [3:0] s_330; - wire [0:0] s_331; - wire [0:0] s_332; - wire [0:0] s_333; - wire [0:0] s_334; - wire [0:0] s_335; - wire [0:0] s_336; - wire [0:0] s_337; - wire [1:0] s_338; - wire [0:0] s_339; - wire [0:0] s_340; - wire [0:0] s_341; - wire [1:0] s_342; - wire [0:0] s_343; - wire [0:0] s_344; - wire [0:0] s_345; - wire [0:0] s_346; - wire [0:0] s_347; - wire [0:0] s_348; - wire [1:0] s_349; - wire [0:0] s_350; - wire [0:0] s_351; - wire [0:0] s_352; - wire [0:0] s_353; - wire [0:0] s_354; - wire [2:0] s_355; - wire [0:0] s_356; - wire [0:0] s_357; - wire [1:0] s_358; - wire [1:0] s_359; - wire [1:0] s_360; - wire [3:0] s_361; - wire [0:0] s_362; - wire [0:0] s_363; - wire [2:0] s_364; - wire [2:0] s_365; - wire [2:0] s_366; - wire [4:0] s_367; - wire [0:0] s_368; - wire [0:0] s_369; - wire [3:0] s_370; - wire [3:0] s_371; - wire [3:0] s_372; - wire [0:0] s_373; - wire [23:0] s_374; - wire [0:0] s_375; - wire [0:0] s_376; - wire [0:0] s_377; - wire [0:0] s_378; - wire [0:0] s_379; - wire [0:0] s_380; - wire [0:0] s_381; - wire [0:0] s_382; - wire [0:0] s_383; - wire [5:0] s_384; - wire [0:0] s_385; - wire [0:0] s_386; - wire [0:0] s_387; - wire [23:0] s_388; - wire [0:0] s_389; - wire [31:0] s_390; - wire [8:0] s_391; - wire [7:0] s_392; - wire [7:0] s_393; - wire [7:0] s_394; - wire [7:0] s_395; - wire [7:0] s_396; - wire [7:0] s_397; - wire [6:0] s_398; - wire [22:0] s_399; - wire [0:0] s_400; - wire [0:0] s_401; - wire [7:0] s_402; - wire [0:0] s_403; - wire [0:0] s_404; - wire [0:0] s_405; - wire [23:0] s_406; - wire [0:0] s_407; - wire [0:0] s_408; - - assign s_0 = s_408?s_1:s_5; - iob_fp_dq #(32, 5) dq_s_1 (clk_i, rst_i, s_1, s_2); - assign s_2 = {s_3,s_4}; - assign s_3 = 1'd0; - assign s_4 = 31'd2143289344; - assign s_5 = s_407?s_6:s_9; - iob_fp_dq #(32, 5) dq_s_6 (clk_i, rst_i, s_6, s_7); - assign s_7 = {s_3,s_8}; - assign s_8 = 31'd2139095040; - assign s_9 = s_405?s_10:s_13; - iob_fp_dq #(32, 5) dq_s_10 (clk_i, rst_i, s_10, s_11); - assign s_11 = {s_3,s_12}; - assign s_12 = 31'd0; - assign s_13 = s_400?s_14:s_390; - assign s_14 = {s_15,s_18}; - iob_fp_dq #(9, 5) dq_s_15 (clk_i, rst_i, s_15, s_16); - assign s_16 = {s_3,s_17}; - assign s_17 = 8'd0; - assign s_18 = s_19[22:0]; - iob_fp_dq #(24, 1) dq_s_19 (clk_i, rst_i, s_19, s_20); - assign s_20 = s_389?s_21:s_388; - assign s_21 = s_22[24:1]; - assign s_22 = s_375?s_23:s_374; - iob_fp_dq #(25, 1) dq_s_23 (clk_i, rst_i, s_23, s_24); - assign s_24 = s_25 + s_373; - assign s_25 = s_26; - assign s_26 = s_27[31:8]; - iob_fp_dq #(32, 1) dq_s_27 (clk_i, rst_i, s_27, s_28); - assign s_28 = s_29 << s_32; - iob_fp_dq #(32, 1) dq_s_29 (clk_i, rst_i, s_29, s_30); - iob_fp_dq #(32, 1) dq_s_30 (clk_i, rst_i, s_30, s_31); - assign s_31 = op_i; - iob_fp_dq #(6, 1) dq_s_32 (clk_i, rst_i, s_32, s_33); - assign s_33 = {s_34,s_367}; - assign s_34 = s_35 & s_201; - assign s_35 = s_36[4]; - assign s_36 = {s_37,s_195}; - assign s_37 = s_38 & s_117; - assign s_38 = s_39[3]; - assign s_39 = {s_40,s_111}; - assign s_40 = s_41 & s_77; - assign s_41 = s_42[2]; - assign s_42 = {s_43,s_71}; - assign s_43 = s_44 & s_59; - assign s_44 = s_45[1]; - assign s_45 = {s_46,s_55}; - assign s_46 = s_47 & s_53; - assign s_47 = ~s_48; - assign s_48 = s_49[1]; - assign s_49 = s_50[3:2]; - assign s_50 = s_51[7:4]; - assign s_51 = s_52[15:8]; - assign s_52 = s_30[31:16]; - assign s_53 = ~s_54; - assign s_54 = s_49[0]; - assign s_55 = s_56 & s_58; - assign s_56 = ~s_57; - assign s_57 = s_49[1]; - assign s_58 = s_49[0]; - assign s_59 = s_60[1]; - assign s_60 = {s_61,s_67}; - assign s_61 = s_62 & s_65; - assign s_62 = ~s_63; - assign s_63 = s_64[1]; - assign s_64 = s_50[1:0]; - assign s_65 = ~s_66; - assign s_66 = s_64[0]; - assign s_67 = s_68 & s_70; - assign s_68 = ~s_69; - assign s_69 = s_64[1]; - assign s_70 = s_64[0]; - assign s_71 = {s_72,s_74}; - assign s_72 = s_44 & s_73; - assign s_73 = ~s_59; - assign s_74 = s_44?s_75:s_76; - assign s_75 = s_60[0:0]; - assign s_76 = s_45[0:0]; - assign s_77 = s_78[2]; - assign s_78 = {s_79,s_105}; - assign s_79 = s_80 & s_93; - assign s_80 = s_81[1]; - assign s_81 = {s_82,s_89}; - assign s_82 = s_83 & s_87; - assign s_83 = ~s_84; - assign s_84 = s_85[1]; - assign s_85 = s_86[3:2]; - assign s_86 = s_51[3:0]; - assign s_87 = ~s_88; - assign s_88 = s_85[0]; - assign s_89 = s_90 & s_92; - assign s_90 = ~s_91; - assign s_91 = s_85[1]; - assign s_92 = s_85[0]; - assign s_93 = s_94[1]; - assign s_94 = {s_95,s_101}; - assign s_95 = s_96 & s_99; - assign s_96 = ~s_97; - assign s_97 = s_98[1]; - assign s_98 = s_86[1:0]; - assign s_99 = ~s_100; - assign s_100 = s_98[0]; - assign s_101 = s_102 & s_104; - assign s_102 = ~s_103; - assign s_103 = s_98[1]; - assign s_104 = s_98[0]; - assign s_105 = {s_106,s_108}; - assign s_106 = s_80 & s_107; - assign s_107 = ~s_93; - assign s_108 = s_80?s_109:s_110; - assign s_109 = s_94[0:0]; - assign s_110 = s_81[0:0]; - assign s_111 = {s_112,s_114}; - assign s_112 = s_41 & s_113; - assign s_113 = ~s_77; - assign s_114 = s_41?s_115:s_116; - assign s_115 = s_78[1:0]; - assign s_116 = s_42[1:0]; - assign s_117 = s_118[3]; - assign s_118 = {s_119,s_189}; - assign s_119 = s_120 & s_155; - assign s_120 = s_121[2]; - assign s_121 = {s_122,s_149}; - assign s_122 = s_123 & s_137; - assign s_123 = s_124[1]; - assign s_124 = {s_125,s_133}; - assign s_125 = s_126 & s_131; - assign s_126 = ~s_127; - assign s_127 = s_128[1]; - assign s_128 = s_129[3:2]; - assign s_129 = s_130[7:4]; - assign s_130 = s_52[7:0]; - assign s_131 = ~s_132; - assign s_132 = s_128[0]; - assign s_133 = s_134 & s_136; - assign s_134 = ~s_135; - assign s_135 = s_128[1]; - assign s_136 = s_128[0]; - assign s_137 = s_138[1]; - assign s_138 = {s_139,s_145}; - assign s_139 = s_140 & s_143; - assign s_140 = ~s_141; - assign s_141 = s_142[1]; - assign s_142 = s_129[1:0]; - assign s_143 = ~s_144; - assign s_144 = s_142[0]; - assign s_145 = s_146 & s_148; - assign s_146 = ~s_147; - assign s_147 = s_142[1]; - assign s_148 = s_142[0]; - assign s_149 = {s_150,s_152}; - assign s_150 = s_123 & s_151; - assign s_151 = ~s_137; - assign s_152 = s_123?s_153:s_154; - assign s_153 = s_138[0:0]; - assign s_154 = s_124[0:0]; - assign s_155 = s_156[2]; - assign s_156 = {s_157,s_183}; - assign s_157 = s_158 & s_171; - assign s_158 = s_159[1]; - assign s_159 = {s_160,s_167}; - assign s_160 = s_161 & s_165; - assign s_161 = ~s_162; - assign s_162 = s_163[1]; - assign s_163 = s_164[3:2]; - assign s_164 = s_130[3:0]; - assign s_165 = ~s_166; - assign s_166 = s_163[0]; - assign s_167 = s_168 & s_170; - assign s_168 = ~s_169; - assign s_169 = s_163[1]; - assign s_170 = s_163[0]; - assign s_171 = s_172[1]; - assign s_172 = {s_173,s_179}; - assign s_173 = s_174 & s_177; - assign s_174 = ~s_175; - assign s_175 = s_176[1]; - assign s_176 = s_164[1:0]; - assign s_177 = ~s_178; - assign s_178 = s_176[0]; - assign s_179 = s_180 & s_182; - assign s_180 = ~s_181; - assign s_181 = s_176[1]; - assign s_182 = s_176[0]; - assign s_183 = {s_184,s_186}; - assign s_184 = s_158 & s_185; - assign s_185 = ~s_171; - assign s_186 = s_158?s_187:s_188; - assign s_187 = s_172[0:0]; - assign s_188 = s_159[0:0]; - assign s_189 = {s_190,s_192}; - assign s_190 = s_120 & s_191; - assign s_191 = ~s_155; - assign s_192 = s_120?s_193:s_194; - assign s_193 = s_156[1:0]; - assign s_194 = s_121[1:0]; - assign s_195 = {s_196,s_198}; - assign s_196 = s_38 & s_197; - assign s_197 = ~s_117; - assign s_198 = s_38?s_199:s_200; - assign s_199 = s_118[2:0]; - assign s_200 = s_39[2:0]; - assign s_201 = s_202[4]; - assign s_202 = {s_203,s_361}; - assign s_203 = s_204 & s_283; - assign s_204 = s_205[3]; - assign s_205 = {s_206,s_277}; - assign s_206 = s_207 & s_243; - assign s_207 = s_208[2]; - assign s_208 = {s_209,s_237}; - assign s_209 = s_210 & s_225; - assign s_210 = s_211[1]; - assign s_211 = {s_212,s_221}; - assign s_212 = s_213 & s_219; - assign s_213 = ~s_214; - assign s_214 = s_215[1]; - assign s_215 = s_216[3:2]; - assign s_216 = s_217[7:4]; - assign s_217 = s_218[15:8]; - assign s_218 = s_30[15:0]; - assign s_219 = ~s_220; - assign s_220 = s_215[0]; - assign s_221 = s_222 & s_224; - assign s_222 = ~s_223; - assign s_223 = s_215[1]; - assign s_224 = s_215[0]; - assign s_225 = s_226[1]; - assign s_226 = {s_227,s_233}; - assign s_227 = s_228 & s_231; - assign s_228 = ~s_229; - assign s_229 = s_230[1]; - assign s_230 = s_216[1:0]; - assign s_231 = ~s_232; - assign s_232 = s_230[0]; - assign s_233 = s_234 & s_236; - assign s_234 = ~s_235; - assign s_235 = s_230[1]; - assign s_236 = s_230[0]; - assign s_237 = {s_238,s_240}; - assign s_238 = s_210 & s_239; - assign s_239 = ~s_225; - assign s_240 = s_210?s_241:s_242; - assign s_241 = s_226[0:0]; - assign s_242 = s_211[0:0]; - assign s_243 = s_244[2]; - assign s_244 = {s_245,s_271}; - assign s_245 = s_246 & s_259; - assign s_246 = s_247[1]; - assign s_247 = {s_248,s_255}; - assign s_248 = s_249 & s_253; - assign s_249 = ~s_250; - assign s_250 = s_251[1]; - assign s_251 = s_252[3:2]; - assign s_252 = s_217[3:0]; - assign s_253 = ~s_254; - assign s_254 = s_251[0]; - assign s_255 = s_256 & s_258; - assign s_256 = ~s_257; - assign s_257 = s_251[1]; - assign s_258 = s_251[0]; - assign s_259 = s_260[1]; - assign s_260 = {s_261,s_267}; - assign s_261 = s_262 & s_265; - assign s_262 = ~s_263; - assign s_263 = s_264[1]; - assign s_264 = s_252[1:0]; - assign s_265 = ~s_266; - assign s_266 = s_264[0]; - assign s_267 = s_268 & s_270; - assign s_268 = ~s_269; - assign s_269 = s_264[1]; - assign s_270 = s_264[0]; - assign s_271 = {s_272,s_274}; - assign s_272 = s_246 & s_273; - assign s_273 = ~s_259; - assign s_274 = s_246?s_275:s_276; - assign s_275 = s_260[0:0]; - assign s_276 = s_247[0:0]; - assign s_277 = {s_278,s_280}; - assign s_278 = s_207 & s_279; - assign s_279 = ~s_243; - assign s_280 = s_207?s_281:s_282; - assign s_281 = s_244[1:0]; - assign s_282 = s_208[1:0]; - assign s_283 = s_284[3]; - assign s_284 = {s_285,s_355}; - assign s_285 = s_286 & s_321; - assign s_286 = s_287[2]; - assign s_287 = {s_288,s_315}; - assign s_288 = s_289 & s_303; - assign s_289 = s_290[1]; - assign s_290 = {s_291,s_299}; - assign s_291 = s_292 & s_297; - assign s_292 = ~s_293; - assign s_293 = s_294[1]; - assign s_294 = s_295[3:2]; - assign s_295 = s_296[7:4]; - assign s_296 = s_218[7:0]; - assign s_297 = ~s_298; - assign s_298 = s_294[0]; - assign s_299 = s_300 & s_302; - assign s_300 = ~s_301; - assign s_301 = s_294[1]; - assign s_302 = s_294[0]; - assign s_303 = s_304[1]; - assign s_304 = {s_305,s_311}; - assign s_305 = s_306 & s_309; - assign s_306 = ~s_307; - assign s_307 = s_308[1]; - assign s_308 = s_295[1:0]; - assign s_309 = ~s_310; - assign s_310 = s_308[0]; - assign s_311 = s_312 & s_314; - assign s_312 = ~s_313; - assign s_313 = s_308[1]; - assign s_314 = s_308[0]; - assign s_315 = {s_316,s_318}; - assign s_316 = s_289 & s_317; - assign s_317 = ~s_303; - assign s_318 = s_289?s_319:s_320; - assign s_319 = s_304[0:0]; - assign s_320 = s_290[0:0]; - assign s_321 = s_322[2]; - assign s_322 = {s_323,s_349}; - assign s_323 = s_324 & s_337; - assign s_324 = s_325[1]; - assign s_325 = {s_326,s_333}; - assign s_326 = s_327 & s_331; - assign s_327 = ~s_328; - assign s_328 = s_329[1]; - assign s_329 = s_330[3:2]; - assign s_330 = s_296[3:0]; - assign s_331 = ~s_332; - assign s_332 = s_329[0]; - assign s_333 = s_334 & s_336; - assign s_334 = ~s_335; - assign s_335 = s_329[1]; - assign s_336 = s_329[0]; - assign s_337 = s_338[1]; - assign s_338 = {s_339,s_345}; - assign s_339 = s_340 & s_343; - assign s_340 = ~s_341; - assign s_341 = s_342[1]; - assign s_342 = s_330[1:0]; - assign s_343 = ~s_344; - assign s_344 = s_342[0]; - assign s_345 = s_346 & s_348; - assign s_346 = ~s_347; - assign s_347 = s_342[1]; - assign s_348 = s_342[0]; - assign s_349 = {s_350,s_352}; - assign s_350 = s_324 & s_351; - assign s_351 = ~s_337; - assign s_352 = s_324?s_353:s_354; - assign s_353 = s_338[0:0]; - assign s_354 = s_325[0:0]; - assign s_355 = {s_356,s_358}; - assign s_356 = s_286 & s_357; - assign s_357 = ~s_321; - assign s_358 = s_286?s_359:s_360; - assign s_359 = s_322[1:0]; - assign s_360 = s_287[1:0]; - assign s_361 = {s_362,s_364}; - assign s_362 = s_204 & s_363; - assign s_363 = ~s_283; - assign s_364 = s_204?s_365:s_366; - assign s_365 = s_284[2:0]; - assign s_366 = s_205[2:0]; - assign s_367 = {s_368,s_370}; - assign s_368 = s_35 & s_369; - assign s_369 = ~s_201; - assign s_370 = s_35?s_371:s_372; - assign s_371 = s_202[3:0]; - assign s_372 = s_36[3:0]; - assign s_373 = 1'd1; - iob_fp_dq #(24, 1) dq_s_374 (clk_i, rst_i, s_374, s_26); - assign s_375 = s_376 & s_378; - iob_fp_dq #(1, 1) dq_s_376 (clk_i, rst_i, s_376, s_377); - assign s_377 = s_27[7]; - assign s_378 = s_379 | s_386; - assign s_379 = s_380 | s_382; - iob_fp_dq #(1, 1) dq_s_380 (clk_i, rst_i, s_380, s_381); - assign s_381 = s_27[6]; - iob_fp_dq #(1, 1) dq_s_382 (clk_i, rst_i, s_382, s_383); - assign s_383 = s_384 != s_385; - assign s_384 = s_27[5:0]; - assign s_385 = 1'd0; - iob_fp_dq #(1, 1) dq_s_386 (clk_i, rst_i, s_386, s_387); - assign s_387 = s_26[0]; - assign s_388 = s_22[23:0]; - assign s_389 = s_22[24]; - assign s_390 = {s_391,s_399}; - assign s_391 = {s_3,s_392}; - assign s_392 = s_393 + s_398; - iob_fp_dq #(8, 1) dq_s_393 (clk_i, rst_i, s_393, s_394); - assign s_394 = s_395 + s_389; - iob_fp_dq #(8, 2) dq_s_395 (clk_i, rst_i, s_395, s_396); - assign s_396 = s_397 - s_32; - assign s_397 = 8'd31; - assign s_398 = 7'd127; - assign s_399 = s_19[22:0]; - assign s_400 = s_401 & s_403; - assign s_401 = s_393 == s_402; - assign s_402 = -8'd126; - assign s_403 = ~s_404; - assign s_404 = s_19[23]; - assign s_405 = s_19 == s_406; - assign s_406 = 24'd0; - assign s_407 = 1'd0; - assign s_408 = 1'd0; - assign res_o = s_0; - -endmodule diff --git a/lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/iob_fp_uint2float.py b/lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/iob_fp_uint2float.py deleted file mode 100755 index fc3da87ab..000000000 --- a/lib/hardware/arith_logic/iob_fp/iob_fp_uint2float/iob_fp_uint2float.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_fp_dq", - "instance_name": "iob_fp_dq_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_functions/hardware/src/iob_functions.vs b/lib/hardware/arith_logic/iob_functions/hardware/src/iob_functions.vs deleted file mode 100644 index b3aabe97b..000000000 --- a/lib/hardware/arith_logic/iob_functions/hardware/src/iob_functions.vs +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -function [31:0] iob_max; - input [31:0] a; - input [31:0] b; - begin - if (a > b) iob_max = a; - else iob_max = b; - end -endfunction - -function [31:0] iob_min; - input [31:0] a; - input [31:0] b; - begin - if (a < b) iob_min = a; - else iob_min = b; - end -endfunction - -function [31:0] iob_cshift_left; - input [31:0] DATA; - input integer DATA_W; - input integer SHIFT; - begin - iob_cshift_left = (DATA << SHIFT) | (DATA >> (DATA_W - SHIFT)); - end -endfunction - -function [31:0] iob_cshift_right; - input [31:0] DATA; - input integer DATA_W; - input integer SHIFT; - begin - iob_cshift_right = (DATA >> SHIFT) | (DATA << (DATA_W - SHIFT)); - end -endfunction \ No newline at end of file diff --git a/lib/hardware/arith_logic/iob_functions/iob_functions.py b/lib/hardware/arith_logic/iob_functions/iob_functions.py deleted file mode 100644 index 39fbb46d3..000000000 --- a/lib/hardware/arith_logic/iob_functions/iob_functions.py +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_int_sqrt/iob_int_sqrt.py b/lib/hardware/arith_logic/iob_int_sqrt/iob_int_sqrt.py deleted file mode 100644 index 99195d7e3..000000000 --- a/lib/hardware/arith_logic/iob_int_sqrt/iob_int_sqrt.py +++ /dev/null @@ -1,242 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "FRACTIONAL_W", - "type": "P", - "val": 0, - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - { - "name": "REAL_W", - "type": "P", - "val": "DATA_W - FRACTIONAL_W", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - { - "name": "SIZE_W", - "type": "P", - "val": "(REAL_W / 2) + FRACTIONAL_W", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - { - "name": "END_COUNT", - "type": "F", - "val": "(DATA_W + FRACTIONAL_W) >> 1", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - { - "name": "COUNT_W", - "type": "F", - "val": "$clog2(END_COUNT)", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Input port", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "start_i", - "descr": "Input port", - "signals": [ - { - "name": "start", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "op_i", - "descr": "Input port", - "signals": [ - { - "name": "op", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "done_o", - "descr": "Output port", - "signals": [ - { - "name": "done", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "res_o", - "descr": "Output port", - "signals": [ - { - "name": "res", - "width": "SIZE_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "right", - "descr": "right wire", - "signals": [ - {"name": "right", "width": "SIZE_W+2"}, - ], - }, - { - "name": "left", - "descr": "left wire", - "signals": [ - {"name": "left", "width": "SIZE_W+2"}, - ], - }, - { - "name": "a_in", - "descr": "a_in wire", - "signals": [ - {"name": "a_in", "width": "DATA_W"}, - ], - }, - { - "name": "tmp", - "descr": "tmp wire", - "signals": [ - {"name": "tmp", "width": "SIZE_W+2"}, - ], - }, - { - "name": "q", - "descr": "q wire", - "signals": [ - {"name": "q", "width": "SIZE_W"}, - ], - }, - { - "name": "counter", - "descr": "counter wire", - "signals": [ - {"name": "counter", "width": "COUNT_W"}, - ], - }, - { - "name": "pc", - "descr": "pc wire", - "signals": [ - {"name": "pc", "width": 1}, - ], - }, - { - "name": "a", - "descr": "a wire", - "signals": [ - {"name": "a", "width": "DATA_W"}, - ], - }, - { - "name": "r", - "descr": "r wire", - "signals": [ - {"name": "r", "width": "SIZE_W+2"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - assign right = {q, r[SIZE_W+1], 1'b1}; - assign left = {r[SIZE_W-1:0], a[DATA_W-1 -: 2]}; - assign a_in = {a[DATA_W-3:0], 2'b00}; - assign tmp = r[SIZE_W+1]? left + right:left - right; - always @(posedge clk_i) begin - if (rst_i) begin - pc <= 1'd0; - end else begin - pc <= pc + 1'b1; - - case (pc) - 0: begin - if (start_i) begin - a <= op_i; - q <= 0; - r <= 0; - - counter <= 0; - end else begin - pc <= pc; - end - end - 1: begin - r <= tmp; - q <= {q[SIZE_W-2:0], ~tmp[SIZE_W+1]}; - - a <= a_in; - - if (counter != END_COUNT[COUNT_W:0] - 1) begin - counter <= counter + 1'b1; - pc <= pc; - end else begin - pc <= 1'b0; - end - end - default:; - endcase - end - end - - assign res_o = q; - assign done_o = ~pc; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_prio_enc/hardware/simulation/src/iob_prio_enc_tb.v b/lib/hardware/arith_logic/iob_prio_enc/hardware/simulation/src/iob_prio_enc_tb.v deleted file mode 100644 index 1cafaa64d..000000000 --- a/lib/hardware/arith_logic/iob_prio_enc/hardware/simulation/src/iob_prio_enc_tb.v +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -module iob_prio_enc_tb; - - localparam W = 8; - - reg [ W-1:0] data_i = 1; - wire [$clog2(W)-1:0] data_o; - - integer i; - integer fd; - - initial begin -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - - for (i = 0; i < 2 ** W; i = i + 1) begin - #10 data_i = i; - end - #10 - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end - - iob_prio_enc #( - .W(W) - ) iob_prio_enc_inst ( - .unencoded_i(data_i), - .encoded_o (data_o) - ); - -endmodule - diff --git a/lib/hardware/arith_logic/iob_prio_enc/iob_prio_enc.py b/lib/hardware/arith_logic/iob_prio_enc/iob_prio_enc.py deleted file mode 100644 index 25ec291c0..000000000 --- a/lib/hardware/arith_logic/iob_prio_enc/iob_prio_enc.py +++ /dev/null @@ -1,82 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "MODE", - "type": "P", - "val": '"LOW"', - "min": "NA", - "max": "NA", - "descr": "'LOW' = Prioritize smaller index", - }, - ], - "ports": [ - { - "name": "unencoded_i", - "descr": "Input port", - "signals": [ - { - "name": "unencoded", - "width": "W", - "direction": "input", - }, - ], - }, - { - "name": "encoded_o", - "descr": "Output port", - "signals": [ - { - "name": "encoded", - "width": "$clog2(W)", - "direction": "output", - "isvar": True, - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - integer pos; - generate - if (MODE == "LOW") begin : gen_low_prio - always @* begin - encoded_o = {$clog2(W) {1'd0}}; //In case input is 0 - for (pos = W - 1; pos != -1; pos = pos - 1) begin - if (unencoded_i[pos]) begin - encoded_o = pos; - end - end - end - end else begin : gen_highest_prio //MODE == "HIGH" - always @* begin - encoded_o = {$clog2(W) {1'd0}}; //In case input is 0 - for (pos = {W{1'd0}}; pos < W; pos = pos + 1) begin - if (unencoded_i[pos]) begin - encoded_o = pos; - end - end - end - end - endgenerate - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/arith_logic/iob_xor/iob_xor.py b/lib/hardware/arith_logic/iob_xor/iob_xor.py deleted file mode 100644 index 0ff6b4610..000000000 --- a/lib/hardware/arith_logic/iob_xor/iob_xor.py +++ /dev/null @@ -1,78 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "N", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - ], - "ports": [ - { - "name": "in_i", - "descr": "Input port", - "signals": [ - { - "name": "in", - "width": "N*W", - "direction": "input", - }, - ], - }, - { - "name": "out_o", - "descr": "Output port", - "signals": [ - { - "name": "out", - "width": "W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "xor_vec", - "descr": "xor_vec wire", - "signals": [ - {"name": "xor_vec", "width": "N*W"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": f""" - assign xor_vec[0 +: W] = in_i[0 +: W]; - - genvar i; - generate - for (i = 1; i < N; i = i + 1) begin : gen_mux - assign xor_vec[i*W +: W] = in_i[i*W +: W] ^ xor_vec[(i-1)*W +: W]; - end - endgenerate - - assign out_o = xor_vec[(N-1)*W +: W]; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/apb2iob/apb2iob.py b/lib/hardware/buses/apb2iob/apb2iob.py deleted file mode 100644 index 478fed777..000000000 --- a/lib/hardware/buses/apb2iob/apb2iob.py +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "apb_s", - "interface": { - "type": "apb", - "subtype": "slave", - }, - "descr": "APB interface", - }, - { - "name": "iob_m", - "interface": { - "type": "iob", - "subtype": "master", - }, - "descr": "CPU native interface", - }, - ], - "blocks": [ - { - "core_name": "iob_reg_e", - "instance_name": "iob_reg_e_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/apb2iob/document/ARM_AMBA3_APB.pdf b/lib/hardware/buses/apb2iob/document/ARM_AMBA3_APB.pdf deleted file mode 100644 index d97df5920ae4b1ca9e7ea90f9e3d549008197d35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334313 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�)(2|@1hKYRs$-vxg(Ne8 zGc)ub3Qlh=ks2Xxt|i_!00C0+Dsu4bnrS_|TC#1n2|Xj03wFGVFt`#iEUl#wua3Y&GE`4Qk%buIG$pN9i`!&J|%6mI1v(a?I0Yl&{ zG^@K8Mu|Kl0>Ujaeo^R2ix^}%_IhVZ?`Df1;VK#Z30h5$))N^V*=6-pMCke<9Yk6EbAjI?=1h@qd6{Wy`xXLTLw}MI_5R;e4r=8 zaArr2H?tBFAJFh@g3|!|{DB}yZ^UIelZofS$)}uc7Mc$J2?diTuzDB!eS`CD_N-)spl|u^s#gRn|dmw96F{-hrARR^JCv=f7RAuw_Fu25^#KuQ9Q!W5q*-TmEX`7 zVL`J~1PKY{C*3GW$<`<$LzyAYOXn*H@=o|76-GX`Xu})w3^3mlMb|_?r35I&2+`$hv`9;6juXYx1bh^j&y}JyMqYmF5 za^xt?d3+43XHU&QKMII~o6H(Z&AY^&1XKkut7xR8T30|Jy(oDPjuUK7<{6F8CtCSA_<) z5Es8jKV;jly{3J$)x?AP-3SJxOlkDsw&dWM*WF`6I;TZA5leAf8Qk!YpP-N>`$r1u zUa-_cWW^kTba1)A@|%9B-RtWPUD}!Z+yNlP6!!OTC?HXy2a)h{!hM>&Wioz_zaM5L zldkc22YBIV*x0fVquUQ0EbSo%JkT((3|za&9SQQ-j><=!Sj{}wSEY&LtLuzp=^T_i zWW^eMsesSl1iSTX4Yw3>0b46c`OJ!rAX-$XLf^%yASQ{$qm3nqRSc+gQ|T4%9?7wz zEUO1Ss36p^hGO^nrjsgFa;+xi;BkVhn4T#IzT}biSt1r-Lj|?Jsxtv^&yb_|IW=tU zw)(pP@w!hKxFZ@6G9}}4ZZ9;+5TODAusV24IE`uMAkw)>_JAJoYI0=RY{zr;ii&u` zIzsX|2J&fL$8~a7&lHfHis^GFoqw9I%KW6j%&)H+9&7ArLbC@_L{jVxPebY$$IzYI z+H7n2AmJYajjrdM0P_Jk0_U2F(HGOZ@5E4Z7TCpdRZD;Mne$V~X|TpVa>JA!JFhJB zxs)O1jcSCn>temQS)nzR7^kMe)Cyg{>%ifYoQ9RHzP0-9f7mbhb&L$S}+S0 zds2nY;^q6~UFuXyJ9ttm(m;LR)P&^3QPmet7u9&6kd=?-;_QE0fM%T{>O z!7ul&NSXZ#gPy3jyt$YHiLHi$nvc!&CNgH7hH&`It?g&F$x-*^RJY;bB8WQxrFLGO zW+m8NL0*WkefN#akArodc%8g^?|$TQA%Tce9*)P##fpGX3LnLN86{IBmUXI_G9NY^ zT}3QIlLS>Wl1>7oeZOPU(S$VLLWvX~B@L`Ajrq|x1=&nB7ygkM(kJ*%PCh0yD8<|%w z^nWyfO+-}7NOruJ_bIw1N$dMV0(V$8i9|}1*S-ZPgFnLX=VP{eM1HGUvSaz~q|KU# zt-n#C+tGD+ZJ^bBFS!2{QLB%)w}}>wlkrJUmF5g&p#-;A7A*D6HO<(rQqRgqX?mwh za1q~alU-3OnULEgt*B>k)nc2mK?`G6PZ#x+|+T?{h%+2U9!%zLL=^OL`? zQCOWiipzs21`P$YqmzCR4G(c-UQ<(r&#Onqe$AOX`rbp?1;P(&`9?#p%KT`>aBM@Q z?v@2dCU#>Y??M@f@~rj`8HR+Sm{cuuTx$vF_6i9Z>feA!st`2y06#=1Ad zkAtJ=un;UiIT4qQ-WphJjb|4;W$crTU{28dFNRl z;Ii+eEs!h)V2SH8>|rY{#~e{krZQ7^06FX7;GJg^Y0?;B0}ZtL0c@{^8EI zPRpvYIA8`grVi@Ipi_I8Mf}lQDqKKcmsF0TSE8w)Zq1bqgN247aKG89mO`KFLrQ~Q zRIhy3=y`VBYSG-xY>>88MuZlaY_jN4hIg1;wFyI6c?LmQUm!v}$bv~{ zmM=rWN7N%eK`m%aGQL#5_{icwjiD0pKK>D^P03YYZqX9)`E#_S$S^$YoM8)6gC$6> zHWcW+!3vlpV_pD254PzwBd$oL_YQ8jHa8#Bu{fYqLO$Kpm-Ay`5U zcA=Th_^#tRrk%e2;f_yRYqmxR_iUK0DW1Ujj6&G*wBIP@Uym%YtRQ{Ya9p%_2u#Uw zQPSagH@KE>yklfRWc!%fcb;8ysFkp#4KGqSV68n4mEbdz=Ez!h?)hH|@(4&9fSd+S zc?gh<_+53}ryGY3e4=xLf80N2q)&o1ps(R$wSf~t zZ@Q${iTmWZQW_on{%bSP#xw?$igd3P1g#h-+G~)6r;QAu5we(NXikHgCTSj zq8sRS&(I#Jb?FeQ7zonx&f0Yf#^|crDD@st(_otnQ1+Yz*;vuxJr2#HskOr4x?`M~ zj~hBm*pb_e3X%(4t&jJ7<{Cn2VFFl-(}tgWyHFH2RvT--z`v8Q8>rh+OH=iVPiug~ z%77S{KF<_%b=TKB$TbQAcHQ1kF7@)ijm;B!H}hVco`Q;vW{Kg z$s<|O?|g=#{SNI7+|&~ONLpgfnNA~fu~GWB+tu4FW|lbCy9WI<=otYQY5T|eE-tW&wS)WH zb0mNh=FOv$N|obWEj}&J1t7vUk8@~J#OIp$q8&&pGhH{7iaSMjeGij>s&X=@{9BLU z(eipQER2rIs$vl4Ht3y7Or6F8bw<T$#vN`(<3uK>R?A~UcgeNT=j<}@46(tEHYM62+(vKI)IV%?^92XBb!3oQLEg~9 z9#G}9sDWMY9HxPfUmLdvcI=~+p@b}1r+-rmdI(H zK^`MxN1a`A*)Con86XI~WJaNy1Iz749W}mrYGt609&Or8;Zk~zq|K(@G_-Q@Hxo@0 zMz;;_ao|gwBs7HnXgLclInD5n%G=sN&59nZbnO&V91D~UTvZ5!5GqXqx*y2pxhq~d z(?-Fp0s{`z8m-)uy@mLEp+Ln4(@Qp4>TDQ~pfbT7i4xL_f*6#&4omi9W1wOB&S%T~ngZoxj%~6c zw@Vo|KmNklLn$ULqPMp zR@b(I-0Xfu*cKw4O@vQud014jvR4gF+I4qdtO-nAyQ!AW0`0p~&QIH*gShn+v9NoC z1a2pFJ}^J_b9Oljt}L#EeNn|7 z4Qea9d+_Fjm)A!}^bY=l&VBoORXRjle1x6v|U0CFQ4gBgP|c7+L7&MyCOmc47cN&O17kTlKZCdNXw z6O)NlE-Tl+6mePzz1*`ICG&%$Is+6-1f*>tYYaNc^8L2KBNab45SyZgU?nyhamJ+t z3#lK%mk9Ujy=;__jE|Syxb$(B&4;$GeVH5pyOQpK0{W~NT2)Z1i_11&m)hn6?b;d; zwmE@J@6tVvfiGGj?!S&E8Z71d%!Cf_Rvjzo-6$@e!!@?c{49W`;i;1so_8e$X|MU5p?l{ zEy(h8`b;z<5ARczY2v3bf_S2juoTS5n8O&5G1o@jX&y}`15X_Zmd$(5`-Hf<<-ql- z?t2kn_p55{awI4@bUKthi0lT?pd03B!K-KhhzNJ>*?0#9l%u} zKYLz5c`Y;vEmb^$5mmg=xuh{rtn@*xP|t8ll;8_Z$R|xc@pOjIY&pd&(+uo?5Y$t$ z^t)FtiE^JUt0rQX1lYfTB3Iw2kIn}iW*(JAl3Gzz|nYgEsm9aAzw6{neLg9X;p-pt73 z78(7$H{^j6 ze7AZu{eo!Em=@LtLSQG`_&nb|k_aH4VgIQ(5frqbvWqfK9i^cBv7$}z(+w>1YsG(>ExGkbtR8|6aAiVdB#YcVf@ zrH6w(>9s{7223`=Xd3Bp%Sm#K9_zRKaN6>ErS5jcn1gJcnh-ik-?7?l!G-8fQs;WW zNPH_+k;TKCezXy|?&*Ny#hX;vWue2h`n(W}c+jvCM)HI6wt6Ha;Fb14dp{iW#~$;085uJ%0$1hOY6&3ZmUM0=DxxPlakQyGRiKhh#xy-fnD{xC=Js}V zMpXsduhnO!g5D&`8HC<-sX42&C5l|Q2^rQooY8FDoS8;HI)!IQ#g_fS&$s(z4ZZIA z64`{wuWucn1s%ix;#fEU1@Nd=`HVvlmMzg~c!K>b-4LymWDsetjt2H!ZKo6KT%4lY z@BSgHi#Z8(;5-vm^-$sSnYgxusP`!ygvH^o^igz1?xlrdDg>ID4dw@)(dcNIRzL(K zNQY>=r5Jtxhpq+ER`QDrIgwKW`0fe#&t%9Rf?(2!9I<7?^GCSTqD}TPG)@;6Z)a}R z#V@GFLWZ-wv5PG|z1BJ!$Egjyb}6e2U^DSHE{%|e%PBz05^Q^07X~P;5A6I`HHKZb zf7CDNyUO?2~-c!gS|JpRfTR@8^Nw%Lu!sN?elvODxtWm-?ArcoR0TmfmI0H zd$0}1sn8?%kP9@ zo4UY9gb2}PaL8PZ^5G-NfGj^H$%M*iA+k_hLtaq~s4&jszEG{Fvzk^4Y&Y3p82GU< z%APlzBt^ekjNOdM^(th<3tTbtbu|IH_Ykz)6+zxh>_wXX@YG;|i`_e`nw)C327(0M zSOej>e&8}9qu44 zdk@mUNGz+!iY)^8;||-tB+ay=5X%c&H3+lW5$pxIl@od&c+k8gvo|=0Tz16kmc>vL zVXmwS_ryAsmv3uH8bI0yu4BIzScI+sB~1EB1f7-PB;xam(ONSoMLApZ;n{ZkJ7M5d zwK=E!2Q_dDqW*`xYPKFUQhoU#_BSkq3pW!QB%GXCor&u4c?~8`w2gbaa!7{dSx5N# zX%Fh--Ob6o9-hwvq8l)f+x~mnbi)76-};S6@{@q`Q${w~7mRdS;$D=&ANdy&&TlfZ zF|%6a$P1p|HONkF2KgI_2M`o0p}AQkg{>bwo6R&M)-l`eI~6sE^3jCY$EJq2x-)~E zb7^E8jFKkP*=|XqPLFe(#}ZSqv|x6X%rnK{csek7J|4biE3pwX%Rm2%$}9u@FGS>@ z_w~Q&GXI^L{9pWK2KwJW`>FcDK>s_h{x1m4{-g8k?`wPfX87j~|Bo|@{;%ipF~5_D ze&f*n8;R(jIDJ2vwj4}r}C=4dX#zooxIh^%4uJEf?K-(McX?C zi4vskx^3IGZQHhOthVi5ZQHh2+qP}nHqZKJ_MRPQVn>{ri*r$T74=qDWmQyVKKXnK z^<9xt?hnrn3g-T1+w|!G9SPD~PX}dYb5vTvcbEf+$SE~LVI>M1VQBZfctD+}?WE^0i65{YCotBx$?%O==|uA4u8buC zK&eGN^L92a;kzq#GbX{0`>#`Bebasd{P7v(P7|Q9XnQIZBCB=c2!zqi;JJ|cHJ&KV zq8(Z$Hcg_{CSg$8gKw7Q)2IxuhPp^-#(X>5w%vA zd0&?}V9p-jc7bh5$F1%4URnyMqNkiJz~@?c0R>M?MK1&SJ8h3_uxT_Z?baWq5>Z>6 z932QEr$i{aI(h9lyKs|F$?jsjLQvr1U&;U&PguKC7~B_$q~A(zS_e7{!(v!( zi;he-#DzO^SSLN}Wg^`Nj|oUNl?@ z`jT4o!Yt;>zna8@JU*}q@z_zq@XwD7pF@)ZE`YjCb-Fyk*X#6GNNk&f&g_E{f?d_Z zm^Gykq4Aso_?%F=J2dOw1$P^c4`tLC<-^7iBcf`Pt6+ShzPW~cp1ZZD+KI0Eb9Xb$ zg0~M6CgYZDXE1D#1QE{|d~b<~2ux%50@Mc3ru%!elgHG>D*%;of@a$240l{dsw&7- zb(SAc!lpbG8&t_DJbG)^?XAPvm)6=9AF^+^pPAp!$!Z&cKrdVEa_ICkJL|-kFVd9H zUu~hCo<|E4^C7(8-m*iaKM0 ztQlE2%7^pB%sBx3cPjfBvcI4OTI^k75C1=9Lk&)`@1mg zK1WY3R+PW&Dho3|BfCBl|L-Y*Kjd#neCRdZ)u!jPOV=PX+r$hIs>pX}K3*71T*o7C ztk&}sF1Y0)o2|e3SC*WwRv$}dS+;fnHN_`BdcnEdbClMZf3bGn|H{>Kw?u27d+-nb z%_afQ*%_WqveqR0qeaoPD<=Dh18VtN8rNSqcz(^($zv-;?pqEmAte@=hFx-)#nwNh zVawXOcQ>A%w(%UTNc?wTkX%nQ1C<(s!R`uYbtkuejyI1Rg$_#o6}@9%F(Nb{bJJdd z`$6XYSsbMC;vj_mGeD}U{_~eJeJQf)BYJVHkH&8NYFG{dZZy^WPH}tS4r)fwZm6Lv z^NriJXQCP6)R)!+^z#Xs!=e%#{`L6o7cZX@CNd6pshfNs7FX`4p4`NkrdZyAa&1wa zQ8f3XQHQuKmu2@&(xHcy|3 z{ZaF;tS~s^PtghcKS0BW4lCj7tc=EdoiY6?(ea+h$QN}<&<0mG$r@Z@^9w=ND-AQS zCoxn$i&{uUs6j+->OX7t0crtMYdcMUJ`%`4ldcWuxq&!z#$=qy$P}`KT8`TBt#{>q zVuB%DS9h<3M!gvk^oY4PUsg*1ifKmmf0x3EDXCOF2mTe%~sT8 zrN)qI-NQepFp-8;z~Uo;?R}`AnK)TbOR6l+et)t#2=MPNX55y6no+A+eB}UxorInB zeW`AX`wN+XoF{#zb9wPf#22Lzk}0tc1yF=zOXSXxc;X=8Q%!0AD#vX*?Qej#BfNFp zit%6VW2y5~(;l{e;}Y$|cY%}ebM*Iat#CIjV4gsJAi&^QMD-(aUOFK#tyUFZ!NouM1aGD&gX#V<{==L-% z?siAem1>WHzwksGHax?KU_raj$=%Cv&bpX+K2&4gcYJYEkV~I{`7YMUSdIW>eLk*$ z0n+IgPk>*VgP;rB61H5y2X*8+LtOC1I0W>BBd4X}G5&&k(gkq9t0BF2 zAuhTZ z5UV@nB{FDxNH!TGke00%ae%C^rz&?1O5Y`Sh1V&|hN+-1-K?Q5Q0+7_`VF#D!?c z3$QYQb92A##^^q#5+O-Btc*ld0z^Bd;Xy4^GjXf-!mPWA@CZHgt_kOxnSD(Bkq!9K2So@ z$15D~A+@hbu9M!jy(QS8jyn+z1^A%5Tus_ps~3XZHSdb{)?8bf_VJ<`Nj#w%ekhmt zw0^FDeMs;2A@eJBEzJP3kI;aV_U29G&C!&Rh(aMrl~watrt}}###3hv!Gt`E15wF; zr_1gWL3rFy)Oc>V>7aw(lY-K_6QnICLMJbiZRI&Y6E~^tU*RFde7)FL>8IjO6pkX& z=cc3_hh>M$*yKB@RuMquWHKC{na?H1SfZNm6@Pb~C1dCK)E(-Eow(958R|)FLB>Lr znl0uzw>?aIDbDQJ7Jk7CZfXjYqxIW<-C+i}-;q2qK7&|8g!Ims(hV z34w;|GndU+_1w{YKkEYkx_BFoeefI0o^M3IY^LJ|&S32NVAM`4lq}C&L|B$1v0W%v z(5s7i%ZMUHj(AL>^c{U1!H}MC&qlVtCT}XOizGXrpjxhF`C_T*nVDDFC_;@~LdbSD zuCBZ_lSaq0#*Q|PhPpJ}Lf;(V2*xMt>ohNGn2#T@h9DH>ccKpKN>Qe?@X~m>NfiP8 z?il2}607H)VjgKJ(CWwfm23=Xh&$r-6c7Zf0mN1jr;YelAG0AY3iAx^k@DG0)KvK@ zy28-!{J|VdcFA8Q=dCIiH@`#Wh_9Kl**I{X$$}pO=~57|1M;w}S=jnnICO!BZ=i+s z$UlfRFYeUiw=&f{v#C=*FA#;Ijq!NNULHOiwPYMHK(jke&M2vqL%UvtLweI-T_F(} zZSbkE9(hB2LrBx*d{@vs6UxK`EB^Y2nU>f`-axA#@nb$?U89$A5N&r!9;JgVlnGF$ z`2-=HK{90qv{KzXk;hDa|7+MNW+CFqnO0=@CemR2vN%05L^W@8F_`cV8okvwxo4?a zlOh@~Q~NQY<Nf@KImZ-;CHt=gyaN-M9sJtM6J4!N4=?6=x656Tpc7;OWE=4Sz>@&Glni znTCI$4ra)&cL>n0aFxsn_loP{@vXy9edD>LcIW9u(WNiSVNjItK4%Kyx0uR4ss-3_ zkW091MsxM;-u`UZiUro?H8fbk^F_6hgU~_agf537C%kb?>Xr(w!cF_0I%PJ|J_^M> z)$oiUU61MTf?74J0)(<+O(|#7Bv&6MHiY*L0la`dR}CiyxjHm4;rfZtM=@oC$56`O z@l@Oi+)&m~Ix!^Z7u+3IrT9>$HVGZ{Mfwy!~SbbEAA$<|v3_ORpbi}MA z#u~7cSA=kX^x*Wk&)@;E-#Y)jozzD-zD{7*k|v)|gp7h01UL)}Tg}b;+KPZ}BL9P7 zX!i6SK`!grYx)f{%=+~^?S2J@kHEV&7HJO`FJu?I*?GFC+rs;p02+`e&{Q~iHT)~H zz;w((Eq8m#7W@0`-0_3FP04A@MZl($g1eLE8h2lI?(o>WX@JqAX6w^BCutv#3LVnG zTVCMqad2j&OO9m;R+Rn999&O)lQXAAZt$iBIKv>PyU* zMcjsHAS@Vpqa$A{d9*XuO2Q-FmWZQMt;a~H@2QQcD7-CvgK{!Y1Bq~lqu}ciPp4jf z?5O)VEWN6FOAK@s*eO`oii!$#$Tv`_@Z<%@Hbj|~#17c+;B~hLM$TW(|P7clBoen7wTNysNf11i~~rds6hTBUj8M-g|pnS^K%cA0Cf7btyH{BGUg2m_UFbK2&D*t48dB|j?J zx5YT6&C_8NlHfZj(XwMr)6_a0;->gAUf_zcb2ptFmZUPElmzxZxL?i>s={0EtCH`Y zH*ne^<2yAuU+eBjFB2Qf-9tlk$=}OGI3M5v-59%QKfdDh{nbL zmg&}Ao}==)lCTT>5XEF75KO^tKzK;?3Ttg0V5c8)#8RbK>q3-T?&c^+JRV>95+Zs; zahrR6(31n*=1qe;L)JVrIZTc4>GoA@M0iLbr9{VZeQ@MyV?nKkSVb;Q-ge%q;Fydxz#7r8HgdSIeh1H2Rq+ZUr2QL|Y5KpB@ z#8@ZF^^P|NXRN0y3C`KCJ%O`tMmej=^Qf^f_;oOaq*O&3+1_Ln9}5K2uhEC>|Mw&F z*5{>TZ|Y=`YUAfe!8&1e;k$p3i)UbIrhkoJ!ns8)Fo^_5@6kR|@c13)zP*LiWAU7* ztK_~o97f^S3`o%(({@W?>oMB4t;(52E?!RLK?S})G^vi%dtN&s=Ct{9+lj8Trl$mt zovt+ga@w&S2|sglxvFJ0_!|O3zBQ_}Bn57c&_HImF8wq&Dn{N1Cb!>U2oRi!r<#3e zEskWeeaJx<@l;9N3POgFURziV0>mJ;2Pwe^pGjZJn91C%;6WDm)Eq~8=}X>xhU^$? zspY~)$9P4oeI62Bnbo(}>n5|J<~sLi*3)Ad*Qt#J;0xv7vcb5TLXe~Ugd)iz^ak5f zKMrfZtcX*eA0adD&Q1ab0y@CZU5q7XlZD>cD&Z7(kGi)K}cIq4fX+U;DzEy#r8?5C%QzW)B_-*m4a$xQyqfJjb{d% znMnZ3z-;Qdsy;0oAI~wv?^_j>OwHtLfwGmgcRYsT!pp3*zZ0ZRfgB#ScW`>y5taqr z3rRCJRT^K1;od<2&0?vf6#`c$`c*i|GxjvB*1ugfU_mEHcC zc9%5@(G7S592R}w%X4-HwZbDE_f9{Zb5$ZELp&vgyoE}hjK0TRIyAD#-1rh+#(;{p z{X`cR$xcI@9vpkfX$be{R1`&~2*`8y`XkhPt%MIH{y-I&(JGo^XI6o|K-YN+>$CqV81#tA%tC0=;1dD+MT zK;Np=2RVQGR7mV3?YQ@ryP!q@MvLqd?`i|PI<=DV^i4*2X<~_m<`5xRBQRe5P!jdC z;aMPr)D7sIS2hawfhiY#xmJNxD*_~aT_y9bELh(j;k$X;Q?^bJnWte2xZw?aYF7l# zPvK-*ISv|=4J)_@tk->fL?=G;&o9hkU>ij#uZC$Ab#WX5O%$sfbBjJlYp+FnHj}p4a&gg(GJy3(O8sEO<6Yx57MtENl+* zv~CJpk^F_3acBXvj_2r`)1`}K%b!d%nVZeiWfdY!PqXSkOCrb{r0n(@DizhaTnbo= z9Gl-u7W@ULhIK#gxT4)iV{_b$L^Oq=Pbj?kv{3ZcdTExEX`iy#Tb>k|5AA$!YQrb7>nLXldN1(FK}+G=M+} zoRU(aaod-bu=w*e>v0RbnK2muf~*Q{qE#2^#J2z}-w5~BXoeAb+{LE{@q&Ie7ktz0 zFU%Qwho=%8Zd)dUOD&L(^_C1h@!=g(wg&`9aVtLQp??c$Z36dY1YUDMEf*<41W*ah zickeq%+yr`NX~Ico?{&G5sP?h!z^3Z#@u}sd8`NA^BfdfqGg1c6KAi47Vz)00nWYS zIMos;nD&m(_7g{d18#6P5NS_H+*L~Qoy_`g`B6_G7kSXkjsAd_2&2MoQBW~>iS7QT z9}4;H1bCz#p#bSz;jUu|rLT$O*Kr9eBF<1h#@L2H%<+MPmaKlw5D9&FpvlM%8>t{l zN0KQLAwzr{C~1yT7x3_Rth}+Y4eqqgzS|9ZU%Q+!+=@T!$B6OzX*ACLvM>#I*VEzS9nJ0P0sOlXx zHM$ry_E4UIaZuU2tNo)h*7i0P{?m(Cth?dPi`52YbQ0OL_%PyNnip4V9{;e)e(M%g zbFBX*dN|^->cNudR|B^(Hj{?X@!si(6D0@Wr z;ALH(h7|zGxJHWJw|u+wCSF3S45R(jtPQ%fB4~x{MQi&$Np@0NE6X51EdeU(6h%%z<=_@%`e?3Uh z34M!6_ybp4=1d-L@n`$&iZoe+QRcU&j?d=dWbzk`D-ryPU+8ej!18SJo1l>y_y^8| zUk|IaE8Q>TjtR5DoU^&qLalZUYaVk`zw;vx2EQ@riy9aL zq!~qlze+dW1g1r8TiCb%v4!f#Pz#lJz0{rpKG%LLOe{XQm)k-7D@ z#prbax)gd7G}&)~w3jb!*p4ijW`VhuP8sRkTqg2I-% z?p-{J_-Tvnp`u@S(?R3|*DW7LckAq{{BJw-(QOM(X`Kqi4_j~`z2+uR9yZv%lr%yi zA_rr9IbXN000+ zp4~Trw_+!3joySYt{$ke#XiCK=jv)ODFXG>a;=-M=x3C9?xAKJUV#lFhP97IT!hm- z5omC57$81_a+4g;JPUBej)d=}y^P1&TSg#bSj?M+X4tPBcu%&m`C6w!FKt82X>+!g z@n51W=PP1amM3iUvEm^2!%5kFYL1dMfanzh6K7s-{hNCazJkT!b0N9z5i%nABH)l_ z2ACkFR=uGooGQz)^M@oGkwgb@7SzH6bebc@&{SX}hn7!_YQ6l*3S?PPj3OEtC|)0L zi40Kt`+86)yae$AGG;iZodzxZg+)4+o0?~eG=btMA7No11w5=GGyj-=Z{xyIEL6ZxVgyoun?vS3ja^1>npD92$p^cS^339tOf~kJCyUtQd|W?q zp?L&-G}YGZz@HJ(o@^joEGnkSo<@-d$UI+7Yn_b@=aD?pI(%`FhvQ-D#^jg6k(asW+`pbEa>qoXKuHUSjkzv{xiAV;JY$$+Qj6`d$bQh(8av_AxIFTE*&pYjO z1|!{vDx}J((e6;;n^7^tU{A8@!geS~!MM}!*lhHh2wt4=v>K0`G>byHLv?y$f1JX*{&J@0^uMTF6z^Ziw&Ntf#<$FZOvSopuX0bgKy;B5tHo z9-g-B1YEBi6_ zsq9eLEXcg+&A@~9E>*Vrz#d&_ALu4V_QB3+3^O>#mucqViqaFSL1hqBa@t6YmjI^V zbol@*H!n2^W(H*=#18K+!T}|mC}32 zhov{efum#x7d=>Qb%GYU4`n$v_TzYmujAkAt6@5tP3XP0VQe@l`4GnTBE|{d^D&EU zSH~91)rUN`5KR>k^rPAPVgVv4d`#@$TPgIvgj66}VETzkqPMpujKT#KWYh)%k@YWV z<23)aKJe&^8*nIT>DhTK5pkCPk1J@dMA&EkyJ99+L^I=?Rubc%Y6CwjomH>H+cBnF z&4Sz2Fby3z`?(!o`A=3-Z8!QQ>Phcx2Adl4hL&}=(=qM+?3u}%w!5%MO(x-X55nsz zlFz^oWd9SfJJjcx?uXCA%~zodz7z{r=MaKbbjrVE^P+fQpvT=z}&s2tBd?T)jfzhmdkV46C ziN<0&XtuDy3+W^XJWB&N@JavhYo?Xhm9v8d%UEml!C84bCYR9lK<@#!)3OMFYu=8Z zTPPEg2u+>FwlH2^Me&<6aj8&^?F8rRhYb(Hz`Y`5VY$wzkH@i)eV?>pN&ShE`aBg$ zo6}gveiy|bbsP=>gQq3SwgTqbw^0{b66H(s=+b_zFVq@KQl$+sdO z+iM&%v@n|)?Ma@bA{c#DrJFOmy;;i4OZ{qN=GklG5Bo@|>vlT%y}XzBY{e<@<#AiI z(FDz~l8UCyvTNcZtUlm*H$-mt}r5ard1jh_g=#9i+opXOLV`;DI zYCeN)u(|faB@ znVk811&B8K*rgqDeqb8b^MlXG{ML6ga<;irMD`x!`y`j}Iy4t*`EAdPZPuwB#{l8< zIE>9Y7#L4+Y#{u~V9D2|9yWy#anWD4+nmQD^`FZE+>Hb-8r(ZghyskmoejC7qOTt+LBUcLs(eat@``39E zj+=tX!oqk!@Hn3NMur$ChdQuGz0BYSo+IyFZzzehCsOD-lwbq@#!>x`AnRXP(tplT z{pE}PUC_n&pKw(FGC?vj{4XaL<3F8TjEw(Oelh;_X8+HT85#enFk<{$&gkErUH@A` z`QJLb{+ao&*8bC;HFny0xHM{=X=wCc>7@QEjnzNe_;0yH(?dTlY!^Q|j#K|t@PFm8 z`o~56w_txr9or!mg1e|m)&Ds7ztxoe^Dh2d@UFuLy@aK|jt)`ce-q67zeHus|B0yV zpWWu)f+J^X7sL$nI%TZ?(_E&%I!xBT8}nb;H|yU5S^wY0uzz&Zf6LX2-Y2qVr!V#? z`cHHJ#|QUciP;$c@gV*y_y2dU@c*dPVd=&GX8(^d^?#ej=tbR~#g&~6olOaNcnIjl zl^F>bS=s*iR`?%?kpA!4*_%2#TberkV+UDb*1wPW{~a>?$IIk@HS1rHAq&HQU=J=? z$U0cQ8d;%{Oy?@W8h(;N-1#C^tNRWX1v^^?3{Ms!)hSdmvXDW&YwHCLsz1klk`8-U zDN|OEusB7-LfSd9KRxM?>js#xy2rB%9E1uTe#9UKiGyqPZSi^L?uvQb3nf`OaSPq! z4X0V9W~cFV`M#Xx10d37Xjw5iM;K#h=8;($a02E{mwEB>?YpG@zCN^w&KoFz_qv=Y zPyU!9(7y;~_R&wXr92DLcfqSWV*Sp^j#Pv|Sz}JT5Tg7*{f%$%jyd#Qm##YJ;szcJ z<(D6CRl%N6g%LOpkvkWOi#=q*S|4$f#uPa++tMwqOgY3R*)dsL@j@Lvxb2YC9M{a6 z5CcLcF=#k%RoILfj?xW9hN?}%^3YQME(e~pV{qwYUxbJU&d{=`4&4umgGsO2&yail z(zrsZ9p+$#J5g8i=}1nRfhrWudNJ|ffd!fK#4x!{)4i9ffc&?v)sJD^X~XLc5EG`` z4u!JV#&7qF8J?VYL=gBR6OdLLSZ6E8jfILP_J*eXVOs%4P4=B@+XzYpdqPqd=Rz^0 z@&aC35p##x+PPxUKth4@blysbFEc7y!HpwzRUPGhm#Z(BFpX24A2j66WK^Wi-bsv# zNT0){;2J6j)L$N)irm{CkN{mM9Lr!cUG->wKW1mUCC~m4Lef&*l|plshjOB#cz~-v z@^7`agaWp8nzoQ(WJ!4T=xa=@D$*mOvYWYnV1MX$nF1N61Jo`EE3P;+IiK`_Si3$^ zN(>_1c5RvsflUx8joP_30a>jd07)kaFre>lryg3!*^=WdzkJVYy9KFO=V2SlRyJo- zg{FQWeya@3rz&o?+BlNjpcd*fPT*Q!^|ALJ~17`nY>SzGeCAtpE<-Ei7(&hg2jT(>iIaugEL!9@PzwbTIN1@ zfEU>Lh6OvXPoW;6lK9LTr(j{YzLia1lEUn=vUQkb=7od`^6MB-eVf|+nRC?T5`SLO zk`!oGQlb_PBv_Mu5A!+SG8Uc`Kpa{}dRrPXwdF(U;JbB}IE>xsxSSA-au`*-hP#fl zq|bH%WK%U=tcZrOWImnOL=hP=YB{~t6~gmb-MsX*5(C0LdyyCO4^^Nguq1{qy+;06>m1bd@p=A}lXSR$)D*X${ z+*;V#@}QwWlX#S`pxic7E)`plVWGd6EA&#oR1l_LUUT14W=gd-AbU|h`ZM!c zQbUoRbB*-gSkS!d3dt*pGHn=pE4}0onU-1kp|t8X8)=_ji?^Rp>CZ4O!Yz3EHx)jP z3{N=-j!i}lxw&%fI-fB9>$vt7GL1^WapuQK;Dwc9jc-jg=(NA<8sT8F_&k@M*+p;; zBOvP$-GN3ye=cmsuKU$i;b@9DO@YF$?6kX$CJ3$2=asKM*P3O-0C+^8Wfs}A(FcNw zTn?g;^*(Af-WLY1qqDpfus`7qsH-Z&+^w6&+Wx%bs=a593QiZ30bi6}Ele5ro|d+f zbs9Z3;D(6R#RuC71tn_xI9=}XZIq_YdvyN7D#;_%)lpJ{7{a4;$p^b%3ZQ%!9bu-jO`QqUOjg`ad?;*yEI zk5S^!sWD?Xui~&@d0EyBFG+Dh&7T!Pnxl-~X{i{$UwCh~136s$Q|(uJ#n^J8(^B%+ zC5KR{Rn5EwPr0Lou3E=i1--(JRP)r>)B?Zr+``-M(|dWqsJr}5w+(szb$0x&eKJkO z%QB?1AwCjd9rT+vLz{_Cs1C?%3fxV>-;Z*Yzgd}spBYs5jlUyC8eyJX?VY0um_Ty; z*s(#}6^R=8z$3`rg6?P7bsj$tH3QiQ0pz)2o6#s?_eg5rvY#=i;HvXz$L?NYn=jG!6ihd1?aDG&g)%P%1lCV zg$eKr))xTlsP#X)-x5f%HAunu}ox1dh%;FPS^eQNGeSWEz+Cw zc#mzT4F8EqWGFJO$!nglY2YG!?68nkV4wgj!5?kkEFm-Jss||&vwtnN_3u*9(B}T5 ziU(C;J(TGDAi!0eP=jdO?ET>d3tD+F)-Pgc4*CTn4*p_EeEh z5z|~)_~{#=o>t8g&4N22$&D(xM6rk~ix|PQ61#HwQC!|4`KJgyjFpy@O}!UTc;@PA zvSndWTKVN`YY+{+zhtPbF+S9$JuI0X3+7}e=AiaGb;)B>*LdI3hb-d=-eD>C##7bu z`U9?Pt|Gs+;pMm&eJvZO!bHh%q(P?rlW>FYWS;zp0Pnr>d9Ql|&Jz(T+hlE4H^VOB zsKUspT86=PMe0PEH8(t#o`rp9-1KH4O)0<-IuU@d%OnKcwwm>{fE^)&aSN+;C)5`D zTho-Zs8mJPuJ1arsn@w$_^`R&}Y6RUR0=pD#Wi%cN@US5Vl5wTB?X(<)$m51+=1fYgs)B>#OY|h{ zW34EdktthY{yg!mXC&6ZLSQ0@QI}*C2Ifm7rI$jB5@G4pxGmAPy5m0qyrC4EGuwv0 zPFf%ql@jzzX&#WQe*gZpXB}lEZ>Y%&CL;2u#*%iOP%Ue~JcF|x4ekZ-jhMMvOD(QW z$cT!$oniU#WUB%!>m?b?Ew8rOiE&lIia6rwz_E z1yPsN)ZX&E@4;a*OS)TJy5@T`?PxfMxC0VXJLTC)Y{5W=IfU6bA+Ym%b4!M*TtO)M zP30S>e=MMJtCs83XndA41WWbt-NtaBheEp4P?X%xTknV~F5z z{+^yg0-TeSDJvY0A%ggvGs@)+_k=VV2aFTvQO>qmLh=s=K2X<3_c$TpJlunanwG$$ zkL7_2#a*x&zKqhL7pqh~U@b~q%bUj6%R5xG1}%`KnKTywh_~y|^OfgAjal2h$|S?+ zWyKh@;M8xBwl(=_?ju6H^0cwAz?T=Ra}C&HX)ai*7o}hw25SLvCX-XCJJ+i^hrYZk zw$T7*axP(|8LBYHCD?Cr!6mIKsl&MKY<_Jn+=o~t5vv2=F5_E+D$dV8l0}WMrk)-G zS%H8>POMOg1a!Lym_Y)?w0#FsF}`7yHCx7BX^nu}9}kl}rwTt}FAZ+I^{fr)OnIm= zIp`3-O$*_uo3`I6l82_*r!-U+L^V9sW~_k29wlA`JSwBv4We(7l*>BnK?^k$@76D^ ziNxM-_j9(0w_-h;Q(-a`+wI*N^01HMV<~;;p{r5=3Q+#ms`aGWB=<5R>-p$4US`Ir z)Cwip>Dpa&d5jSmJ?cJMW-^(7%f7=y-wH}l+~}}ea~dl z3>;Wl0QdQQHOMdPrb^_%N{px>E<-b{PZXM$F*5$xr)7mVbfAEmG@Pb+u}ucQ z)}jKen#d)7Iu7`Gpw3F^QYH=@>&+x+fXTT*{LAP-{MtObQ>DPqOeYGjb695$SWtoz z( z+3tCalNa(~?!HHvj-AR-3a|7?L2ttbFfAJ}+Ajv=v*I};wl0E`#6WpEqbnB=GguSk zlYfX+E~gt2cHjop*GkFtyl*P?su#H<)CWFhU0YgH<4z@yNvH|lEesK{GU}HgNtFpc zps#F>%A?*RyvFLN4};c`7Iia$UNekmQI)yL{U8x3*U4~lo-JqZb-!>rd{0G03wt(P zHwx~WDOUd^b!hEUgwJkEbSqljW4l;K#;g;rS_m`6KzAh?IMDhng%d8UF!flSa=+G= z-9mXBZ!c|r$WYvw5Cfx7`&g-S;Rr%3dpg<$9QKvW$<4L$1b_jAe&8XP#qJxMT=wrS zHHAvu6KtXb`q-k31fM7S(GT(Id5G9M6i}p*!bq83DHcJ=)bPC(UGxMVB<`xc6)dlF z(*AAkur54n+3v=NES0&_iY~tTzYB)1EKrV9=Bav;GrkcT9{kDW{ED0D7rlC)3UOpB zXP8-3xxN-b>}97p6!N0eJzrt&Nd10_pM2tX1d$LOdIi=RGE`6Qr%89C!;78nx0m6| zPvpWd@vtNZxuqWBfyW$inoJ5xS52@1|=5S2+j5tnQ>wcbuPC2QL@_p{{M~& zAG9g1&&Vrdne7dy?W%|a{JbxX4f+z0w=+^I;et#NV1d`eH`%Y@45g6?@xXvE4{SU< z4C^TDf9M0jaK=0x{>zNcR^B)7Il-)+!+6NOVU(OjvX+@ug*>73FnvS#*+KVVTo%c& z>I8lq^Nojl(4nm9=`lT|+uy39HzFjS2g$ya6CPWsbVa%pfLZ-u7CcF?DWb1Hf4wI- zzI1)I4wYDQ^a=v-tXOp>;aMSHB9sy?*`zxlSNe6&5rV~N@qJKmGr4P-xp(}a8HNuX zCyySu&ht3HXbrB>Kber}7BxX(?b1~&KJg>CpZBtm z`9Nw&ja0RVuzKVJ(C7MbtdEc-j{0fIuR=MQ@H>&|3B|3O<{V0qOFXhpAsf|-;BFK= zEYe1R3Gta4%SZ~+QzN|I?wID!+)>5BAWk}i;!E|o*tcI6xXn4BjBe?MI>hhX!xbkl z3n$0$DzRzW<_ENKzuJur62Sa z*{W}_8a}o^f(tQv;qQ0(3K^wP*c12<(xObXdk5@gW5_CP-?BdCg|a`x8sed!zk9TJ zm&IZS73=Bsexj+mH9!TumIq2;v2563}B{T72#_KiRqF}b!CGHp+5@kfoHIwv!={nLDF8wL zssg9e36i3OPIYU1&Im0NW8@jQXyZM~O{l_*^&BF;6=n+B3+gt+hEjxKoxpwl7JJ(; z`g-Q3=>VTQ10^C+xHK=}?fT$R^S&<_3?W*WODDPja`;t9m7w9u!8=Hh zp5JySYv7;`E0cGOQ4jKba1hI#rn$83@U&ZDdW>>~7=&5X9d-~Nmv)`91`>I+cle{s zE1gkQIQ{3H%BI{gxoW!8?8xxOd6SX3b#;2nZXySWQq+!JZ=SF>c`-I_UMo1iAuGl; z$;Um~Gtgw|=@tu5;Qi*NTm@j_#(v3+MQRft^%6G)1bt4BmMEIa5g2*WOpvcQf??z~ zW8sOQj`8xj&_ghCNv~A5W{QJJJSUkEzuX?ub9P5{c?_5+VqDj7t$(vLX=mjOcc6{m zkUjZj-DmFAA6d*g*a2giK7<(x(_9m?pr_6PZbbPF40-tQb@BcY^2WD8>gBhEXbsU= zJqo~P;g$=pOHy4Gtiv8XE$J(_9spj)?9T61wh>T_c7iZEdd+g@U*irRy(`Ja1M@*O z7@y*PFMoeag22o-vyjP;Q^2hViQ6xsRy9Uv*MRkRk#W z&MVKFuvI2imeqB7;>S2!Zra^p_2S|%X>GzFZFVFN+j2{Z;je#l^GoA}XJ$x#24-WJ zC&g1K>G#^jRd*97rLgda5Tnn54Yg+2O<$R;t-qW!y^aajfv`mNJh5=hW1+I%Vmmv` zlvXN;2-y=4_9I~Yb?u{ zx%qiFxquRxM4Q&2=)tIj<+8?A-MuohLPTWjEA`3^>|pQR6%_RJ@#SB~%8qiNf?!<# z@)iZTpIX5qpEcd7=+-h2=P#k|aVLS_n82w3ScGT^3vw~MU5brj~Q+!(fqrg2xZ-4DpoiB(iU#bieuTTV2Aj#tc z^&Rr=8NU~sT$N%}Ynm-#1->IMvJ={zt`LK$>$TBe`04d#YFxrOUOXS-!J}#Iod+8z zmAh*C4D0di_aJ2#3hG^z#;mbl)tu^YFykq$Mh0Y>axS9u1yz-Bs>~apvzoYntQ!aC zgL{n&`2*a%Cx#Pt@wF@PyhQ@1{P#{b2nT>G-w&ERl9qy7zdCP z-bi~RR746*`_cDd?b+g78SmSBJf%b)_>~>@m@gs7``5xCiei8u$6Vu7VXYVWobG7C z+x-Gv1-xa4zQ2*pJfEoYuTyZwWv+%%?HmPeWxr)r4pup7UV73wtydQYT+2VZ%**@B z5u!?6Y$z8rPQ_3hmSilkN!Yo5#dz9K3uLT43;K+SUc-YhoQ0pXE zoa#vfbMF!{Uq}*`bUHF+E$MIlGZhGw;J{qRnvnpNkr}C!!_r^NTb+J^@{!S60Zz-UhnT3HxjmbEL$U%$Ikl&O8l4uoh< zQuresUC1@N>p<-N_MP*&S4UOhc#2=qnlI^2F`1D=R>$+Isj3%@P&j%qIuYYpW5-dpTe0<;kis%2+EuD>&Qa}`I1 z7ByNoIhM(bl3g3P4c_E0Fm9;kb)9dX&L>kv zAu|yv7?S2+^l(cRQz)i*92_0)LvCFtuD)w1X=Dt6*Y1Lug&EzTl`m< zma$v)+%UHD-=@2BY&yBX`y7hy^HqvMyZwS!DYp7g*}L%EDg~o|!iq!v)MG>xgy<(Z zRkMEuA{^#W6TOW@On)a~dJ%b3ORw{!lLAX}y=@)SykGaqyR^80^Sd#7Mgx(31W*tU6(ZQHhO+qP}nwtbIn+qOO5U)5a|HQh1O zJ=3rGu=mcL5t$L0E7!WIZ62z=q>os*rac+_xnV{I9Z3Vmor1dRDqfV8~T})8lkthg6pHqe`I4et!?CBYk0inh_uK-Yhu3Kth+rmTq78cwW4~ zuTa5+M4nHxdi_AAN>ncD-k5tO%CPWFx?nMfp!w!=T_-=Kro$p%=^u)-FgnP`sko_Muq*2BRYc!)6ym?uFb~$&Gc%EWFR^U+18PJJJ>@zoSjqwqON1n;Ueqc=@%kOCNX_x_g*eD_ z(VWuhVbXf!VHyCb&G)I=B z$H$l8(v3BS^EEh77X+E;nY~IirciQ2u|*M`i~9{S4=syii?B&Idkn7AFP(WJ1*7?M zPbmOtxii0s0lY;7U>lBjoY-pR~rlE#G};MfI^RM$ucZVhBm-=T3r;} z`4NH0Bm88v6bEkQ0P zR0Tla_qWUdR}7sI-bI-``o4~|#%Z*ca)!<3{dd8VJ)*-|x{Z3J3e~gTp}+a1MfKYy z>zvDp1)D*rH=Tf$mx_RNYH6$*0`cc0ABrdwj$t3KF!6Q&#?5|#Z*p2FNh~|_sQU8d z#=h&Ii|b50kv@9YDo&o`*DqW;uYNAtzTAXFkO*Y&!zOTryOu* zm`wR>ZW(IIt)>DDs-Nwd1`*bM_J??N|(CP^A*y*NCdTV8n-J?1wj4>&KRqT^Ub4xS&d$bT~~yy&)_l} z;53=l(|z@)%uz|5W&*|-JC9FI?8V2{Q+Sjn5aqrbIP9oBz5-6kZ)IO&zyA}c?BCdl z|6o7<11dYwcGSn~7hl#T2DSVbc=GRdu6=b`>OtQtAr^ClRh5MkIBg2#`L79r2T9`c z<(w>S$K`86_hQpVz;%K7Sp%NP7aywkQEQlb_xwVwbd|HX<64!8F@JT>Ds9->6zoI= zAw`R|jhJqiQ|rU-(KUttN|iDEf2GR)oe6`b`%gO;%=nMb1LN@zwtj1^pssG2LTS7pBLlT(`v1; zdf;#K(0BUlANdv;IE(dkit-itOU;4};|kt?BU`&iEQjK(!W&KRb6OrVPjd_iUf;lh;bi;Pr;^cql5 zUUoua%=B_&a<9H42SISAj0LaPV>}BORS%G43WQM_En@D2120>5R}G0{l4`aXXr0EI z<Y!{ceZ`rK_+c-S+tE2g|VcKV1zQ`$j)#xGPs`r z3a}NYXd)EmEy&3{=fZowB$wf?XP5Nr;LK?5q#1FvAQ@Q&gXff9ic=mV8|^7Ns~nata3Gk z{uyfdI^ijjp61qsfq|~daHt!o{+g1KlIJ7oz(#Gg6=lHD#K8FIwq5WM2nzK=u1|Cw zTU);e1V&rH1=Fj{edI|;ITA!}$$;)R?G9OzjGK?Een|4rO+H3d*OY-5AW9gys)|iD zNnU8hTP1YHzopuRn?GzXU13T_5S=>(EXhbwfHr+`O+V~JQ=T820&olrn|n~zy$Sx4 zRg@zq7zMeDxfu6utBcex7s>SkQt67)7YFnQQokKFdvgGob(R?n;4SfF!Q<)o6=+Vf zSbVBM6qMIBK$#{!46d%ZJ|wFo_m7E1c)mbxs;5ZW&>+9nmWt;$Bt#dB`?o&8k`QVb z0`rh`?>Q;Z+>;N+Ht8s6!_%L2l_=b5uhbOTzBH@_#X1sXJ`eaU5HE@c7?s$coZ#f; zMXLTr>#vK)%1YY9#bsPWgkFB-j7sqG+tSqa$;3dgIjldqDC4wmmC5lno5l_;%LkF( zHUu?l(p$#DwyVFQ00WrjhA-#3!Im;^mV1(^RibKlik6||F1_jExA)B*tf^js5n;ne zHnmT*W^?36-b!0HVZ|O65Edi_jbnot=4yGif;IxI6;4W5qN9F z$z>z&cTwO-_6z2^5`n`T{{ncP9D}HUw4E=-GRqoy*{%6kobDCjTnD)P4I_mTh&7I; z_<_ywd)$tY&7LD++;K#cf_y~abbaALovHHJOx|aNrTjR0YMSaQ$uDGoY&Lrb2J`+p z{}YhZu*8X40c@tk2I6j+Pu@snjPV`c5Yi9M@{QPCFiFzB7klbPd_Cz!vV$xe&9>zsVhs!~ z*VNw%pFHygvy_(+16MJKrc5_wbzuMTB_D+#S1XaQp9=!viu$*G+-g0O%Stxr7Z4K{ zI(2+gd`%FDSM>6Jx*Gh$@5p4JtxmDm+2pE~aI|z@xEx@K^0W1rgC5^#6 zyA{9rF|h^sBAS!RE(S&Z6`d#ob4tQPII8i273(0)&#F;YDNkj$Z3;<%_F#NRZ8q1(J1)m>PEPe8fPpvm%S z*E#*v`HG6dyT|ThCW&3E_s;*F{@mGI5dGCQ5c_JuXaQUL?6!Z){uuAh#)LG$&v&HG z(o4H6B6+@5(Mo{gQybX{DJAO%Ttdh>TcCO%iSh#1>9I7wsnuu{!njO$W@WN->l z;P{(9OViZqi5etyXFCeUoV?cn8y1GqDKXFYHSrXmx5NQWVipElk-6qn2+|KFedeo* z)bB@nYMx^+&OI~v=_>`3@}!>kEaeiPRUMDwKyRk9@B60g* zUw|2xBhA&?VlMdRH!Er)T%v$)#y1y#unu-<+oEc2rQ15?(H$M3WffpaVOa}MDIx8_ zn4V*8asG>F=5BYL|@OKSQyNRAS zCO>68_waI*dt#CGkR3sdX}bPW_bUQ`P7vD~egj97f9RoTQ@JV%i==lX9-@l&!NTst3*_e|nSsUSq4B z6{FZUBSq0|ilx}?1J?{@8A0dbiSul4x}MffDhDmX=-sslv7o_In`GuDe}*G+Aw;VMgm7K)yAKH(o`d_r&;6~PUEfqEDab~6#}x2|ghN?(Rt#`M4R zI1pt>1k^Y`?|ropfzFxMM_5u$~x5PE~F=NpH` zK(PdvRl0+1CCcRmSAUUWRmDf;M1#K590lwhiUUYZd)_18;tboP5=089AKDkp z+^Z+Y`#Y3Ke6TL>mUogb4Iw`rHg-;a%-`dHOr>+?C5uD!EX7Cqg|ZJOsh=BheNeRq zAbx&OV_p#Y=};p{K2#vAi1J2B9{}XSSI+x9kzt}oywI}-=cMeDUe_bw$}0K**^Juc z5O@%jPa5a`EzUB0Mks2WweeEc$1yJ;YY|HcHSg$VMzXZb;Vac_ZnB%fR0MI(HEQ2$ zMx8U}7{#%8%GPLNv+FIA+^zWBpK=HEM#UmI?O5|bcNw9LXm+FN(@x$)Mz5yEo_~7$ zx@#263&lxrqo3h!WlMD?Lpza#v?A(t9nq|JpZZhid=(R2kqdjzujF;`-2O5gpxSXB z5KE4R&aodp7UshQ8Kay``Zp$e$5X-UZ5_1GdFJ55GYoi6_$J_$YJSMW1XsDQQEzdH z^*qK+`IO~oze*1kaRJyjbCv35fixDUL%w()5hMVVB2zQJrtcNFC4QIRLY~&T5<$8M z#nu?_h(P(+Pz~V*BTV=!DI_c83Y*AzcI+;0=@r&&Zzvd%g%j`ov5;r|fB9C9BIIK!`~ zXgGT{vuma7_9B9bU($wipU>|PeogRxv{kHJnWOPR1M^(9XMmZd*pP7YC+xLfJ}4!@ zCn5wt<$Rg4%lTja>-m!>Iq+S3K8Gg$^5|veU_M2*iyE-(m}AH>|7cw0g~Z(@^E|GI zB0@qMjy`t)1x@(SI5T9j$OVi`ewH|a->;@#{EMHx1Z)2vA}ZbqmeszZVPQ83c{rMU zDxmQ&$Z$ty?X@ALK%zG0JYSM(HVG?}KV-xir3#7c8)83(=$cHmUWh9po07bZ#W&(S z$H?s4sxWg}ImKMA@sLWX_65o%b79_VqO82jR-d`55GO{I7*eVW%5RBS^T<8&xk@&& z%=hkKv?c(~yPxeWB9^1* zZeiq3GKQg$TjqdsxOJYo9dXmKvMS=@Pnq>XRTq0W7o@Co4y$gaTo(%rNYPSp9{sVP zUSc1*OA;()dsflvl1~GVj;U+Mvd?ncB~|=@iqSjAFfVfRx@fC?3N=AVLx5T5Y#QhV zFqgIkTYsqYSc4gC+R{q4|JPeQL#1;q2Z|9hUuJ&x^N%f0Yj1qhp{&<|PY|}YMu5OR zK*SVjQD1;1pfY&G1y1+Nz~$CZ-4CB96h9os*1W~?9b8Z(k;3|2Z z8sF^tzOjX}MYQj@ri!*Po#M3dvQh?SJH91;rzq_GPG zLy6Pgt?40Wt<|x_bDz;j)M6j>tIM4kV8VSHuA*vwWmAU__0b@I8B3(_6-GzgQu-lM z_l)u%<@S|m-uG}_lONdBPNllVHPal&W2(yIQ2s^ngs8#SL%{-SL3X1eUd^))r=^gn#~C> zGK$3jA-1JsVtUBxxZ?9RIjfd-#0z`erTrnJ#a zakLCi_T9sjungtY%Wy7DxOF*!}-Pu*7nQ){Ubt+}%VB01LR zbj0s1GRZp0AQ3Z=J{y86DSM12Js-8 z4MNuKsR0j4S60powC#$;-SVNBG`9yv+FcO}?JVrA@@YmfaDSYqYepLv{Ljho(Ae$e zV|~@g-%q_5riTNu1Ey2YRq~-$o0hiWdk0LXEc`5w$`5-xGX!W}S&0tS3~>$PAzswF z9k)*>b(UXTgMbL{+sA6w|CmS7gO6|9RcCJ&Q9`^G*pG<n<3%PV9a00j z?h~&VxXpXN@l9&6Jt-E$HoKVXWb>emAXhpFyZFm{eE6nRMFSHP zd<>^QD`7Qd&1{^HqoP{E=sQ^d%2do@Pp9ed}?%NnWkG>nAGp z;=}uQAUX67&)pTwy}hPXxXY_3h(>ms><-heRi&(lWx-qBT`>oc*W%fX0W!csd>u5; zH3nStip{_6VfMK|_hRME=;VDbh_Bnx%qc$dynGrwRycUv6jT_4dhzu5A!ym7$zzRL zmZ?#uCk*3Gocr8G&z-nW)SHUySdl0V)f2E>tr+}Ui9CGEiFR5Z@PDESxNKHP7 zZMpxmSJWEOv*Q8g=qIGU(vfVO6r*cNl~S(85^k>pMfQiU_QpKLLh?2 zvJx8delJ)?gdm8fM_G9LKOf7CJ7M~fUkPH`B?~hL%cP7#W<;@MFWUEr-gmq@m8{@z z!}+(u8tJ3O2Vc6COk=54fV<;QQ=~pxfBB%q)K$|`TE+0oAtH3x7*z%m4g%k%O+*UG zyLS#7*0Or-=$AlaMWYZXw-4@4ktfAdZojTRd!0+zG_;Q(!W{_5n$@-#`dcS$R z21MOh;-?<1Gi{kJ5vdoz+2^*K&J}HIr}rLL4>@$?rYO1U_+ZmSbad6G6oFEpFY%n; zE_zF4!(EjW)_0BCJlmnu33scu_P+95tltatWi^y970zh7d?vtQm_fvAtw7Ku{5gir zY|ig~WGF?VQ2>7k;+)#)VT|a)4IF5%af$jIY$Wh!N3gG4dD$iKa0(9;W>DNNP`0Eu zVGFE0U0|@8#S!ht#sNe`Ab?Je{MZHi=kqwS<8}%hVNAtkeupr*lplCJ74ORBxYS+4 zZF%i^-v|T(Qt#~t3V71p>7GXlOCyJAaO~L*UC2W1!y?d`8NrKmgXmZ+9sbVRZpxF~ z%uPZU1urh_nvJHX1P4L}SqG^xm+r@?H~q>Dd5~+9Hn_U_A}M_pRrQ+K4xoe|UOStR z6UnbNj@7_3S3B2DT=U}aL(w|&1*(ybx{|QG@`vWd-c>{}v~tyfy_1T!HcXwF?;8ol z6{>Eh7w+ZdXKxPipv3Bp=+6)JbFf2sy7WT&h>5>3exey>sU-W`m;>?^PSmhOaA_Glx>s~H*seSh*&1Qj7gCIL&#uiA17JOXH~YVKeVeJmIb7;0^mad&t(1HT zxg(xERHlT5ER+?VV(FAsjoXmir=7ftTqLybf^0W>hWU4@<)ul$r7+ zb~?TUpV85e9Rx;2SAM8_MQ)BE-|u&cr3i@&$*RLO4=Tg1A5u+WVX4>c^mj*V3g@L} z)HHwhok_A@idSSX4#>~ThvVt8p|kN{>6Qx?YWzTvzm-r_?fJgQ1q&AkVVea}t9^hS zh=+z5MtC?bWpVK|fX5l(mpDC~V}Vf$YzYI~W;}>5Yi}b(F?e23wCDo$uV@Qm$ta1~ z=RX?k3FN>&y#x}rOE=x6vJefcvO{7h4qiafukoWRV?~-lH$7XL!+nD4aw2&DmRJ+0M*M7_&Bom4CRCw z9;8(;2{U?nUhF%Mp>OmSn+)NAqY`bd)Em_$ac;oVLtX(@ps3HYVoBp<$_6-Qyih}C zLhXp5SKquh4`vTxuk7a+@QAVQjor~Jaku`4Af=IJ@|UizabumK|5eow5@B~fdMsuv zyDtief3`B6c}|8zz^u8b*yX+CsJmV?SpizWgyv%p@TE315ouqUMogA6I=v7CicEB` zD>HH`{BW6`mbfeS+#f>s-z2OSea&qFsN7SrSPnj+sPNvh8E?Epr%;^PLbY0-#fF8S zIDE?|Xjzt36FC8mAXX@3RT-&HbVxk2WG#oX7Y=|Kls2Up>3VKS+LPAY)~_Fj);^sn zDg%J)(MT=sGp`kde)S{8TXf>&kc1{{rnoX%1$LqO6L=VQGf10-c^fu<-An?Y1~K8h zba0{ich1V>TOZJUE@JxHBP{ktmWK-f3mzIV{>u;*bG<<3;;vVC3%qJc>mfk$E85y} zLc%yDVoD)ekC+RgfaLBgnf&oD8S!cpJ$25_bCf~PpJv?kb}IH$yS6{~UUuo4{02#- z9nNNiJ)@64rxSapE0@gL49c5TKBr}lJaM+}LS09E4}{8M{(a=Mp0dU_h0C7+;kNDF zQQYw(uY^l>{e+B5<~CjM!;h%=1+bb1@G}C37?4mUIhtzMF3~^^T$_t%4!apXi*%Q} z{>;1^qc)dVV?#u3HMsg%%qJO=G9g<>?H^qoiA7P$X+)0oqjJrj?2`It$`0hG7P}Yv zB*w(La~B_14Kd5qrHei;60OMy*TyA)&TU}<47r^o6m$V04d^+6qqnf!mgiv-PXkMn znZxLtNwbUaI;C-()TLK0v}8OD_9hkbq)(1O{UcQ}zh0g628?gjcka zK|L!rBJ+biz04m=VaHOvWr5sTH+oNxko0W~z!w1C19FF#1z1`1XIiurn#5pwEm(nm zxG#wc>GP_mV2Jw{Jyf}o7h(t4#mq1IdFyL_3hw8aAG$ervZOY;pEJ_j2VqS1Nwpr& z_&<7VVsMCt9l%j-V#ZY#fd!a>H59a)T zBiKBE&^(2?rb~_Ui18CL2a-S%3<0uK&Og|eMQYoPd3tBg=16XC!Y;l7rnO+|+n%A8 z`7TE3TPLVXj8VoOZXrrTn=}mFH3kIP1e%w?GBe>(5yB$b)RG%zdtyuJgP{xR0a=!j$?*~VG zOH={ATv1^EWR^y*)6)*lY&a_fD&uI2<|Re)9jcOo+>43o_mvh_{+Bi3wF7r)Cf(mjn@9R@Zp9VhHCDpSL zgc8@}_t-Xfx)Jec2z^0Z1H%Hh`8nYM`$Ut1t zlP%TY5PxtWH*&UI%{sv0zQ7t26A65BX@3!5v3LxP)d&oe(Sj|d*-hg0^fbB{Td z10%ews>zCVF`9FvvpxaNf_+4vKzpV~)%SR=$ZTjhmcE=DzSYxLZw{uBlOB!UeP`nl zuvwq?vz+$fwK-Ot-D%-ziRV5@PtpO?$XWKE+wD3?&8A*}puMmA{#Q<>ByJDL!=7Ko za}}%LX7W~ks(|Ohd(2XuQc~b6WX7d)fi?0O_zQW$GT`RWpCHqjkyz?&vSi6i@oF$H zE#XrA0+0EP!#9~BgrA%dH3Pwj*vCsggpLLq#5Lq#3yg(Mop$|&nC~WY(IIq|pJ~LT z?q(-L<(Xbr$_YF3zungDL0ojd39xyT#(j&Y{-s}9Ef|}>jz=m$tEK>6qDt2QC(jk{ zj}XThV!7UVy(P|7#^zeSk*X*8`XzrZ)1&#e;MAc=3=|q3UjHYY%)cQN|05@pIA9aZ zDURz)SoZLrm`qBtP%DS;A0xJjaOSQ+7*LEKI)qp^DkQGUt|7nV>ACY z)_pe+5Q)7JO8!~|1t6ZF<$2%pUr=Z z-qDHvYdQbEG5-fdH*EiKJpU=e`4`J$Vr%@b^)oR3>+1ctGx_hLn}3po?EecO&!sa; z^nQ!&Ar6mt3s2`;7$HdBjr@m8S64MnPmhPF)HBGit*v>{o{(aw*TafuCPY}%?S%4X zp^Anx&lNZ%)kTALx2k`IDWOS-&SNi@Se4?N0DBl;r!#j7&Fl}3Ow;}${pO$O9O;wq z>DjDOLndqUb$~^$*H_PiYK45 z6tJsPrOvFvA-yunn-H8(Sstm$8RiDkJX>|pIw zK}zpzCQ2WT@b>|$e#Ec+jTj?qvBi69LGiky8P1Xez~*OZb7h}_O%u%m8O8H>&rG*S zR;+G!h(-~1*Zf&%)asSG_>8&CV>5EIxoH7M0#WoiQe2y9!k}T|qIR&j&4Q6A@j%xs z@mDSPgw2nB7>Se0H~wbd1r>|ZG#1^gJ(!Sn2lqryI41GVv&4qP?D-fnRM=+YUc$^X z6VV?X+)l`1UlaDO!`iur5%J{{^V_Z_^j;!+@+W4sKGQcw#oh#1#Bp&u{o{)Q=$%Qs zXNCYg&=UO44M@ncGg2BtV_};BbFJRjLa&b%kR+8x0er{%nZ8p_Y3gPaeu>bq^S4>E z#BgX=ELHR97_Xq)vvW$wO{nzB?}$(30V$z1QI--2FBbZ$N96NT1{-Pp&T!;*N^!6l zL4iUPNGo}W$v;LdT+}p^Siqt%;pAM?ItU-oNV~Qx9Z;vQzR*xQK{-3Ky{H+i<6LiIHPkqZY~y<&!QDhKVWKkjm+#sj3ek0Z@LnVX=M)wsB$!1*hI z$ZY+Da$IfSDP$Ak=mn8k&clDQTnP>IA{iLNRg^j;-=DXE%$H^s!s}aYL|m85?*gnB zj=#snzQ8Q0mevG6i&udxr>JE~>GJse(Fn#QS?qjLvq^)&3ia4p^8> z6BbgJj{NR>i=kFjZq*VXb$M3|aGa{i5z?Y@CnavRpvf2QCIlp&!-X8dmqy=p>ar$bGXuIEyg(jzt>{lsz*N z+Mt~86c>5Y8vh8)i*BQCc;|DX*zY}thFPqFB+$TcRgOC7rahjZq>{+zEoEavr|j=a z=IND=yB_IjdWEH^f1d_*E!#kjb%_MwlQQZ%IcGvbM9+Q3ZSd0W4kizLuzB42sSWFP!@O|IbYk{&NgXgDuRqX`in^{V#1NsyaQ zPccyXK&8cBQ9p%LH=frcVX-bOsQNVsiF`pq0+$Eo4=hwC<%K-Uw~okwyS6ev!g{>o zdOe;S*<3cXy#jIjsYy+ktpjRqS#jvAdAdVc!EG(o_-p^C45erkDA&b5g+@_YbJf}!Nra8j}@|2~h}-!hIno@K#j!C^eP zly$-*Bm<(V^*g79)+zt*ZM0(x#>Y?St#Bq-&T!=qTw_#ahmyTH>WMs-)Piz_V6CPD zG#D*m1YA8du8lO_E2V1Q`sr}+nU)~9Is;X!S8=wzY#OPv@es)q9-mvII8n9D<_7v} z-=$Hrm!#Hq{B~3jbi1^xKlxW7RkC5QQ6iHTH5cW(- zm@UUBpZ2P{!ap+?(|tJ+PRUU{@`8#DzW8s(82&yviG68`UD9$~tVvGOn52%T`ek4T z(X^BdmP2xPKMlKRj|YXT;u*v>osSHJrfv*gi8?;TghPqVa3j|AID5~9Tr*7P(@`u) zy9-$?V@AbnWonbTcv+FAIK~3i^L%%gM-f|V{F<$-%3$sW3b7JUZ_@^Ig(vnbCm-Ak z#G-NLa=j?#58=|2wp|k)(Y6*}pop;k;M9Z8Y||O$tFFMD?A)j>#6|}Nw?L{D2GvD@ z0Y9i&>2v2Y@4CsN)sEkbr#b`h13#s{xM()UC=1~wI(jci0^L-w`bf)Q>Pr>A}{5nlhl2K)$;6 zr^(Y}{HMxPBCj~|LI+tA%JJS~1{B#U*5g`X{GI#~ioepr_rTxHeB2@aU7jcS+Estqf|EPXTDwPNki9vn`(H}$_NeSkCBb)sL`hs&Fj+h%cHez1_ zn4rh|n2S^rz+}ZOto|uP*zs0PP6Q%*cP%roZ{!AMzFMxtFaT|Z5nitN@R@5MU^0p7 zHYPaf8WR3#c`xk*wC)dH-9s9yWGI5BCN$qrSLS0ELenHKLw=UZK`JAs5h&U*Au3ik zpTIh8Dx6KKXCBXN;8AdAvrSBIqFk|RDgo9!UoXkM{kdCIiSf!}cl>I}v}dk^{pc3RM1 z`gLrT?t3o_D_n+X2T3Lf4j|WvM=lLKf5^{0OXHA+FzAfUh5^r? zl z04`0XPeP^piZj+Hn$`djc~)*|EZH5WsrDnGy#yF>$LkTT`p}Vp2>{99@L5b-z|F^i zzDWbWKf}R3+d(~(JIw0noJ<77aVe15ZH+r0Iq`s+@tfV{kJT5WaxsPPlK3#I6Rig? zDI#fZ?W+WUxGxTHNJvTt-t_BXiQaWf6~1ZRh-lHV8W6ZcZh}f#rc!ivtX}}EIupxZ zaz2uC*-)3TnswhW7Ih>Y@^E%KH)kcDJ!??FKGh)3NvDWOvA-SbD2S~K(RPsPu(AqV z3FmtP;qSR?{Tacz?E-Mg5Eo}N&wa`o#F<~%mc#hY7;c=PS3)Y%JMRfaC%BIFEsswD zrea$9KY72J`u^*T)JZ-7y`LbZ!>Bh-CkJVfzySLDBGiEn6t%7b7B`w{B8knLK{DB8 z17YP1;^Q39!;XF)NaoRn1X1|`>xHLtSkoPMLGxq1cA?epM`_9nsc!&zyN1!zP27v5>~y}qlgF90lq3bCA7ASHYYg?7`1W~c(&RFR_!vY5_827#F-aX zu_-9-SO>5vSbM%8Hk@sHaySd|O+X9m?J5fDN@J59{LOBm=OIfjT3)yaM`58G01<*M z^R+Nsi^~xeS!+c8NO(q``n)paL;{|xF-v?L`C=Twn=^~l3#(Dd9zqSRLIDI z&}2!6H#_X;KfrcrCc&Za_iernY?LF`i>M30h4~q(+h)`X#Yewc(B5z(4yEZ=*;Np+ zQAv-x-dDYWxHV2cvjV-R0*_L&Nr7g4`9DP`K_BK=8MWgN*WtbF*yO0}YC#r{Ppla1k-%q4M$wD-BCT~4GW9k=kNxr3E7p_Unqp46Nd*Q&jeG%)>= zCT9jg4;BvS(%sIPm;dpM-#| z$@JXo35^XgiA|p(Qr#ky{~lVkb(isU?YKVX_8fJZ{%3wDNknXu@#Qv8lOYE@H< zpodntL|IWhJ@TvxzVum3`FgEKNEu2wgm&h13SL9ea;Yz)mjE?ug={H53xEzWD9w-8 zPE~;Qugx-ieno)4$*Ytq%eL zGBxUaod=)%2B{N1J3NdPF~wqVb5)UMZ+Lf}smTHtJ)kaSlZ*dQ9umD_eY$C5)y6Igi^MQ;$~OG&$8$Tvlnc6J)0z*%li-A{}z zhI9-I(PF(1t)i8AXAM|X1kx=b9_V8we!dQS_`%Siva=E3*vmY|=IB z8L+*0#%0REOBt~$8^$~WsXdAg7_W0TU24C9V#gz|?59e%4Chw0oPbEKx)Yq;4KK4b zHNglbFWD7z5H`Lg-x?wh#1@BmrMoJ*v>h%Ix1`~_F?4K<*b304mfZ={L~=Pend_#B zzOK!-qPap)f>zI(HStb&yWkEbiOgq`QVR9-MA58T^qALNpm{Ku^JJsKb40fip;>wP z?E3w9H|5h2W{IvWu@upg@8f{C2e4o7_V&rQv}>&;xUPsA%>e@uqJ8xSCIGHx^Vbia ztVrsTFl_H9N84>dq6E2(j13eGHM|bPwr0GIsvES*yX?|rF7_XDzsT@2^Hd)6i<>>GX2SXZg=rTk*hQ`mm#U%20W=4lRM(}T8-WfU^4-HZ^ zn%QB=KA+wQA%N;?0sh(kToI`Dc-<>)hYu$f1Th&Ud8U&0WR}YiP?8U-(YG+1J`sx6 zfmF!REt=h)_npHtC1%6kVF9RwrGijwQ2en9FTydCtMS8F++@qD$hCr^48F3Dqi~>3 z;aE~RId}CVLKIrM)M0?O-Lo+#5+{CnarCYC!!sRr%*jGkqXE_`tGw=?YJB-UT;Oh= z8>q#LRlR}gU;ci<_*jcHQ0Tnf?(k?~tA^q3Gq?Wk0`g4b_vYl9Lg_}cqv|&C$oDtw zLrjvmcq;oXAb6B)P^X^MAIFHdA}x;f769ax4>%<`f@!PMg6?oJ8qdJ^j#+FB6nEg0bK!2-jkE{n}$qud~ z*{^a1yxS4&+|R|iL^DT;#63n!54-8mai}+bYQbGVaA5;72iSGL}v%E@+KR{$zsx2 zMYSW}((KkvhIogps}_79kVDEj`F|8B>)!p|%qO#P%k`$3w6P!{@>;2cFqz)V{6s&z zGEq)d)@J3WfewL-?T8$k0G^!UiWhd$;ciEZAvBb!*>?hzf4hZ0;i)!-o`&6ZtNb;= zd|6kqNZ}SJG3ox)@P*io@I&;&<~&Zl-~z2;JIZ@E-0L_H#xapzMwq9cGkORDeWJ+{povy3)LpMEW>}<a8(9(l>yJ-hG8gp~E4cUcSRF-KV4j zPkdww5(!nQn)W}QQLH6oq;oeKVe*A)dS_numR+w%k6}#B3*}>`+(RV4yWW~l{$*WC zL0$rSRNO7nVC=ZMYw*iBjb|eGnEq~#W_c{GQv%ykr<`hHY6)mHT#kD%SRuNqofN(E z;TzIqY3k+F9uY1bb25lEkBr@^k~IySRW5)*UZ+tOj`(e?69v=)USO}o-sUg8ISv6n zC^O|;g`v$>eJu{8ogMRf?YKy-rz+jDd`V|HN^OZi7iY_Au1V@IHtSE>_T0_BwqY+a zeG>671od9aW$h94e8fQArQ({LVt-sQsHGYh3>7J#Y1BhLyXy!RohLV?MJ&DpJObpBaoW zsRKQVAr@M-x%wwW<-{6kYz(zlHimx(6-8fdmM?By&gD8P2QbwfHeI%2bx zDa^Q{P%2iXd(VzAn~hvxCPP0%$M## z91FY+aJdJo)_S7u&8*NTHKL@#uj4_cMCRIkO0licAN&d~ETnPed`mu5QCM*ZzI23VJ2u6|{RUET@UnH4 zru%&IT}9}}{K4?F232qt%Hv zBDrje25W!PZ!lZd^x7NbEO;Wua5$*hceE-_hJhz(m9}EQNLLD~9}l@H3q!$vrM_#D z-Ld=sm{y#8VC;`BN8wHH1vqY>Jl4WuAD1EP3u!3+8E36)|9UjXCghDM)C4iZ;jMcV z7$kAvoff=k;~V^3cR`eiYHmoo7KezD>mvIaO>o^6zAawT|EiFzAS~)Jgi6?M)P9=zh>6<^X$y}<>QC3 zr-QlY1+)A{P<*mnV*ruI8+FUIKnmctnU-(F25wOtVcm`!tn*_?W_&*)e8e_*?bYEA zsE9W%G_aHOs!HS7O1-I%XT5E*_ipQlhU~&$CyE!fZhVXKEm%(o;KcpgRQXjPOD8OO z20u1_*8$I%cVU%B(m?1%#2W(+5b(=aY^Hv|wtChPdgJR#BE+FP$fkYR+7rX8bHSb1my=|t;Dv8i|zs#bZ9QPV4jyqhqh$5bAe6Vv4n-WP<;EKI*xYk5-#4A zOl-GGNAG1sK+K^RZuXy@3$gN7k$*QJn3KG_#g^io2v=WiIb0nqt-by25wnVy;AoWc zc{L+3wU;WH>5cIF%bC%8$8sCYZ_)9i&mXU&6}XzdySN^aHBSRexpy&aMC2q;FfuKH7%h znWr=hom#fBRfFvhdl>BR2_JK8zcYKsAs^9~ zlAK_p1Y(`}1u|njfz$H=K~t<;e+Gt-j$w$rejXT>^Hii+pRG{muU}Dc&i1pGaZiyK z>HHV~Mq13a;Ee04qn(I|;&E&X5pCzHFFxKDZ0|F4F-AJuLCl+4-tBGBACPv7eeK-y zLRMaY^#g#Nj!@}`2TUUvppPCtef9@|AFt#DHA4*6VGMO`B}YD595_jyE#(QxigEMKyc^%zw(L2t5?AN0 ziac8%Z3!vogUHP^<8^PMh_}JasR94!tXQhBvfP#fPnw`#JES}>10gK|eXf4|i9a_kQ9ETmD7Mfqmy9n%_Dl^$7r%x3WG$+6xj0zD*{DO+AsilBKz(WDwRE$vwo=(@k(uFRrQ&Pyi3OGGv`9jloN8$pntE8 zF>89spq<7CtH(to97u=DTh1+*_nfFch>>#g%-dgv0b58etx%|IW_-lN7`AtIhVk+#+aER3r+F8 z1PsS<>(JXr3#?_!&X~ zp@k8tG1Dp~b>L)f*yqx1R7STo40#|C-|@#Z%7Rq0IvV|)jye+1rN*hFzJ+6(y;4_3 zt@EN4i7LMhbx4E$Lyo~%k#d^3C)$Hk#y#)MiRNGkW1g=vl!9N7hjEfhL}f}OvO`bL zY1+)gW<6pm+Y9#DhPl{WdFN3#(bk+PI4*rsnd!3^?-nZe+f5$kR~3m&vUY|?GX5A9 zFfyiSYo6+B_0jy~P_7%~HZEG4+o_^gWI<(~X|k4;&k2l*Zh}j2E_|~gq@qQ<(v^*n zO<+yFh-mNM#p-Ypr}AV&hG{spBQGwLs$QuG>_iLsFr3S|Ot!fbER+|dadKM^898bz z%RC6;2+ZlcZ75kxp@e+wd?UM`u8iYgrnnga(Z>l7Zk9Vxy`Gq0+3xiV??{J=duW-t zY$zn2Q~{Q}XrHL@I4bk@zstGVbm{%o&>9lA%;MeJ;lMo442|z?iY9UxdAj|#e-zkV zo5lbY@tjs9=Y75i8~)ssIgMx9KV>(shP3$*hY7G+acL2W_UB=3p$0x`M4gHg5{6}( z*7=ZR=~*xI=v%cHW|Wq&@SWePAv%mr`Aqa>8a|AWx@(A;=TFhs{OQ=RCkEN+9HxUZ zi5NJ!sl!F$dsYt*t@n)^CNArKLy4ixIU|>V9of0-grbul{PVJ5pF^)1KG0=khHjlz z3;K5+wp=*uZ*)U}v;3{0>|W~J9tGQa3I=>@1;7NNi&Y^mbHH{^_T2lZ#hxWe*=_yo zh=iF_*YX3OV$BsE0OHOJcrq5B?|A+`#RVR_DSnMh+6&aqFX1=K=;iZ3MZcdW;%{iP zNvseCOg5O4N0^w(8`x5qWzi-~iP;tOhx@;O^q|eqN2Wk##Qk!hzWaVlJ%1Fo_|~Fy z{_-c9n$$`wP<3{hIT0`v}|PZDvzdS_SD{Q9@1n(A-{(uCVNW)HOwfZw>N>c>^wP4C2h?RDnaW$!P_%kqkmdh$p5P|55 zqtD2$60NolHH!n|8T;=XM(M(VSC4aOx)Y(z0@a93BhVMp%tC%;QYa&gxV5oU0?)H&T7qhhEP81-A&QnLr!s zgS{r0nqF~e=H~vEIh5FPFr2~0ayFb0?DBdC)5I-92KH8RAz6F+RSdU7#|UFsCUKDh z$-#Q~#mFX<&P<&Zbc0Y;pw8#pku3mt|Pcs(papX1cPvs)vIxfqvrXC*=gS0 zqzm1F4KfSh;6g0d?X&pII;_!%)ECRMVce^@-~ zbLX%1-2|h!r6oIBDT}xyqNY;bgY=)Hn|bTNio59@l58XkTO!N|NWEnJ#WWwfK(h4B zf|JhH!-41+K{`o0WW^+d&U!KlkLNalvAaj-RiKvA3#;apNrv|;>gP@mOoh+@G zJHP!(eQF4fvdMKiTBa_ivNg>WYc-RoKTMePRlWt4$lxu<)m`Gp4{+hj`96w{?+y^= zrp%$SE}w^HS|(<-?c!Nr(Kn!OXZrI(#heCigV6OKyG|=^l+L7TW z00xbMNd5z-{7-dj{|B7%e|GX?_*b0#{>CZ)1>gRSQ)c_O6a3FP<$v-c{EJgALqVK9 z*07SL9^Cv_Ic0hlT3lvEdRzu(dR%5EhJS@q{(s{u`2UPkX2qpr_^#6nn%G#%7HJ5h2-65$jnblGkQB^9-Tg8ejw$W0ebYCdbUB?=D3=li4(BV#F#e&*c0nvP?IOv=YCiTj*F+=htO#=JxM&jx(Ik4{3{B#z;TrQoEK zMg{=4o2)3MSAt&kpKN7LzKV_ z$Dan>U{{`CXE+~F(2rDGC$qy>1B78?hl!1_@4Ef%zU6I{DEk+f083Q1)U+o&&V1Mq z(MH_kMWubm0qyf`K+oz(*>1J{uJ-bDUXkM_5U8=SzMYzxy{ma(15(^i$7qzVKfUs! z@=~h&D5O8y{JAr(T(S;Q+}kKZ)%nH)g6&Hb@lm93xTt%e#wP=OBWfm&PM`YF(VzMx z)j;IxCOrT?$EFBj{ zMLBCnTotxkO@G;HiGAOmwgSz8EEMQ)5d-^v=Z{S(ajPD+LYfy>@w|7!~ zr{i7phIR%qO+wDb(s>C5E7g17bf{%=jfN87({^QO&a!gzHFgHaV5qEYh^^I7a(;qr z97QRWrO*jg=bXXkCt-^Q)l3dr&oAGom5;mXT)UgXp~(s#(mii6O8yo^*klJ-IbXq_ z$5xQuUT=Zw@{jYg&WmxBA99TIV^@tRgJLV_tua_zFzpY?X-jrZA4a7KD5%LB4}a%j z0;@PqRP<@$n&}u=ZqGn)1EHZ;92`h2C8JakgtDMyez>WmnAwE{N?!c7-w&r{ZEs?H zf?B~yYG%51&8feE)Vd0|#f9hAC9}vmJWc+J6ci@7zmz;Fpv)spI3fJ2A-cJ@3vg=vP`o$g}pksh(_?FqjD|cJz0snfU8P^dba`^y6VTocnfEd zZ1cfyG=laX9{@y(P>U?tco0&PGz?6*NV2)}8GprBW;%{+~`QR?_9c^7>0LjQa_uaCedb$uqy9I!^>5Gw-W=3 zl{i}~30j=K;gCzUeFn^-Jzk=XM-mTUBK29Q3MvpZu2-$`p({r74Er{irTL)(R+>lr zs@FKRas=mD?iGOJayT4@15EVglsIJ2b(Vc0Yzt_lQ~s!k@}_?M1s2l?J;79A7WqJZ z&s;LA|CXg&yovMyEShglYvW(O1)(=ZP+67J5FOyoPzmQjAVE}oQ5VF?o@`#6fzk&if8ip`Zaut6 zZ~&5lRb}6Fdlp2h-XMw#-r;vX;-fq6S6A-KhQQXzz9lH0(o}*gQxm}bn90>zjdDUZ zA1`gZXa*CW*&I0cFkppMr7Aw~4qYNkIOh(%y!yu*INMIGCgp58&=uyKUBJAh%c}N+7A)=?6E#k9 zUrI(|kC+@`Tq#P_Dnj!VH&uf@uL`VOX9wKJ6uULYm3m7$Nm;P@IFw>G@&_^Y-&!Uo z`pj^$Z1%R*<0c<-D{sAloYxe=DK0-dvhh}W2}Nr)=dYpefS>ppEa;s1ox3wWzd4<} z@DJPqG5k!?hdqL-wu11^+6ved+I$~6S78wXSVcZ%bG0vwFmUiiXZ;l#aw&=(z7Gs? zm(V~9)`Y;u!T_^KlNAP|n{#D-Ri$7W*c-8!``u?7tRPhF+7o7sqtnf0pQ%#^ppOkm z$z74!PxuN=ZLPqyI|zLcUEUS(G+eo<)}kbXFOh>iaPUoO!=%?{x0{(8oaoN0^+YJU z)MaiPM;T!5fuM=}d1+-kXO(J6bzhR{DCiL+R45K*Cmo{EC6@=KY9(Sxm~eRu^57xl z=WNmMN17Zc$zMF?ub5IhZIawWi?_Bc9UYAXKqBrqV?->2!R$U5x;+tA9z(v+1npv#}G$JaPaniX*xro_s4|JDSnx$zqk`Rm12?1&my^{1MAnm?y;S`;lLx}E za2A0u8E*>ua}z^X$g}$eM4UBO7WG8-9{SFeCy~M_@j~Mgt*pNRTMtl{wj-tQb%l^0g+>WMx$7Lf{mLNApcT=T@AAwo7+s z*iT`AxpTKZq#m*p1Gkqn6-6h==e8$Vnn{kd6Lum>Y)dn3Dd~a@kO@r4!tWd8zB<*b z`bwlvYnneBE;Gi)~N9rf0;}^JSL@y0!4xqDr z+eFmAufBPF7>u$N-7={#XXB$p;pbCFP%U5`b6wjKnT}WSRG|p6T@iTm;t4am%b0Qv zHzEmP5+Ko;o=$-(EQ@m^@^f$?>GEdI3O_#5ekM=C#^F3%my0v%a^Yr1e3{PY`0ico zE0YNY#C?eDp^! z*^la+TOx)z{<$SPY2i?xD9hE7?e$dQ^ZO2hV22YY5WmD?Wml9HWyTHVn_PF`Mq4rK z!2+ssX@58(`ER`2P4CY8y*|=yx>x`H@bqQ{!K^suCIs|`f-!e0+ zQlWV#qE7~c`YOw_de?jTcao_7OB};%qSQKp^FloYNf~!oL70-H-@MlOIPP2-kDDgi zDuBr{o!CA)%ssAWHK6CJy)o`uxPwe;E<{f(n*eqvyMmQ;cMeoiOLLTL+(`8{^f#qu zX!|9*g6JQ6S=z{8{qzSRy%Qh(vArjHj(M=ia1eDWwHQ9<#^*pE^3zSw0Iduo1Vq)S zrwJmt89}c>yuAfhHm$3NmKy7TOEG9oKl5C6)VBcHw!e`jqcHzMZ=X{>o^9WT5nqSd z@&23}jUZ;U2Sk5#g7xuKP-u2_Ac}?RbVj~pWfF< z>4)HF2uh+(feOZA4f>nDtllkkn??aUh*3;kw%WYSi(i86^E&=JtONKY0EiDo@VZzA z3r}5+Zudo0x#=;wgq@UjM;8w!Y+zPTm?@DcSYs1`NtEg(5n7K^6+O<4TKGC0E8Iol zVmC6FV%OR{?~q)73y_u_=3SOG3jBcLs%)6RGd<{}Fa8Dej8xd!thY7^WV33?VF|#> zWK>10CPCy6eWs!8M@`A_je{DyyXUs~6d4`5g-oQfHeMUmr5%)wUP;uYC#n_57(>rP zN2*5*0j3PhWWlD~cjuK12;?Z6F568w7_7@B`K*`TK0MvD?Ld&k`}sg8x(aFA`D1Jv zcb!}PQ|1q^c)tT72BGF#(udqG^bK|&Q^SzFu3pMy+oCD7Xc`PWgImAld%>tRij#Fo z{M$?Vj;gqfXkRTdI@o!eVQAvby}@3SRg;3{tC9ss4hF6Y-6~}?4|*(E8DfGCiEZeN z%Z8!k^2!sny|BJazY{Kh!u_2kOl#OC-5NS*q4n=1TG$&@QDoG5b1BVWZr5z9_-1fs zv?9yUKzfnVWys9tkPJT%^V3kDZi_h21Yx_rKm0g_q?XVQmJL&55P3tR$Pr3A^%3P) z_jvQXK|~FeV#F$!gmHq;9vglu5RUyNJPQ5Oo%q0R<(3Avp`lMqEG9T`2_#h*EA`{s zoCaFKny1$QtcjxwtA`?l8MSJWihEUwyIze1KDwi1JAY(L@dL?GIL)-+9V5Hw+0J2Co%oANj2EM=UF)&#w}_c8XHP zHisXlC8J%+Ps-v22$I1iu6g^@MaAl=Dw^WH? za~s})Cxg512pjJCij$?D(j@r7DDl?9qH08Z%A`F?5u_sAodrm|3`XJ9$Rde%y-D^S z`P-%h<_5YXM<0(a3?5|2dPfheQk~tHvM6T8E*xzr>qWmd#yMS2jxqqbH$1^MH69#k zU_3;>>PWME5E{tx-GW1b+Td zbcQN8)}su*f*Ll*93s9ngyxkLc>ZXT5(x)uQ^^G*MS(d%6GoskeaE-X+$}LZ<$t4k!*#D6Z5nh6>#=+q#$SLL`!UYmTY*MtA5sJc#sae>Jx7Y=VBUjW=QeCY49AY#0(Gp7B1vIjxgJhxG#=WDSGO#-qy7J5{ zJA!{^U7Rm8asE}??q1_tnujW?Z;=+~ZE7V>fX+Q%K%33MR?~F78J9FV{ZsjK%oc86 z;lAR~(|$4N`;=x#WjIC|5cbj!e@?<{kbb%*eErV(R`ir$KVYGFx1 z{Gl^u6b}r@UGBPVV7KP>G#jw(doO0}JWTX#SsF0#mE){DpqLM~HQZ!^3HSOCOZ7um z>S`Dl0Towl#HF7O0PzE=pwPJ$;$_&ywFt#V$4oR?_3)#YBtk~Q%?hvoAaUV5)q0PM zrYrW;?}6Zw$p~pJTIiqa!Ha_2ju4(Pp83xPiO%BQ@T5O*kF%B zr+G((#dP<{OBv&%Per))^+U8_GW!XVFtx|MQ6?joDwt?>;CccZCE8yl^3XW<&<^pv zQ8%^E2Ya%phJHz@tF5lNBNt9r#kC<~#g`0a@+H8QqfJ%%TvXW02$7>|PGf$?e zRLBZsr;7x8I15t;IfF5v1FKM!UKwkfv8I*Pb2wFuiuQ5gJ6~!DdH)=oL+dlTe@SOE zq{PewQ-@7mCzvMhwiZ!Yw2+xOu4O@lwBJ<7(_4w54=|_e*Y*vLeoQf*v}Hg7A7?Qz z<^fld7r5W?j?^&i)D6D{>?EL+XWJ`zgF4{6?E(HJWk-Nbp`B45PgPuv*68$eCoJXn z63Z7qSBN_dQtenSvFz`uf5I4r3v2=tmZ;(^oGy9piGbcIG78^Qzwr09p9t zgyO498Y9yvYFCuNQe6KdW+5p-ktxeAkxpWx2%lKy-p@cHG`@+VwBE|qX`?%hr{7^U zP0X`eBmmHXB?aHtsEZ_y1XUf6hj*Yl)%J{Qk!#H)VGbnKIK2&y!Yug*K&5;NvfO#L z06IWzo?RZ-5OND{rv`4oC=uQq4O6B=>Bj`u`FIbF&eDT!Hn~DJ{P?Ea#dnhH58r0M zmZPBX7Kfe6fMV8dx%1IUt=SKg-(Tu*|q{hmYJ9nO1{(8&b@5 zf4?f3y4e%C5n3yQx3jEB_v!d76?8vnU>Oqe>^bNhs z-cv@Lz3&2azM&hts~^RkO^>PcJ4rm}FsX&xFK0MU!^Wy}oF3$~c(jLoT<2I>v7spi zYp;FTR|i?0z6Uc!3$7TZ`Y3OQ{*a-O{#MQ4xyYY+qx!Gk&YdDbetdfiwMk_lm$7Mt zKoxVE+a)h2 zz6}K{t9Q#gfdmXrw-TWmh|#m|1hT82B1Vta&{KR=EC(j1*26Fj9^K5g%zN{=oFaO* zX{}ZQe2!F^phN5A$zcE+;->`04XQ2bL`^|MvXn9@E37;94zu%`7%Y?Ay~AV<*z~it z$9y^6a4NIE2K21cS~oR;pPpBLQA=<^sTzuwD#kZ@Syrpz+_eYA$~E`p1wsizP=8w$ zeh`;p+!>xZq{?#?z;~6R4U4#)a|=Z@169l5lBk;Q!%13(-^_6+*NQNdX%(XFWP?7X zgIe9KpwV9~gW%yDzk)@W@qB4Z!tMdbN3n$Od|TcamSOQfp=Npz_B0r-X?>2C$HeSFVmZc7KZO#vrH6CAln+`HiJxB7v-IKe8;(z~+Q zytfYrzIAnw3#XT8R^(%A=N|DK`OqC}4cBH-p!y^8{EU~BYOLX@jKzY1csGl1D&x$t zZ9D!Vu^w9lfG?Dn|5Dk6490opJQQY1;ozcrR%qmBBTG*{3d4#xbWcZ?bzJ=Beq{Md zOzFPdSApF^)DJ92Z#}JsUulD$*H>Z$JC;27n&FZ!uqmWHvFZW{>l@aK$C>DbVxvEM z_6{jW14Ro|C8KE{vl9gg=C*^-hSPGM%V>r;?~(b$03`JNppBOr#j}?J0jNgg4w*ME z<=|I{oY72Zkh;mX+mPP%xMkGyW7-UtCR(~f%82FrXV(TNk-Uj*VeF$bel1l` zanEaBajIjDmI{r#rrcCF2bGo#71nlK2xplH$w5{KreqiH5Y}2Q<&Bnt{ee%$0z3%8 z4C(922AHKzEk@H}*+}?iwy^D|=N8at?U*^oms&at2XGoNAcHJUd>TSB6El+D2ApJ* z#6z@|)V>$@UtpcvK6o1bx;eZXelEBWc^LhWb~8wz`t?JBVFy@Q?F;McleKH@X08Mb z?kWcjo8TP<{Pa6-SFTdt9Z{O2Z(aWK@g|(0)id|Bb6lQSELfJWdQ4%4x>-d?XV~FI zlB2b-a5w=2pe)rt_b;tc=*K_q2#}hL(~1fC6+q3UDJa%Z>Gt9<$w29w5a>w7LlIOY zz<%=^mZznDos|n7S}x*J>9kz6A}dXNiGM;o=l$U9>}xsgf)NOdwpS-y<1H?rS|HT@ z?T7sh+`Vh|#w~|1lQO6F@LSJokCm7!49-)A3W1Ed#9V>mz|x+Ksm=Kv5Jzx^dfU^( zHeyM)?~Qs8q!~K=XF7yR)hv?LJkLwT4~_N=3_}md?DF<#FQs881i#w8bgo;TeEgbE zKp9z+h@gDm1|iX^V0IoQ^Zq&I=6qSbT%$WyM8`dKjFpf_@VC3O1+?`1utsA+jT#NU zq`LLjsX|AN+jumOFzHbe-JWC*%~ir_7ApQ{qaR5SuLVY*cg8s8DCqHWu(w3y=aZ|G zqk}9a?~D#Iv;~hZSvIpr&~*6M+n8KKYt-Q8xa*R~m+*-v(Uy;J1K=B;yk^&Y?PGqD zPH`AgGKEk!{>MV9WW-gi0%2xw)ay_m$>M;|7^5BUlW$c&L|Ze3hV=+UR#Fd2PiaYz zfy0ZmhL9n)k;L3eOnXm9n9NMvgGp7adlh+4p-Wjl0*8ZoobIELbP)nk7tehLtjzMR9=ch0kNt6ln*)f1GP6yg#|CZ_+WHRs) zm%jBFPc{(VIkE4az}^3-Lxwm*rC)P%DQ$M+e<~F)Z#ZZX=Oqxq&EM@~fot7)__eND z(L-Chj`Dj)WPyt$Qfbx$58yrHn;D?kPR ziuiu%=IjQC-4KOP=a*O#<<14%3+d=dDEC>zg-sYDI&IW3@InYqlW#^t#C2^}MXl(s zlJEA@^1Jgm)0|hikVGoBcSsa5HEWk#03Vd{s-ivF(eeD$yA8*HN#7hyT}KWA6Pxx? zXbY7CL=mjcYwq1jGfYNY#==)b{Ce@q?FbSdLZqBxke|wqKy0xSa9hPk-B{Xz^Pt-5 z)X|Gb8oH8;R>|tICL;~*@LKTf#z8z66mtSA1*E-t1Er@FIZKC;+xSF065)`4QH4)1 zB#$eco1E$H5jCOr$Hw)N93R8rEI+^?MIMI5nZC9gWic6|B!&U`Xj+E4f=eUry%4y) z%+k?N@RsWIZzC~9JO*)98iC*CtJ&A00PaI$#VdT=ZD{s)x>@V{&Q_QzZJCB{C#sz> zGxmo=aj^!ZWZ`a_e1E+wiaU|t@qY%?Fv62GozGq;aLO3@ez2<2p`W|L1S-~&(J=pL zCt9n&U7n+(lp(ItjCVjq^Wy`{VB=%hI|>)`7`ROY;N@V^WYo4!mPrqDddf!klglI= zt@YAch*PzY{MT-lrc#`f_SMckKU~LAa1aqelQR1Gi}1Rb={R*t>PG1`nD1m>v5hQ| z*mHq-)RgyiOq5fia*{{BoZ=UWQ3nZb>z_ZCRE7L?u%SWOGzM( zK-_@@AR{>6oN~80X@;nFfQ4iKAWdkK2qvWd(WccNW08gp7I z$x_PT5%ev1yN+D|E3LZ(1h8WPZ}eeL)XXYmL@ebF?5PIeCE8Q$n?W=i{$iti`_6K< z067TC*i$RbYp1P4AVZpFx0Ikz>jx%G_HfSYzBP4s)ZD$qsf*ay_;%o9CCrhFN6;cO zt1*C)(}ojQgyEFgE@1et?-i4WlrpH&2biJ#{HBaX!dR$Y(_mC1EdG$S4-k+Tren?t=v|4a( z|D{_74H0z~QWO$X;6lW2%=dMk-9A>`%V$7LBVT1W&%h4_t`NPrmgAspL#a3!1>_^` z^oDP#(PfiJ^I!u$l4@nJ>}6$6#9mB=eHG>CCU=aoJF1bz?2TA})%yO_ot14>ZIxyN zuRNcO>_-Ci9CfvBIX%ZyXm{O?-cR3)Z)a~NTrS4P+5?uj&OA=*BXwB?j+K?1zfyjb zsGO?U?^5>yuZU{AQ!p5T3LlQ@Sx`)2MGu2S&cIdRahHAX4XUh{^AMx~TvBs?EloY} z$3@lHh+)}SMb8Vf!cXw*dB7}mx_$2&>oh-XxMNUeTaboUOPn2KXq57-V}_e(8{~dg z(HLC+UR00zBmXq`o0>bMP*lua$%Jd^X#7zq(nS8P5p(}W{w7r@3kjcZA)no*c5xjb zlwCEk_;W_$tTY=Z(!z+AbWvBW87`gvaDtg^5$Mf)_ZphYm`iK&``yMj^HbJMIO3#E z)M+}4-`Trs+PR0Z((Lf5Qg%G9NVR(F32P;Z_|p(Owyv_a_pdOR<<&jEVjm(XNL4Vr z67KVWnT?8x8hiSCvx8DG=QVmPh0=fkf!I!5FGce?xqD?t(Lwbf5zroQ?)8d8^-~iN z#cy0sH78oEYv9=IjXM3;`7xqv5+xsT1@Y~=xPCUj^z%$Kz9^X4JcWI3Cd*R|co(gm z3dFG^U~od+i)jI%V7{)DcPm2gb91t@T54M=E(V9*s$t6BpV|9aFx|FqXhwUvGJu*U zf=j>(TQ%H;C^>i-K*Jl~i*}%JwpyL?+OyJ$ogyE&Sqt9NE&z3h#%^*Kw zZY)mrKfj(eYN@kUjTNOuJ+}5{@(KhR@>|}+F!J+lXr#!fFz~;^c(1KM-(AXW5xaj2 z7Hz+baT!`zL9a2|3o5>BfUFnfw(vCPy!39ZA{%n%FGP9y8AYW~vK#b`J|50&XLpKm z5&*e^&cK!X!M7s2i~oBVNvhx$yvkGLRM&)Kd!z+?`yy*(V;ANxKJl7-MYAk8Lv$tt z0##`jq~vbb=0n7F{+i$Gh+BJPvEM2b+pcW8)Ld8GrARzfH(Vwbw|yn3A7^_pu99my z9@8Xe$0b#M4*}BN0qpB!eJinmz=tIZT9M_-;7)31!|SkcEEu*OigKR zt?wcjVz%H5Y6`70TE8zJ0c|pJY#|l@jcikV){!~`Po6G^V8A=FaRm#PF+pe!Fdkz| zuDd(l<*NcmTG`-8dV@%jul}}iX3VaGL*Y~E$cK4)6tYZ9GgB_fM9g(orp;M6H~JnH zF1bw2I=h~?=hWx&0a;+4w9SFNzUk7AwGW<-K*!x` zf4|qGwVE~R{Fyuin(c8sbGXznO;Trs1X`x&WT=~HE4lq?u@vi5$Xi+4lM3M!WTkKAo3x)<${Ft}*z_Qz=tC}NezxG$#tNF+kT z;Nl}QVm_uIx(;U!#e!j#&@3=xm`Sq%Bd))U3R66KqZaX1XR0C)Il zfkC~(y7a67{iCrx`8gahV4@@F_VnG>aE^9#RLetSs$@YGQ-UC^7Q(`Qjk{{Da<6ps zqBh)XL7CvGc5`>N9fO}wkho}ONzzR*xF2j{ugzEEu5l{+rzsZG&#S~~JH3Pqyv+#9 zVzw?REO^uHJTieG=43)GW@33B3xhsvPh2TmsEg;M7T&K~w0yDaA@M6UhPu zG25}zxu?p@*G-%r?P`#5;$rP>!jx2+UH2^n-OIBxhTf0GW9cyEdPw!z z@D4RblUch+Ex9N%Kr39tpU1p(5tm0fVbBEM5F}w z{4dz%KdI9H%r*miXju7&4Qj<5ZY)f_=hj^|HXv)|M)Gl{n73J%5VA4j{k8N zW$mo>FGu5lHzB4G za&ZumchGe({Bu_kdAdLL{rByEH!1$}*Zl|hmw|!pzej&hVKh=}thEp0u73}PP|42C zhf$snJ^*Xy3V^Ut+^ja4N+98?U8ex)>wK>i1^t-(vHrC16lyfRv}jqIE#}m=vGlAV zD!%FY;sV9qB|Q8x0u;atAw&v7IjdVOf1VrGE&aH+KBDNBj1;dAk}KSvHzC*njGvv| z@{T?@WN=@OuOkRvP`8V_DCH(Eg#H<2fK)3wQwPKRDQ>jV&5hW5WQS-WztJ$=j-`3p z*d3MZC_a`ehQv8c8);lmLR3fHRhmwlc0GHeS=b;97p76c^jXT;mLFw677S>7J-okh z0`)Nt1+ocGH7w6VEeQymNMiTeJi^6bXWUOAK5Lrox`JQ|uY^~|(!egWSk42Q+}?=R z3cXB(zj||)Jw4F$mi=XTS$Zc#j%*uj1a9j}!{Gj@u_$)IR*&5A+Pn0Fn?)NjJhPGY z<5P%R1AMa&0l)(_XwjA{yU@3hD%TPSMtIi9siFa?{Aq4|7H))%=9ln_qazB3t*;K z&JfFxBU>MGSD(Y>0qF$eYdUuYkqVa7%GfMe9kUPX_%V{= zj4aOU)g1^_W(F!C(5odtN^$zRU7fLPlb)QI8*DIx@-j>WWTA;U;f;3QtU#^%q%WJv)mM!Ykx3ZjO!!Zpxiiu7v!iH$#y^DBjJ^#+%xoj^WCz1g@8&ZL9qSKt2(pu zp8HCby0G=|1ExGv%-SRacJH1y{4}>Rma1(5G?h}w6-rfPzvOl9niDam#q5F>yBn-lF&sEos66RSGx62*)jjRZZZD#M*F*NeP!kRA1V8n zbc^w?X#Bfw{iS68iy{brp6fs8miAqs^>0>l>1CCFRk!Gv*uHX#=>M#N!2BgB|GjSg zc^CfC@Bb6s`v0kc0P|H5;cFA!U$2yZMbc=#F3-l$&cXESwe!csL`s0|>z(wMZ2ocl z-}U&Y--`~CFC^17GKjAK|_6O14G6Sn zhx&Exz58F?eN|OUtHiH$CptfOUnx2Pj`S$h1zv)z&dND7R@r%En0y5kvSuM9@6 z$A}#IFfbz|yFacKD^{6dap4|_Kf&LH*eVJRL!1UhCL=2QsKs}kk%yJ>{C3OYGOR{W zRgX&%V{H-+w@f_IlbvAw!}2PFGTRu)F(zM{eXA_*8r|fxCkmokWA&+3L|*J!Wk#BW zXGzX;iuUMMe)+`!-~v9JWDa;}UIf>$N*}rKm3YJC^=kVll^0pr(Pexa{>?#G0ScPyuCxl^116sqZU8>KE#<9i=*8HYxkTMG&UT7_X zZ7xbmlKY|`BBPUg?ru$IhjwU^bn^(CM^FL>Av$|iK8ruukegO71RNvdIG8S zyqPmbUIUS2!cW!5~PZUtW*3S%T9I8%X^subk~AAW0n4cH2`iw;w=ITsERQ9~1X zPFW=63B+;mCN*2mfl|g9Cqe;UmBk&0fy(srOrGM`ob-gEZ`8h=ckUg53>iVu$eW1y z={Y73kq8s-r`u~8N4b>SN6=rgNxFTm^EWKHnPccA(u|r~pxKrZ%vzJ)SC65s=Inx7 z%c^`ZCy|`Ki?Lv$z6bX`aRlV_*^~_yX1gcbxWqow6p}(*R{Q7e&Tlw0O6%}9+HkVpt9Ozya3E1*$ zu&{u3!22Z}uufdrN|oa+ieNb;{mLp-w?Og&$Hv)0Houb;0x-FvTslXM*jo4AjmEEU zHz2B|;0bQVVvtSZcLGg&DD=H+RaWRzPD<@<3e*R;*0ZW-?QxJu3fr25`^*H``t4i9 zDQU8}ba)oy2MTYYN{5)oNobUJFGi((){mpS6JKbCTelC_rJ3O9t9-(el{zo>?PF6^_`tX|Xn;JZe>#2Eq$*s$wt&^%E?T@lumXih$J7%QiA z6X_5#^}^`)sBko~qxk}78af25Ybn*XlNwJcz|s~4N{2R|Jy?moIvsZDjbSK4-m-;sA z#a*3DxG}q!rXlE-1N&?ur$52Pxeh$aNUpF3z=AYv@7#HJt5%11i`SHnM0@TrVTZ2; z3E!EQ|Ek-eBac2Sf3-sfxGimLft?9fd5L%!lE)$I89Z)Rg*L9VV6AHMUC@>4 zrSFqv#y1%&#LOr~7m778VZj7bliL;G~7lvC8 zad#H_VPTK`SgDsbZnNPkBv|h9llQ)ZNfbEfN0&^~x?$OT^7}7Q?ws^YAI!HkW5B)_ zING4l8)qwU>a zZ*nX?ZKW89TQQFzXqyo3{R~q<^Rek;lnFB0+|A**dD9CwG)crrPw^>zvA5CYYKQ{b zbt#-N@8Qb(MMFn=C@K7CIaX;@<3*$@w=stf+aeG`ND#*IDOb%&=O8J=^z;^kUc7Mz}q zY>-6zdv_l`EZ_;Yvc{p2PuZnzOpD;D&5LZybrJ?V4+6{E9w->TwET>j{_Y?xFw@tD zvxg5tjnHV7!LWts%b6k4g8_n|$HMbPh>sP(GKRzM%Eoj$ZMjk$t)BUwwdSh7%ARxiEY7 z=6)v(c_s9@2jHn^;c|>)wc-NoDdMx0n!zyY1Q7JS*UaI75bvqx5pb)|jFjPVwkhIL zBtfZi{(IMUI(aRZxY;}U#SQ!^pk4cF(qK zM&Dy*Wg!J9rdFs`qCl!+=iHvgSD5K(8fM<*N%NmleES~VeTEN!7+ly-IzRpGW9Xt? zd3CquES?qeK+uxb*_rpJ&;l}Esp&-WY6^Cwr>Pd1MD}LmE9yzXM<1elhQ?3tBFvc( z=ovZkxx>@HE~`zRz_EhWyTV7E66Qv~EaSF+9^n;<04AA45*HAH!nli=BPngw^a7j{ zn?CQa#f>-zG5=5_k7rw~QSY=0qDY=3wOldWC^JMS0+xC@9;(K!`wZOM>>^2+y5hWb zOGLoG)Y9e4tnjr#r@n|*o!#8n<{0boz4gG-!Q@Rt63vz_U+8ksX+J9f=q1;2&{}T8 z3hJgpJg9P*Q~WSY1nlnKVX2RI7(DK-iw7+)wKOR+h;A4Il=M;l#MtS`J`09sY+IFg zX2#pq;(A@5Bq2BAGSS`Rzptdis!an?5~UFC>9{yQ0S*=tg7f7j-bzr&Y2iexk$Kp# z)t%g%s5uSVmMnS0>OZh1WYvU%zOy@|^KgG!1kc)A1GEqds0v$mj)YCxvuF3@*`XR~ zOhtT_HQC~^p(AkU^;bq0UC4KOr0KMqC;Yh06y97;b^h3;ex%P~c1BIog)}0Y3SYBW zpfwL9p3rUME(MX%lNgXe+vL5S4o?>;El(jfsF{l!0Lj*FYcKgVKpPP7oCCaG9VcOLnh{oFqb&=jRr*X_SC<1; zjH?3A=((bH+yv9BR7>y_g8cOB=dcrq7uZ%~*qy^>nc ze={A+p!V-o%Yyn3M)DQ%XDuh^F~kEV`gFTM0ZX+@j9(+@Cai@U>@2b!&NI!bVJ#-- z;a3Bl+u+j$>)twE4HqE2zkN{k5YH-xZ(X~>QqT*FSbiRH6o}RWs(WSm>}sOwN|{9( zao_!X+#d=o*lHVncDMd@GaL1==DBoO$~5OQtyt)loubYucf3BhmAfSG7;6T#H?Rkz zmjV;aY`t%{2{Hkj(-o5u$fNOoJ73jLI^OH&?`aOA{SO<`ahUK|g{fqQ`reWg45{Fwg3<~x% zZnH+%pwVsuWZ%|8Q1Mb9hN6Y$L_#OQ^fzCjEJm1EfUd_WWvd%8hbO)E+&2 zIX_4@k?QE5XynptDw(O|&P&T5a1rN6m;t~fG`zFWMzyc1JW{tAyLvs&z*R++6;n!D zSV2R;3fF70pO3D%MRY?#Zbqm6lHSD-h)|Tzq@M%3D&Nx+(z;Gj#~G0b^=^P)(bwI5 z;V2~(lK?>K(D>=2T?#42v~}rol7(0`;Ues0 zt1p~0{Fq%st%8L?7`WSVL?Q1!!>$OjPh zrN}W0W^pQ5q6JRI3~5RF&^S1zOZjw)^BCKj>xqlW5}_WQl#f zhsGqAlY+I?#|C2L8C&{J2moXN<=NzB>S>E@nwr`Sdz)_{sN|thLfO@wp?5lKiu#OW z)Iw}8xeE5RQhkFH>?>LoSf;mL!HwNioNkDWP_fh^GaszCXLb0N#_nMBc*!QCH?%4PQT&D8-um-XA zoU4s31+sK)YUnP7SKv|;S{ zyGn9=jiX`iY)9%=gUl4Z#63MQ5?_bZN@Yu}w! z7!-e8g@W?gpo{~d6Z33LmHY^f4wR|u^W|C#d_0fXd$w1&xOQ8aKD@PS*$IazXWqBF z4WTn!_4D})%o{Kia7`6yygq_5zB+=hq>IA7UZrmB!kUgePD;_Hvc;lw(klx-;l{G2 z<)KGARmAqXm%f(l#CSSLZiPtZl|MZYzYG{tqzxyQdWf@^vYP@ff8P`V4suW3Y9(RWRDziCA+a~Z_B2g+JZ zSon_lfZ$?wE}lNwYr{!#yFwFr=sY)ABU6mYmXhql#eNl3p1b9s!25L8Y>v2%0MvL?gsMzv^KOwS{IX4sDH7)R z#w0QQMAT1p(hQE4wcGT1jh=#KLUv5BMEUEJ*yJDb$h^@^^J&lCPddgb`ANAYMyuxM z2o4l&9|pQk08>{Ct*;juO=%ZwHpNE|6|(9NrSsI+b!PL9>Ve%RuEWJ8(jFM~<2$ofs?bio1`f;}6ZLJRBV)+oDz2hZIxDe~0rq_^If+X^va2~* z(e6CJEL`!yZLJuPx>%;ehd}AR)(BSkqoF9q#gKDSp>0RP(dqJLfU;$QmYhcZfz z5NPm4Z?*qWKZi5ctS9)6r&}V!*gt_reYqCFKGzhbBG7g?+xbe7v0G0@p*S0z{<=7b z6xesbFVVzB?^&;OMJ44jpnZ?ShiYs}*lV2*<{|ZyIKL#`)?oDelL*%5&kenFuq8Xe zDQ05gu()fcy*A*q(UTwHmfZ7G7g~0E+;^CciOLaOR^F8Yi`M|@_TjYb@?~lPX3m?R zQeonHMcf~Zc4W1GU8ipHqfa$>J&`N$JNY`flc({>BY)k3Pv4O=^-3JTeY62absRX3 z(OTkzXayA=ulJ29tFxFo5}RcJuuR5tWwDL&(P>dZQGnNG2MdRkZ~_%ldPvHWc(TbZ zv(#C=67WcSfr!5Z^%k@c!5;ke^Q>M)t7b$Y~hx8eIjQMW;4<`*TqK?_so z0xS(^f@TGhZrxXparZUXI{0>sg*x?d*ZiCWPaL4!x+OcV*$@q)k}dg}P!YU_V2B4j zTRfKBZJtj%Fg@yRf?-*PGm+;HLIrjoX&rzW7&Us&M!>r-&T9l*5;@%loOdY(gv{DQ zAV%-d>(H!Di#vAgb|uSf4v8US=YUZ8;OAS^aI%l23L??W$IU1ri!rPc#HD1Jx!sIR zHR5()!snbqE51T%aW3&>j4zh+0nd4Aa4m@)(ffLe^64yoP>+z zk+In;Unq^+QG5u9e3#OqBW*qJE0dNdmmUTD|4S`kUQnX_pu|lL}{) z?P!e^{cAlJMLvb9=$3~ zkM{-0(<52k5JTJd>eX(DIQ!%#a!rX0tUei;7*q(Y<*(&9>Wm$%tp;7_`m9Co){hg7 zo;i`SdNZywrfZqd)$a3XyC5@bnlqOW0uK~Hu4dh@VHu8GOuEn?u%hXz%{`0frZF77 zI;3_blTg~}3$irf37z;$gJoiigV`^!75(wN>`N+9?SVvOQ{TlX|>1WDf3jt+OI#qT`>EoBrO zd&@wiuI+!-87-~Ylgu{9Zw}atZusZ-tejE>#}=_*5dirorUFU z!hH5A{?m2{7ut#IE?ZdGm;T0ip&;!%0_V>~7ac;t)9OkV8Z?&>&wSF>h3LR0Oq48| zGx?vs*k5k~t<-3*0Nkqb-|q9dYrsrOt(7E*-n}GdLNU1yDtr_cxt)NCfhLTZEl!7= z<3uF$CD~g}3!JCpdXnCnPN~;oFHML55lfw4eA0XsCE$TzRjb0CmGx;whD%YU7mk>V z;}6o_Up8#f>@2XfqdMgG1m+XIyj>sr@EXFt;A@ywa{%gaJ3H@psD~9eu(_?z{0#Hp6#ASP2C#r?V45?`;IGw=SeV2R(SWCUw+ zf!W43Y`^g3af2mNH~9js{Z^n=r;Wg1xG_$7)VYv`N-x;L=&2fBOcJL%NoPi>oU5)w zhFpl@*X7>zsfvLp)9K(4JqSKCE55<-sib=-Gm=xAXDAZI5 z10&Jauuw!q#$?l`sy#QyQP$522>3^NC5x-rw6XW4ccc~-G5&?=?=QQkkBlr#2J=q* zkG5YTSlmH#tTZ61A~xL(nKHzaA}Q}Qfm0%}$(V+!uG-BtV_7hp>dubvSGjC3qhd3+ z>W1ymIe~r2IQbnjRh!nmo}lwFX^oK)da+STE4+M*XqwChrgDsRG9i-FM!zs>%d74N zVeom24MJy#BJd^D*~>{i&%hFwEcYpz3!9uAdEwAW(%bIDuJE7w#T|Ac&q`hppe^30 z56|-#Zq17}U+@gUxF9e3wXZj4&jjhtpVBpVE*TVtzy!)#K$iA~j()ZF;*DiW$DT8u zr}=~*XavO2I%MF^P%0hMbbGoT6c5q=z>s_KB;?k|qGe@~9?}*_4MUIlRxFj~z){hv zq?f2a67L_)P9OC(%EU%K?_`BGrP0n*iE9_%%bpU`U3y+l>apY7`&grgIpGS*-#qmn2Lf=95_~HWjkd>gKeEo^7 z{~>Xjcrc4eycM+~rCzm4SaKE@H4r3=U-mA5rChi@MmJ-plu)}IP8)&%U7|oa`QSNJ z2TW-_Dq)8MokP2dK<%k=5@roarwG>9zIi$y0UMDmP1LCO zK9li%^**aQQFMBy{8_)aP2~J|acRm>7dlxYgbDTtW?n{1)|EoiDNRc zvl;}aG@YAaU&p>yEJ@xnJu8>%$)rVm>R;n_vi4P-Q14+2c=Ff2ci-G^X*D0Atu&%o z-(47g)%J2*Uz@!lyLh3;9pN?U@VoHr-~4pGDd^Lq1LieEWz3}N;xf6%cZbZ6tpl$u zklz#w8&nhRtyn3K-JJx+j;@L??bJgL&D`3_CnyMY08BA)&OC+#=`kCVL%d#=D!of$ zrRk8TF5~CXVZBfjt!jRYTqm#;%2*`g(V>rE;k8^p^6@+He1%LklE*3mavWq432KfWt`y? z&kZdn4TX)pOeCDg6XJ`y#5O5SJ$BXL>55RGLIfM600PywtjbKsKFaG;r=}=l`9w1D zxacZ0TUs)u|9S=8yb@`iJlU$cMpZ2iEQI!3>C^6WCm`G z4|vC`@qOxv8{)S0+C+k1QuHjFa8m$3h8U2`NTXvb?;`E+SSaz8voSpa?b23^GwV0Y zKV9HxTYfxuR$<}@gZ40re(ROURoA%x!mAxYAIr)x42vaF>+r%SF%F8jvSKu;BrY9g za&d4`bn(C&5lEEiecEc(H&<-wzmJ){Tw(8ScOAWW)SOuCKD=+CbMNkNcdp)F1C~W* zu;7?=9RWGME_MtH>B}C!OCBUhEz9?%pCua+IPbJDbRLWZ)kmlNoolyh(G1&>^LKV}qFTTg?Og-r5UbLK1=8Xmzs9pO zr%|yXow6)Koqg0|sUn&&MfADZ^czJ7tJwX-8H3^iC(ky;6J%A_F&O&r!G#IGFxy(v z`{7Ktoj*;S;ilFhMDO8)>EH@`V{&ajP4C>}jubTzmyDqFofeYJ2!z$hIVK*A(~{a$ z4a}@G_-*d}7xS}c!2SzT5`-K@%-6|v=7$I~IVH`ePty~A;SpsKx^a7Uy&r%AqvzPH zA=X$yddtr!(G49xY641IlaM}xf^azI2<&X7mLSv8eR_N8I2xsTK)!pnZe7U3B7B^vD>xsY@p zOf`+R$%GygDxK~_sNue+s2i~B;7U*0!Xh)QITm7*b_sy4bIzy^J@!`;_fa}ySdnr| za9VfYWZ=oE1y$&Vb1w)csfdDQ?rcBQz}3%!CZhr9qseowemQNgv=6JIRUF6rA3U>_E5f4p0tw$+IHG}Q+6-_l2n9vV;%Ey z=5b{hcA1(E|Ej5h6%eZ&u_n7cL8FQbqg6Sq37qRKwmbd>&J4?%T9Oq3O1658x=-$l z(M+azy2`aH9o%=giVIeOjPYY*n>1coL;5&xuV{u9a<-a>)C(6BX&wL;lWQzR4g^99 z!TM(PHKudHc@u$KefUEu@ziuf-hz8X-zJoAew3~r_Nt-~xu9UQ?n;Dv)j*bdPf77K zChG(kn#8{KPp(4AGg*-%D_w!lYf|Jo)_Ju@q!LVnon_pnWBS!mku4|;a*4sE>EaAu zsbt|FGlHkPBV5Lq)NE~dYYbIxipso!-r6K_UEfW(mefD@{~)@|8fli-%g;7 z1VMTUTz-&Ud4WS<6c;O}rSx%!HD!O!-wmPmUQwYwNbuI6z3B+1?wWwo`&&YJwNjvm zT`M}C8X%uBPJDJPW7@ku_*+$mB4S3swIgr2cPn(;$mic3ZuccNpJWr+_NJ9`kHw`oeD&O8(bG$MQklABMJMw&9dStjq@Mn21t?cTkXusySFsvvA)&f_9 zm)$)Xh6t4V%?yGQgVzmnZUGVo(Qngx7Y1GuK>@M#yt2ZF5&?HOn?D~wGN(C>;ls|= zu@K^Hw3s#mvyjMjNv>4CSII}8Qz3T&-AyH0+C)d>eKa@bK=bmpsb=6b;{zN6Tb}FTWMZ$GK7&}Cb_7c_^y?x@*w6LoTj4vu&GB*eqP}UM9@P5&%cg)_;RHJHrO~7i)8oqtW!U9MIC*fjUIty&lM`F- zr7O20+Cbz|K%;9;CJb_|vw(H|T+|A*^`!CMgWaq+Tbf+%zUHm6H8-(tQgaxSsa@R8 zCY!z`yx=&0GG0m!M8gmHP9JoQ;54`*7exL=O}>P~uZI_jZVH%cTu~Gvzh`zzDAa1q zCndQs!#5H9SZ+uM&_MOX=zR$eVt|Z_wz^0h4d&tlXe}vi2RqU&jY|LUIQn97!l{s3 z8X8aOP%B(dj?CxgGTwPtY>lUSV8UfT<@l93Jvc+eT}6!CRhD~GmYQt=$`2bq}D_v)xwvXta;=10dIqxzgpF{ZxA)_OGQ zm@bBRXxmyU$&VaC=w`vh=}W3$6QzLV`$a-C{X=Hy&(y+y%WUl@@#Vm1go+H8^QeNpUdwMccZ&|x0+UlD>k60UBMA0`7E7xmDkT-bA4^yoGqOs z39B`&0X0c0DP(Fr5W7=tDsimI4~ou@HKG5zx`gpZCj7s~@Eh5!e`V_bJ1d{@ujJJK zDJ%c4>XN@Q_dJL_1&9GfGfb*a9h(CWnWx`^AkElD&4;AWDZ=Gijy}pi) z^`hwG+7TWKj*07yAZ-|NlQZW9eVa#t+l&}u&lJ?ATh6~mRO5;-Lk!qhlp^uvFDn0F z#S|F8JQpYc)y^M;SjeMvZ_m-qH2@BlKC-?PzNN?QDz(uTfQpXpt1|C^JgLHR`{KyTI|ieN*e=wh0+2INN*C}3kM@Fj;JgKnt3?JcWT zIR%&7tk0D*?5-#CgHdU*PEre@VS;MzkiKP+)h=V_7x@qh){1yMO!KwgLwtTqwPO?% z(Uh-_KnrETkuFc@^sn_~Dp*|H4OnBfD9~l*igTH|>oK+P4(Mwp2jF+MD%EoHNx14w zCzS=^E1#c3U_ibMXU|iS>nfXFnfo>+*4ap&Iy!AUL2Hx~RBD6nF+bO{HNe0U+Ch(i z%mb0j$rTiJ^*80`_`ZWi`!ET8QGbT6%w26vt3j}44sP1;-o#ibm$CbEG4vPeui!Y87Z3v?wn#_rXoXt9ZDS2yt7-?|^yrg(?2k*&`E`~`T)~JN zXwqtjrZ0(iX_B=S?2nRytT@NdaFa;Bl*l^bUiB~`ja4*$WsMP|QVo+ZV(4GMu3JU& zm>B0CtrhWN&MTe-eDGgs(3xqr1dfx3NX%Ew;|g-q;uDbyVU~n8|E!uG^wOEeQO(4B z74Da`aN76It*5b@@}nNo2qNMuQhgS@<&=UnrekrJ`Tk8p?bmVBTB_F9t1vCNd}XHz zljMDQEqA4?h)-`VASU8$kBO~6r*OhzC~nI! z^~(B*LO;G{PN>66fE8wpTirCOeDUtkhg2IFbN?sV{I61&-;k`oH0M7g>t7du{0|BF ze@GSs?H}3r|2Mh!|4| zln@ckN1?KWFLAA{AmxzZ$G4HE(iL${QnGL*{gBO-Gy)#k+`GV`jEPTBSls%Ab<@)y z8xHr}NHL1&b@1kFU{Pl5wc0bkGYgC7EMBTFc5)GIuE-PCq<+`t=PyIBo!)-GwE(?R zfc{-=HmfqgXNoIhPq|qHtzjcGJhH6ieU}i1{SCfw9+ry%FNa_; z9{F{CLDb+2G@*;|HV~HS;Yl#n*gA6kLCqo7PV{C~?`wl}2GM@QQs<=zjjqXTyarZ| zKGGHdywedf?Q?4OYV%>U53%kva-h7Brx`nAbRu~l?l~JR%B`tmFEK-^Aws$V6};Xd zMVgPeF69T293V|DAKb6pKAfS82Kt7j>5nRvf7h5W(Eerfw?p$!TmM>1{81bCy1b+$E)XdtBLAy3CiFjQ^INIv|GGYCKf->+#~PuB@u0O5k(j}%Yl~2A zN}NVW`dT=B^)b0(;F2@;(O2@b{H%j!pLng~RW?#BFi|Ku^1fP|IBXGc5S0@9FlU4z z_(xwtQ90@JhH!#?OR*TjkQF@z$C@5ly|gdW>7JXpySztvaG9`$2Gj(1bA>M~jdF3{ zb_+jio20n-e*L&!Z~S(l`z*=9v`5NGtMDhOIKv9YX}17~1Bg6n&2qR_cu5X_DX|!} z__Zenn|U_s7GG-D10keu6NZo6ePE@#av@==9a6BT;9ANYl7UsBCDx;(k-le2BY0aV zyuQ~Aujb5OjVsV^2}rwC;~P{5A4`YkJZ4~Bx$jQYN&+f@1*ihLV{i^Y`|s$J+b z!sEkrbYOT$TT}yta^Zt;bZ5!WDTYT6DlOIZ=dG@;FXsLC5#?O zNex3M+%{+7*wCVpgxJ=mDWIm#)HZo(Hh zrDfc!63i`E{d0yy_qRPCT^nM)n_y%Bj&<1Y^AU6Qwku9 zd`LNtAHB9zOelh1w!u?|#$cc$E-Mi(&QjJMV2onW1KoX1+;^bzJ3YnW&7p5Wt%vgm z1O{bq$V4DK4s6&d5BDSsgo$q?o+IDYlQ#KD<}AIzz*9hIHmLFA$ezFLv;C7m{mM4s zp9Jc!-~JtQ-|9ktOQ8PPX#A%@{a+XFGX2-y;SYfVaAc7${Yr>>zWS>I^;Z!b9UBYo zuNA>D{l^~R_ZI-aBvAkN8; zq&SjAiR5UaixG^-sBj1PLx~+vDtla?i1F}!OesBCh7s)$pTEjFNuGmTv04LuN2-nJ z@3XO8(OI!ds!sITiFwvGP5vHtH*!x{8hWa6?otUth#vup5lP-3*~E`uPaAmZd&r#q z(5=uf*Y5i4yU&5Cd3Ph$By2CCAFW zQR+B$)`r3TEtWp@X;e5mIHu(=$vV&!2wo43)Qi)@J1?I$uwflcqVLpcUT>|m)hFbT z21hJ`=w$B>y)Xa+vsbiBdE>i6T>VF#KRZ30y*?H)*Th_r;R})}nrG6_V7VbN>gcY) zy^9?xMg7${T%Yh~#=_B*H1* z>uvC6SW&z9)(AiR{6(s-b(tZ}Kc~WBL=%poZ5M&iUws`c_GbPj1q(hQNL6R9+cT4( z%zLzDo4N+eRv1$x-Nb`7i7GS-v7c6D-J3nw*r|nUa8j=ZgHY|^`_T15R*k$uJCuwo{n(dsey%6Y+)AXL3i5$XsO0ay?on5=CeCNtyGB@il;YvOSC&OHK5PoP}T^mC!aKom#^bDUx z>X46V#T-$EOo>9n1Gy7G$1nP##JFd|S?kBQvJ>uisRWjW^wq87Vf@R#@o)+c2cUEU zCeifHV_Aol3l(P9KnNz^_q~{@-kXMit%fCoTDCL;sV5C4sPUt;$1FW&g&M|yhr(n^ zSgVVO@O|qUTBmOHsp>>w)?7V=^fY6nWSQEV7V2zq6gn&Uzi{sS=FiHe!2AqxC`l` z!KJQ0{nh>fq$^*!I=n(HM_tFP}l9r{Y`@*doS~69}DxAYgUM4@bd}lry zc{i;X#Qd;Bj9R2fyeT8^Jw6`aDQ028Q&z@iIgi7R7gTo$iBf>grp0q(&I3lG-OO6h zLob&U>|UkzGcZYTHHL(uA@YO@_m_}wfS7A5Wb9(Qi=(!#g&}B1J#NvCUnn=Y>3|lO zbt4rSE0s)*s-#Zt?RG(8Zp^DQKpRiaNF#P6bux6V14a*X+>=)dG2w0Wb&PQwPnyj+ z4bXWU7QVJck#D&Y-coW~h&bb}*TB|$g8_k}Nsp)ND+X^X?)$wiIWSUYn^pCf?$OMm zv~A+;jGAi@lopi@JJVc!$zh+yrE#nt*e&wwl=xW$+V~! zP$&3A@+4~6vBmA%)I%^v`|Rb~b}6@C6*&w9i_i)-rcn?qM~OIwmm`&5bBqx|+6iYf zEPdFPYsDk+MfRvZI4m!X7Z2W@=kxMO&+SG@Zp$>Sj6oEWfaP}Zj1g0Cn=;{gMVtGU zSrGObC}vhl|Qkm)j-S-~g8t^CDHi#*=a-R_VC;ms)N zdk0s5#9@4wD+YF3y*R7(ZfzAQ#0b3;!d~8-&9Klfiz;YGV)D9sWISbeE z9@k)UUEagjRnR03bo$~87)`BcTu`oo$8jTV=j4`v!-Kwo(8bQV^KLQ$_N{QoEf_E0 z6-9$O2E)Bq>MTrRS1QD>Q#bEYV=U|!V?z+}dI-G8o~*lB9B2LU%Gh};r1AMyCCrQ8 z!7*Ca)9K|hLTF*fKR&v^cAI|p#v8~=rxFaT7`LjhhK|l7e7>?{{bIBMw0EY7^Fi}2 z^op2Y%#9OZxN5xZT8zxAVZS-3U{cfvXPz0VIm82RX9%vhdgHR-TXql{~;GAKl zNXHJ9LGDNm6a)3kc5Etd@2(TZTCXN@!6ip0lSP~O3No>1(tR<1?GqnPBwFrN_Lbd% z9&c!quvzeTn`wX-o6jc2|)MV83mhS0b=%h*bckbxZF`{ zsZB3q_zrB2nS<-XOjU?3nAx-+D%HZ4nUvmvdA-TddoFdhYe6#|3D>4|d;SkzMLJX? zS29`Y1Tdd;oXS-6Rf8EEja)Pi@XZihC=(7T)NmY4i7-@eN#y1g;5N>&>U_W~Y9HJT zb+sE!nbfv#M%Q(~7B!S$u~Tp>$-8yC2=|L8R$V^R9qtKLzW?rQ#Edh{Ob@Y8$rZP; ztDAfK_qO!6e({ghVTDL8 zk*4{m%7u>GGeh)+=uf9p__g31S?V3Nm`LH;O*wOI8IiQUs-Hf)0}~C7e#Guzez!i- zaaoqnUCQm?U3`0Jw|l&92WT;8LPL2WA9mpnZI+vW%huVMmE;om1yx66u3$_>tT&QI zI7DtPX_+F$&xt{~D2ebIp4U&PJW3u0K_QfsG?}shPyNQ4mW)rCGlEbxT#>VZJ_n%P zC3qgmz`=;$Rx5^qd{pFJx{~IFRMTwuvtNTHkqq4$-4*vj$a+^8*N`un+-sQVr(3x> zamlJxmS#Izl7(KY>oYxBP>~e${2hM53A<9giw2hnEYi@RlQ3jp+hUwPyJ=?)T)k65s74W#2L=aMo@eW-$`1*vBpcavMjK4YkY6?Vb40FPtMW; zo$zwiD*DW9-b2A?a;5`q{P^F|@3}K@i9QwqTnY&HP!v;*-4aii^;A>S(N%7jJRf-4 zwRJ4q);6YTJdU#Q+B5d0PMCU0f!*3J$1g5#r1jp3Sou)KQ7LD z#aijqw-B4Ir2%64^mJ}8Ov2J;ET2h)7Icsecupr2{?fw+rWeXm480|LmgH`34LSnR zYe~7i)x1iGL{BSAV&Aw=F;2JsNk+^ypwm};vMh;p=}oUEF_9VtY;!4 zkazcKc7LtF5bhix4HVU~MY1h;asj(CS_i#*-+(v-If6?&LA8^`vEojMMb?cP6zUT_mtn`tSWgz5x74rWuhj zUVMO#Q}Yy-Yg*KC)j^LqG4%^RZi_d8oUm{_EkzR{;hUOr4jk9>ms*0%`7w=s_nsF9 zsT(3%>p@o;7odCM)0r=5m!}hLr~FS9?*m%(mN9PRXCSK5y~t833>q^lmps{%YCJh= zm<)vPVXe-m>(Eecg1&oFN={osQF=b;O!n9Su^jWf>q5IhKYJ=iOaPA;-Hb19BdkZ-}}8)jo_#>+;@(YvAs~`2>WTtyHSQaIm&KtEw<6y$7Jay>a#3nET_k z%!`_T^!*N&s=2zcK>U$M4E`nAIiQ2xL>t=NL}BtgB(%Bl#h16Lx?rbJBD$J_*-`Z^ zzoRUY`|KMxp+fO+V_cRKpRP43#CYP)Wn>y~OOpPhmrLPKQ)vto#BCT;lMx(z>S$*M z0LfQOfJZ?=>_xJy_<^Hcz6rgG@uvH;HyhpCh0=fg{>Kp`9rGU>aXRKdw&-+!_GP1E{&R;<$Na~MJ00_% z2H$R`{%L^akMYp4{ArNoPlGIf8f5vyAT%AzAO6v?{9%!fz@``{~RLgp9Wd~G|2j=LDoME{^u#fUyt_pk3oOf`qy#fAEyi%1`P(Q z{5?Y@kv15=dd~3nA{pKu0R3A(J%+b&{}|i<+#~Gg`}!Lgq+Ijkk^mlo*KGX|-|vv= z8U7%@(SL#bXHh^Dn`Nt>rH}Hb7OAruo=~&u85Zv#8-#*_TVEVUt`U~J6pY-3r zR*kyR#-VEXhhQG#zXPU!>-qKr{MIG$7r_5;dX`{ysA-LI65LARy(#cJWIEPA$n>ng zLjExq|K@UIgecz#do%0yT|v?BfNB2(zYX9QT>c>we*+7J*MXM1YFanR_~9x12KmQv z;m?On%knGa|5E=yL<5Yk}_zW zQ2K&EDLR+lnFVkh$<3VKA=AJ8bM=Ev|0c-4;Pj7}^*6Ff)f#Zlo>e4ir zPx9MezrR5K2MJT$3>{0#Je^Vva@l>VLGKF1J{me22@ z-=^|U^e<`2&$Y$h&{mc#*ROIUdmABll)vTlo0$A0|B{&eT+IB9e1k9nXz7VHBNbAs z{yXF!V)B#xOJeeK`Sdq35qkmMc~ICO(V~0wuaX(*-)8cg8%%FqP=8HKelFPlMxHXm z9RQhH&yr%KDEVdbk9YjG&iqM!8^JGp-2WhBuFcVulmK_q?D5#azfAt&>)RhkKgn++ z_}|Gt7o~smIq0AzV_pbBPaeS^{nyEF(*2g7_mliKf?pv2!!iu~rb$vF`oXb_VE_8J z$Z!8q|0KVS;1|gMAYy`FgHeZ+K9Nt22W9*^`7K_&#fqQgw-NjT`5#2=?2e8BRdqln zYtgvRqWu=Fev*GFTK(Kx{Ovu5uiSM=2p>$l)U!SQGMV9RR=owQpX9d@{DRN_ zuoAm)+!|bk728c1H~Wom`^Td5Px3FVdVlVp{^m1_C6>L>b_qSen$;@_Nxue_f?`Xcq2 z8TdKnx5#fH<0tu-LdMT+>EFnL$K=fHv;|?xv3~czN@k>G!u=647~eL|^uH7`ex4%y zi~Rp2r{@3uFYBM@8GjD$uf#Sw*0-tizb4hB)4-*C3Jklh!6To-LQNUHTiL5D*t(yV zufUA(-^57&gmR@0LqlL^R%;I$f zEx@u`wt!(u>28sf?rxFp2I=nZ77>&XP`XRHL%O@Aq@=r3rKI`ajdHG@>%I4!``zb% zzVG1~CicvlHM7^6SuyXPa}n_7-C>wzm1xMA1$4`nW4~iMc^$fl`-dMJIY_n1=(eDR z2C-k$&vixV$KojMQ+^C!7f`@0QnEaUr<>*>4LUdDYt8fmCrB4d#8fEX6LbOVGroq~ zWo}2G^W(p7$|944(U!e4gR5R5n(2-VRD2nAG2@G*NTMwo)W7p0B3jq_|L~b?iRnd*W$Hi0~v^#xuKetnkQC z!!h1w7$azcDTC9V6^{SKxxBtBqw9NJTT?m8h+TvNxnPTcvUG(RLu6!0MPrKaSl#DQ z)M4+^VIFU*L?OBHV>5TBy-ZH7!O%^FxL&4kFf16y(m} zUiCoDsI2X_8hMa1O^|4iYL_BMB<7s=@meWopa{P3NRweoG*gDJ9W_r(Rr3pu{uqE# zy! zmbU0MSh7$Ae=-b#S zne7>c$&~X1LEgvMer4VsC_*7iOwM3dSTS|uat>sUsLylut$q473QVT2g39>0Mv{~6 z2lMC_P>RV11;s~#Q^*NfiA6=CL76hW@g4oRT_*PYrI`nK(YrwS8pc>bQrz0@(Jxx( zMkiU%)(NY)Yt#GfF&zYVlvrkGH~X2{%V+Iacr-rrmFz2b+a%jIA~wOGWS3mX4V0k{ z(YVhZj~l+^{$l55&s-+Nd8q0|Y%D>_KRKGsfwpew)GUIIGcv%N`Ksbb6JOjN8-D}J zd2(b)N4Q3a2?9Z;2hj!HM;o5RgGgeeYYGK9&yw=kZ2QBMHh5Hy zhuv#JR~;roCJ~$-gEZIFC2F{WIeL=}){=2)xtM)_79x>Kem0wu`z$D*mALeR5%blE zz|+nn{318JV@?emtty$`nc#C&ZF%fCX11=Fx^nu@$YGBK_N-V~U8bF_F)MtNp> zEYP;Lf`2*{{mq%8ZUMT1ZA7R5R}oC*>GtL263Q0Ww|S*QTXo-v!@*4c6>#rvvYDoD zG-?lCZt_fsB{)6~J!3Vd*!DO`<~>84n4o3#w?wppD_)hU$UtQ|!>7o~8F1Sf*PzTj z=s8Ytpko?t&amg+)l9(0RB#iS4@~jfZSMq)oAjQ_{)2Y@E>SuRXi?inYVV$Iz>(`!I!u z`wXCF2DG2Z*M20oeKNY~CfpWMIkYV$NZ0c*FCknA^qm&7*_X-^#HP4m=6n`6ZQlgl zz^R)=gwz+d`TK`E(L!e(CZ%c!oRD);*Csf~DAFFi`y3i#Rx}f2xx!m5F;p64eiTsP zffwtsBcVk5Eq|dSU$h4-U-Dt8lVgmrR1d&gKCtl@mi(SB35GpJu#URlqA~3 zxGl2d`?A2JH@nV#;nJ@Y%$_HGXy;`tVJKoKlP+CTY(BSU|LWGm?aj6Q>dOsU!6T|- zRjA7_yWcn;KqA>sMv1@dd|(Eh*98pmKYjF`86W6)hnf1%^wXc|_Q1<>aAq@ErahUxx|j@dvKO`u~okWJt( zbo?8e@3X0$gs(!zvnB}Oe_6Q#y<|2}xa}C^dix6<|Hf9r+?>49&y>CpSxk}jH%80~ z;?vuXK|g`}j~#z!+CPzXGl{}mW^Ye-0VaUHYtp2Vl`TJjN{>@)$e&2+C4E|1I ztY|}|OUQP=} z!h=t0XM>?1D(HtiE5O}6H53cid{QF$nI-v|W(DoCy7#`(6cqiUZvD@?u1&Z9+5T@pO#|2zE$QNY`OH$5m}y3o+$JhNaBs%R1-Q=?KI%wtEROsw|Z5A z5z_X}L`rQaM--l3z3A&9;;fq9bi?x-v&_-PV+f-R)W;}}h9mFAr`5OPXfp`c&_-%5 z1a3Uw!v%YsIQ2UiHDf=8N*?4H#_@6&xRNr$b)CG#uOBoH+Fd~n)rlT5JtgMa0Nfy z)5#xWMGx|KENPgHdhH`z{n9;wG6jQ_7DlO(NVO5Ej&_2BoXUdPbCI7+T17=J2G?}& zy&73-DfbZ>mhKrZe`PR4doD)l7hKcQ(DDQdNourAv;f{5=_U;rlxj*%-yRD0thCI+ z`t#}hO5}X?uXYUnFyYNk$1Vtjk|~tp{Ng7l1^fHB36jE@U!7tqMQ!!kYVQdYwoLOe$UrJ@VANhWTK zC2!7TirVcrb3m`^U6@Xt5wKNoR10R0$Sff&7ns^k>oS{u64mm&`7^UeXj$Tx>Fg4i z%u$UQ=}=rmGW2YUsThq2?NGGwhQ;cq>j8s-nl~#0%&r~x$~dIQCU>)TZOQ6`LwXfO zWmN+=Hxu&*8YxfhnG_~D+KL*uE=mmZwyBYM4Wgx$pj`)TU%!Y}eUGN+=9JrMvzpAG zRaS>NS26)UH=rMo)D@u+mhP4~Dm>9~*j#Tl*VQj8!HCN=yk zwHY~=cA?;c_z>>4A$$1Vof>Csv@dFs$6o1V5Au@T z;7R_^V(a&t1OHg#zKgB5_Y5L3C^;KA-QGC|lrn*YK~ZxDM<*dOeFu=i8I+aQ|B(Y3 zl0aEib0a4+N6>9-%peyA5N>?{pdWIufdVJUF2@c!<-!CqM6rN`n%e*>V*{ClZ|eer z&X0CLeMI2je+qvz00992-3CxQCPvT|Mu4F0h&k<4xs;tK;obO{!!t3>N}1AsJlEP5f=#94*++Czzg~ZOa22a z$L%3AAP-=Bmjm9&4*&qNe~+SN3_4*gNA(j z&H!&OXcRzTVg&sI2>R}=|M5}*&F)8oJ7WGzgbYBvebE4QfPnlFK;?*Bp!?&1Hx4Yw zf6(2Jh52>?2WSu2Xh4;LJW%fs8U^JMZ;2O_xFs8an12FvJC>iJ0%+Wgiw$tb0Vw=2 zKv3&jYW@-bT>eKR&@&+Y$5Iaf0C)%pwUd9IT+F{$F5ui@~k#|5F8EPW~Qa$jSd;3_)8fqu=laXu<%? z*3b76GBf_7%Kufq03@D21RxL%{v`jf19p@D!+`kT0QhCDm~Zdh1q_S7?1rL(0yNUj zhUP~4O8PdAz-V0^%$@qneVO^{&``!y#n`tEJA{)0N?^v z&xj-G?E)Br)aJMAf zrhnS&ZZQOH3&3-LRsjruuKRtH1U0xF0;mGO%m9$via1kuu0zW>HltJ zxP$n2PAfzgMjQItO;MpEle-_yR-+cjOEV@oqH*%5I1LgKqvHs@qCGCic(i zKLGt{zvTqb1BmA?+>-QKiTL1u>KmaNLTnGRGumCFD z@dXPjh`~T>0TAC!C@`7-FYv{`*=}!#@bh?p0=6Hg1piQ^{v%&V2>rC({%hX&jba5l zVfu0@f!`_!()yD)fED`kj5~r^WED(Xa4<9Ps2nh5wD2=RfrVJpWf1fu`}_ zHUIVLFZ}x-7b$6R8UY7$eXHN>+92ZkXEOc8ef!_;+JB2o|M{i;x#W(@Zpq<}s(!8n z?3mwa?~cxXt#wQ1-wE{RW&pi?&jTr7YyJ5t2ownHchDdJ-vy7KtNo+-_h$d@75@K6 zV|Q2pINhaz_zw(zE&jO*kpA`Q-=Z~15wY*IM)$AQ%HN5Jzj*EcL2y6WCJ`9~91V?a zoQOC;XJA1mMfB~&jm=HX?(#rQ;3?>OE|A%pLDWj$)R73})ctPb6cn^|16k$im_VW& z9W&_n2uw^MQzsY8_g~Hd)=XJ_Yh$8+XDa=^wF}soS{Z}j0JeW6Cu3_Bkh}&;s{O!) z4dfgI;RbRKC>c8uQ8G&>;%ja|?*w9v=GbNi)4hK7aJoYhx9mzqQM^|kUA{lvx&GWx zuuM2e-7fHwq6qz@R~OCH0v~n_cPtJ?bIK_CDSBpBKeHBM=1CZ1J6SIutW2z3Pz?4LX!LPUUc@95-UtZ$77N6>#RqC@%dYp&wc>vBVF2wS=j_g~w4%oY1CB<^k5 z&~J@M7?%yTH7J_JAS)ts(G0yt(@a?i{2;`V&1dIYU< z>7~ud0HNUHFR_9io05}eO=mMiLtI;kXZ`Gjke=Db2K}%|@vU)41)EVlJYSJu#H{!n zV4&72@jZ(S4y`e{${vAXtC*X0pnm;klSI9XHBi0J^xS!FN+>eiNPv`D7qR^0dOx{p zzXpRUPJoLa6;94gcH3q}Y~V>mx6kL+nrHgpc|~Qr(a95uxKUh)cRysYXLozQ5yWDheYx5l;Z|oh>EPgaIp`5nW7PoS!pvW_h`yX$t)S) zgg8AAwe$`B zgMM?uOD0S~6F1r)BmQJDo2cZ*Iq9UF0#n@9>`}mQML@1mVt+bqa^bDg? zg4L55Umjt(bT<)@@Gbfue&NbRnQX81H|nRwZ0f3r56T;_n<({kSbz7Js<5~!{D}{q zVkvSjqXC$qNH!NEcxq%)o@yZfV;ylUfy)omy|GjG-f zF2}xBab?l493;>-sxgBrnMpxMu;u7--kcW+ik~dztnY0W?gUwKxWTf76a

P1nd@ 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∓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%0DOYWTgQBPsvq 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∓9n;kz-n(;GL` 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

-LmrEvb(&2U<_@;Isa^Y`uEB?SS^9Yt9?!2G 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 jH~&e_4_7$v`U01&maA~m5=QE)7r*$$uYUK> (PORTS - masked_request_index); - end else begin - mask_next = {PORTS{1'b1}} << (masked_request_index + 1); - end - end else begin - grant_valid_next = 1; - grant_next = request_mask; - grant_encoded_next = request_index; - if (LSB_PRIORITY == "LOW") begin - mask_next = {PORTS{1'b1}} >> (PORTS - request_index); - end else begin - mask_next = {PORTS{1'b1}} << (request_index + 1); - end - end - end - end - end - - always @(posedge clk) begin - if (rst) begin - grant_reg <= 0; - grant_valid_reg <= 0; - grant_encoded_reg <= 0; - mask_reg <= 0; - end else begin - grant_reg <= grant_next; - grant_valid_reg <= grant_valid_next; - grant_encoded_reg <= grant_encoded_next; - mask_reg <= mask_next; - end - end - -endmodule diff --git a/lib/hardware/buses/arbiter/hardware/src/priority_encoder.v b/lib/hardware/buses/arbiter/hardware/src/priority_encoder.v deleted file mode 100644 index fa9ec0416..000000000 --- a/lib/hardware/buses/arbiter/hardware/src/priority_encoder.v +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: 2014-2018 Alex Forencich -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -// Language: Verilog 2001 - -`timescale 1ns / 1ps - -/* - * Priority encoder module - */ -module priority_encoder #( - parameter WIDTH = 4, - // LSB priority: "LOW", "HIGH" - parameter LSB_PRIORITY = "LOW" -) ( - input wire [ WIDTH-1:0] input_unencoded, - output wire output_valid, - output wire [$clog2(WIDTH)-1:0] output_encoded, - output wire [ WIDTH-1:0] output_unencoded -); - - // power-of-two width - parameter W1 = 2 ** $clog2(WIDTH); - parameter W2 = W1 / 2; - - generate - if (WIDTH == 1) begin : g_width_1 - // one input - assign output_valid = input_unencoded; - assign output_encoded = 0; - end else if (WIDTH == 2) begin : g_width_2 - // two inputs - just an OR gate - assign output_valid = |input_unencoded; - if (LSB_PRIORITY == "LOW") begin : g_width_2_lsb_priority_low - assign output_encoded = input_unencoded[1]; - end else begin : g_width_2_lsb_priority_high - assign output_encoded = ~input_unencoded[0]; - end - end else begin : g_width_other - // more than two inputs - split into two parts and recurse - // also pad input to correct power-of-two width - wire [$clog2(W2)-1:0] out1, out2; - wire valid1, valid2; - wire [W2-1:0] in2; - assign in2[WIDTH-W2-1:0] = input_unencoded[WIDTH-1:W2]; - if (WIDTH - W2 < W2) assign in2[W2-1:WIDTH-W2] = 0; - priority_encoder #( - .WIDTH (W2), - .LSB_PRIORITY(LSB_PRIORITY) - ) priority_encoder_inst1 ( - .input_unencoded (input_unencoded[W2-1:0]), - .output_valid (valid1), - .output_encoded (out1), - .output_unencoded() - ); - priority_encoder #( - .WIDTH (W2), - .LSB_PRIORITY(LSB_PRIORITY) - ) priority_encoder_inst2 ( - .input_unencoded (in2), - .output_valid (valid2), - .output_encoded (out2), - .output_unencoded() - ); - // multiplexer to select part - assign output_valid = valid1 | valid2; - if (LSB_PRIORITY == "LOW") begin : g_width_other_lsb_priority_low - assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1}; - end else begin : g_width_other_lsb_priority_high - assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2}; - end - end - endgenerate - - // unencoded output - assign output_unencoded = 1 << output_encoded; - -endmodule diff --git a/lib/hardware/buses/axi2axil/axi2axil.py b/lib/hardware/buses/axi2axil/axi2axil.py deleted file mode 100644 index b90db1a5e..000000000 --- a/lib/hardware/buses/axi2axil/axi2axil.py +++ /dev/null @@ -1,127 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - """AXI to AXI-Lite converter - This converter has the same limitations as AXI-Lite: - - No Burst Support: burst-related signals (like AWLEN, AWSIZE, ARBURST, etc.) are ignored. - """ - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "AXI_ID_W", - "descr": "AXI ID bus width", - "type": "P", - "val": "4", - "min": "1", - "max": "32", - }, - { - "name": "AXI_LEN_W", - "descr": "AXI burst length width", - "type": "P", - "val": "4", - "min": "1", - "max": "4", - }, - { - "name": "AXI_ADDR_W", - "descr": "AXI address bus width", - "type": "P", - "val": "32", - "min": "1", - "max": "32", - }, - { - "name": "AXI_DATA_W", - "descr": "AXI data bus width", - "type": "P", - "val": "32", - "min": "1", - "max": "32", - }, - ], - "ports": [ - { - "name": "axi_s", - "descr": "AXI slave interface to connect to external master", - "interface": { - "type": "axi", - "subtype": "slave", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - { - "name": "axil_m", - "descr": "AXI Lite master interface to connect to external slave", - "interface": { - "type": "axil", - "subtype": "master", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - // Write Address Channel - assign axil_awaddr_o = axi_awaddr_i; - assign axil_awprot_o = axi_awprot_i; - assign axil_awvalid_o = axi_awvalid_i; - assign axi_awready_o = axil_awready_i; - // Write Data Channel - assign axil_wdata_o = axi_wdata_i; - assign axil_wstrb_o = axi_wstrb_i; - assign axil_wvalid_o = axi_wvalid_i; - assign axi_wready_o = axil_wready_i; - // Write Response Channel - assign axi_bresp_o = axil_bresp_i; - assign axi_bvalid_o = axil_bvalid_i; - assign axil_bready_o = axi_bready_i; - // Read Address Channel - assign axil_araddr_o = axi_araddr_i; - assign axil_arprot_o = axi_arprot_i; - assign axil_arvalid_o = axi_arvalid_i; - assign axi_arready_o = axil_arready_i; - // Read Data Channel - assign axi_rdata_o = axil_rdata_i; - assign axi_rresp_o = axil_rresp_i; - assign axi_rvalid_o = axil_rvalid_i; - assign axil_rready_o = axi_rready_i; - - // Unused axi outputs - assign axi_bid_o = 1'b0; - assign axi_rid_o = 1'b0; - assign axi_rlast_o = 1'b1; - - // Unused axi inputs - // axi_awid_i - // axi_awlen_i - // axi_awsize_i - // axi_awburst_i - // axi_awlock_i - // axi_awcache_i - // axi_awqos_i - // axi_wlast_i - // axi_arid_i - // axi_arlen_i - // axi_arsize_i - // axi_arburst_i - // axi_arlock_i - // axi_arcache_i - // axi_arqos_i -""" - } - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/axi2iob/axi2iob.py b/lib/hardware/buses/axi2iob/axi2iob.py deleted file mode 100644 index 31036d3da..000000000 --- a/lib/hardware/buses/axi2iob/axi2iob.py +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "confs": [ - { - "name": "ADDR_WIDTH", - "descr": "", - "type": "P", - "val": "32", - "min": "1", - "max": "32", - }, - { - "name": "DATA_WIDTH", - "descr": "", - "type": "P", - "val": "32", - "min": "1", - "max": "32", - }, - { - "name": "STRB_WIDTH", - "descr": "", - "type": "P", - "val": "(DATA_WIDTH / 8)", - "min": "1", - "max": "32", - }, - { - "name": "AXI_ID_WIDTH", - "descr": "", - "type": "P", - "val": "8", - "min": "1", - "max": "32", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "axi_s", - "descr": "Slave AXI interface", - "interface": { - "type": "axi", - "subtype": "slave", - "port_prefix": "s_", - "ADDR_W": "ADDR_WIDTH", - "DATA_W": "DATA_WIDTH", - }, - }, - { - "name": "iob_m", - "descr": "Master IOb interface", - "interface": { - "type": "iob", - "subtype": "master", - "ADDR_W": "ADDR_WIDTH", - "DATA_W": "DATA_WIDTH", - }, - }, - ], - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/axi2iob/hardware/src/axi2iob.v b/lib/hardware/buses/axi2iob/hardware/src/axi2iob.v deleted file mode 100644 index e76d5d246..000000000 --- a/lib/hardware/buses/axi2iob/hardware/src/axi2iob.v +++ /dev/null @@ -1,551 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -// Language: Verilog 2001 -`timescale 1ns / 1ps - -/* - * AXI4 to IOb adapter - */ -module axi2iob #( - // Width of address bus in bits - parameter ADDR_WIDTH = 32, - // Width of input (slave/master) AXI/IOb interface data bus in bits - parameter DATA_WIDTH = 32, - // Width of input (slave/master) AXI/IOb interface wstrb (width of data bus in words) - parameter STRB_WIDTH = (DATA_WIDTH / 8), - // Width of AXI ID signal - parameter AXI_ID_WIDTH = 8 -) ( - input wire clk_i, - input wire cke_i, - input wire arst_i, - - /* - * AXI slave interface - */ - input wire [AXI_ID_WIDTH-1:0] s_axi_awid_i, - input wire [ ADDR_WIDTH-1:0] s_axi_awaddr_i, - input wire [ 7:0] s_axi_awlen_i, - input wire [ 2:0] s_axi_awsize_i, - input wire [ 1:0] s_axi_awburst_i, - input wire s_axi_awlock_i, - input wire [ 3:0] s_axi_awcache_i, - input wire [ 3:0] s_axi_awqos_i, - input wire [ 2:0] s_axi_awprot_i, - input wire s_axi_awvalid_i, - output wire s_axi_awready_o, - input wire [ DATA_WIDTH-1:0] s_axi_wdata_i, - input wire [ STRB_WIDTH-1:0] s_axi_wstrb_i, - input wire s_axi_wlast_i, - input wire s_axi_wvalid_i, - output wire s_axi_wready_o, - output wire [AXI_ID_WIDTH-1:0] s_axi_bid_o, - output wire [ 1:0] s_axi_bresp_o, - output wire s_axi_bvalid_o, - input wire s_axi_bready_i, - input wire [AXI_ID_WIDTH-1:0] s_axi_arid_i, - input wire [ ADDR_WIDTH-1:0] s_axi_araddr_i, - input wire [ 7:0] s_axi_arlen_i, - input wire [ 2:0] s_axi_arsize_i, - input wire [ 1:0] s_axi_arburst_i, - input wire s_axi_arlock_i, - input wire [ 3:0] s_axi_arcache_i, - input wire [ 3:0] s_axi_arqos_i, - input wire [ 2:0] s_axi_arprot_i, - input wire s_axi_arvalid_i, - output wire s_axi_arready_o, - output wire [AXI_ID_WIDTH-1:0] s_axi_rid_o, - output wire [ DATA_WIDTH-1:0] s_axi_rdata_o, - output wire [ 1:0] s_axi_rresp_o, - output wire s_axi_rlast_o, - output wire s_axi_rvalid_o, - input wire s_axi_rready_i, - - /* - * IOb-bus master interface - */ - output wire iob_valid_o, - output wire [ADDR_WIDTH-1:0] iob_addr_o, - output wire [DATA_WIDTH-1:0] iob_wdata_o, - output wire [STRB_WIDTH-1:0] iob_wstrb_o, - input wire iob_rvalid_i, - input wire [DATA_WIDTH-1:0] iob_rdata_i, - input wire iob_ready_i -); - - localparam [1:0] STATE_IDLE = 2'd0, STATE_DATA = 2'd1, STATE_RESP = 2'd2; - - /* - * AXI lite master interface (used as a middle ground from AXI4 to IOb) - */ - wire [ADDR_WIDTH-1:0] m_axil_awaddr; - wire [ 2:0] m_axil_awprot; - wire m_axil_awvalid; - wire m_axil_awready; - wire [DATA_WIDTH-1:0] m_axil_wdata; - wire [STRB_WIDTH-1:0] m_axil_wstrb; - wire m_axil_wvalid; - wire m_axil_wready; - wire [ 1:0] m_axil_bresp; - wire m_axil_bvalid; - wire m_axil_bready; - wire [ADDR_WIDTH-1:0] m_axil_araddr; - wire [ 2:0] m_axil_arprot; - wire m_axil_arvalid; - wire m_axil_arready; - wire [DATA_WIDTH-1:0] m_axil_rdata; - wire [ 1:0] m_axil_rresp; - wire m_axil_rvalid; - wire m_axil_rready; - - // - // AXI4-Lite interface to IOb - // - wire iob_rvalid_q; - wire [DATA_WIDTH-1:0] iob_rdata_q; - wire iob_rvalid_e; - wire write_enable; - wire [ADDR_WIDTH-1:0] m_axil_awaddr_q; - wire m_axil_bvalid_n; - wire m_axil_bvalid_e; - - assign write_enable = |m_axil_wstrb; - assign m_axil_bvalid_n = m_axil_wvalid; - assign m_axil_bvalid_e = m_axil_bvalid_n | m_axil_bready; - assign iob_rvalid_e = iob_rvalid_i | m_axil_rready; - - // COMPUTE AXIL OUTPUTS - // // write address - assign m_axil_awready = iob_ready_i; - // // write - assign m_axil_wready = iob_ready_i; - // // write response - assign m_axil_bresp = 2'b0; - // // read address - assign m_axil_arready = iob_ready_i; - // // read - assign m_axil_rdata = iob_rvalid_i ? iob_rdata_i : iob_rdata_q; - assign m_axil_rresp = 2'b0; - assign m_axil_rvalid = iob_rvalid_i ? 1'b1 : iob_rvalid_q; - - // COMPUTE IOb OUTPUTS - assign iob_valid_o = (m_axil_bvalid_n & write_enable) | m_axil_arvalid; - assign iob_addr_o = m_axil_arvalid ? m_axil_araddr : (m_axil_awvalid ? m_axil_awaddr : m_axil_awaddr_q); - assign iob_wdata_o = m_axil_wdata; - assign iob_wstrb_o = m_axil_arvalid ? {STRB_WIDTH{1'b0}} : m_axil_wstrb; - - iob_reg_re #( - .DATA_W (ADDR_WIDTH), - .RST_VAL(0) - ) iob_reg_awaddr ( - .clk_i (clk_i), - .arst_i(arst_i), - .cke_i (cke_i), - .rst_i (1'b0), - .en_i (m_axil_awvalid), - .data_i(m_axil_awaddr), - .data_o(m_axil_awaddr_q) - ); - - iob_reg_re #( - .DATA_W (1), - .RST_VAL(0) - ) iob_reg_rvalid ( - .clk_i (clk_i), - .arst_i(arst_i), - .cke_i (cke_i), - .rst_i (1'b0), - .en_i (iob_rvalid_e), - .data_i(iob_rvalid_i), - .data_o(iob_rvalid_q) - ); - - iob_reg_re #( - .DATA_W (DATA_WIDTH), - .RST_VAL(0) - ) iob_reg_rdata ( - .clk_i (clk_i), - .arst_i(arst_i), - .cke_i (cke_i), - .rst_i (1'b0), - .en_i (iob_rvalid_e), - .data_i(iob_rdata_i), - .data_o(iob_rdata_q) - ); - - iob_reg_re #( - .DATA_W (1), - .RST_VAL(0) - ) iob_reg_bvalid ( - .clk_i (clk_i), - .arst_i(arst_i), - .cke_i (cke_i), - .rst_i (1'b0), - .en_i (m_axil_bvalid_e), - .data_i(m_axil_bvalid_n), - .data_o(m_axil_bvalid) - ); - - // - // AXI4 write interface conversion to AXI4-Lite - // - reg [1:0] w_state_reg, w_state_next; - - reg [AXI_ID_WIDTH-1:0] w_id_reg, w_id_next; - reg [ADDR_WIDTH-1:0] w_addr_reg, w_addr_next; - reg [DATA_WIDTH-1:0] w_data_reg, w_data_next; - reg [STRB_WIDTH-1:0] w_strb_reg, w_strb_next; - reg [7:0] w_burst_reg, w_burst_next; - reg [2:0] w_burst_size_reg, w_burst_size_next; - reg [2:0] w_master_burst_size_reg, w_master_burst_size_next; - reg w_burst_active_reg, w_burst_active_next; - reg w_first_transfer_reg, w_first_transfer_next; - reg w_last_segment_reg, w_last_segment_next; - - reg s_axi_awready_reg, s_axi_awready_next; - reg s_axi_wready_reg, s_axi_wready_next; - reg [AXI_ID_WIDTH-1:0] s_axi_bid_reg, s_axi_bid_next; - reg [1:0] s_axi_bresp_reg, s_axi_bresp_next; - reg s_axi_bvalid_reg, s_axi_bvalid_next; - - reg [ADDR_WIDTH-1:0] m_axil_awaddr_reg, m_axil_awaddr_next; - reg [2:0] m_axil_awprot_reg, m_axil_awprot_next; - reg m_axil_awvalid_reg, m_axil_awvalid_next; - reg [DATA_WIDTH-1:0] m_axil_wdata_reg, m_axil_wdata_next; - reg [STRB_WIDTH-1:0] m_axil_wstrb_reg, m_axil_wstrb_next; - reg m_axil_wvalid_reg, m_axil_wvalid_next; - reg m_axil_bready_reg, m_axil_bready_next; - - assign s_axi_awready_o = s_axi_awready_reg; - assign s_axi_wready_o = s_axi_wready_reg; - assign s_axi_bid_o = s_axi_bid_reg; - assign s_axi_bresp_o = s_axi_bresp_reg; - assign s_axi_bvalid_o = s_axi_bvalid_reg; - - assign m_axil_awaddr = m_axil_awaddr_reg; - assign m_axil_awprot = m_axil_awprot_reg; - assign m_axil_awvalid = m_axil_awvalid_reg; - assign m_axil_wdata = m_axil_wdata_reg; - assign m_axil_wstrb = m_axil_wstrb_reg; - assign m_axil_wvalid = m_axil_wvalid_reg; - assign m_axil_bready = m_axil_bready_reg; - - integer i; - - always @* begin - w_state_next = STATE_IDLE; - - w_id_next = w_id_reg; - w_addr_next = w_addr_reg; - w_data_next = w_data_reg; - w_strb_next = w_strb_reg; - w_burst_next = w_burst_reg; - w_burst_size_next = w_burst_size_reg; - w_master_burst_size_next = w_master_burst_size_reg; - w_burst_active_next = w_burst_active_reg; - w_first_transfer_next = w_first_transfer_reg; - w_last_segment_next = w_last_segment_reg; - - s_axi_awready_next = 1'b0; - s_axi_wready_next = 1'b0; - s_axi_bid_next = s_axi_bid_reg; - s_axi_bresp_next = s_axi_bresp_reg; - s_axi_bvalid_next = s_axi_bvalid_reg & ~s_axi_bready_i; - m_axil_awaddr_next = m_axil_awaddr_reg; - m_axil_awprot_next = m_axil_awprot_reg; - m_axil_awvalid_next = m_axil_awvalid_reg & ~m_axil_awready; - m_axil_wdata_next = m_axil_wdata_reg; - m_axil_wstrb_next = m_axil_wstrb_reg; - m_axil_wvalid_next = m_axil_wvalid_reg & ~m_axil_wready; - m_axil_bready_next = 1'b0; - - case (w_state_reg) - default: begin // STATE_IDLE - // idle state; wait for new burst - s_axi_awready_next = ~m_axil_awvalid; - w_first_transfer_next = 1'b1; - - if (s_axi_awready_o & s_axi_awvalid_i) begin - s_axi_awready_next = 1'b0; - w_id_next = s_axi_awid_i; - m_axil_awaddr_next = s_axi_awaddr_i; - w_addr_next = s_axi_awaddr_i; - w_burst_next = s_axi_awlen_i; - w_burst_size_next = s_axi_awsize_i; - w_burst_active_next = 1'b1; - m_axil_awprot_next = s_axi_awprot_i; - m_axil_awvalid_next = 1'b1; - s_axi_wready_next = ~m_axil_wvalid; - w_state_next = STATE_DATA; - end else begin - w_state_next = STATE_IDLE; - end - end - STATE_DATA: begin - // data state; transfer write data - s_axi_wready_next = ~m_axil_wvalid; - - if (s_axi_wready_o & s_axi_wvalid_i) begin - m_axil_wdata_next = s_axi_wdata_i; - m_axil_wstrb_next = s_axi_wstrb_i; - m_axil_wvalid_next = 1'b1; - w_burst_next = w_burst_reg - 1; - w_burst_active_next = w_burst_reg != 0; - w_addr_next = w_addr_reg + (1 << w_burst_size_reg); - s_axi_wready_next = 1'b0; - m_axil_bready_next = ~s_axi_bvalid_o & ~m_axil_awvalid; - w_state_next = STATE_RESP; - end else begin - w_state_next = STATE_DATA; - end - end - STATE_RESP: begin - // resp state; transfer write response - m_axil_bready_next = ~s_axi_bvalid_o & ~m_axil_awvalid; - - if (m_axil_bready & m_axil_bvalid) begin - m_axil_bready_next = 1'b0; - s_axi_bid_next = w_id_reg; - w_first_transfer_next = 1'b0; - if (w_first_transfer_reg | (m_axil_bresp != 0)) begin - s_axi_bresp_next = m_axil_bresp; - end - if (w_burst_active_reg) begin - // burst on slave interface still active; start new AXI lite write - m_axil_awaddr_next = w_addr_reg; - m_axil_awvalid_next = 1'b1; - s_axi_wready_next = ~m_axil_wvalid; - w_state_next = STATE_DATA; - end else begin - // burst on slave interface finished; return to idle - s_axi_bvalid_next = 1'b1; - s_axi_awready_next = ~m_axil_awvalid; - w_state_next = STATE_IDLE; - end - end else begin - w_state_next = STATE_RESP; - end - end - endcase - end - - always @(posedge clk_i, posedge arst_i) begin - if (arst_i) begin - w_state_reg <= STATE_IDLE; - s_axi_awready_reg <= 1'b0; - s_axi_wready_reg <= 1'b0; - s_axi_bvalid_reg <= 1'b0; - m_axil_awvalid_reg <= 1'b0; - m_axil_wvalid_reg <= 1'b0; - m_axil_bready_reg <= 1'b0; - - w_id_reg <= {AXI_ID_WIDTH{1'b0}}; - w_addr_reg <= {ADDR_WIDTH{1'b0}}; - w_data_reg <= {DATA_WIDTH{1'b0}}; - w_strb_reg <= {STRB_WIDTH{1'b0}}; - w_burst_reg <= 8'd0; - w_burst_size_reg <= 3'd0; - w_master_burst_size_reg <= 3'd0; - w_burst_active_reg <= 1'b0; - w_first_transfer_reg <= 1'b0; - w_last_segment_reg <= 1'b0; - - s_axi_bid_reg <= {AXI_ID_WIDTH{1'b0}}; - s_axi_bresp_reg <= 2'd0; - m_axil_awaddr_reg <= {ADDR_WIDTH{1'b0}}; - m_axil_awprot_reg <= 3'd0; - m_axil_wdata_reg <= {DATA_WIDTH{1'b0}}; - m_axil_wstrb_reg <= {STRB_WIDTH{1'b0}}; - end else begin - w_state_reg <= w_state_next; - s_axi_awready_reg <= s_axi_awready_next; - s_axi_wready_reg <= s_axi_wready_next; - s_axi_bvalid_reg <= s_axi_bvalid_next; - m_axil_awvalid_reg <= m_axil_awvalid_next; - m_axil_wvalid_reg <= m_axil_wvalid_next; - m_axil_bready_reg <= m_axil_bready_next; - - w_id_reg <= w_id_next; - w_addr_reg <= w_addr_next; - w_data_reg <= w_data_next; - w_strb_reg <= w_strb_next; - w_burst_reg <= w_burst_next; - w_burst_size_reg <= w_burst_size_next; - w_master_burst_size_reg <= w_master_burst_size_next; - w_burst_active_reg <= w_burst_active_next; - w_first_transfer_reg <= w_first_transfer_next; - w_last_segment_reg <= w_last_segment_next; - - s_axi_bid_reg <= s_axi_bid_next; - s_axi_bresp_reg <= s_axi_bresp_next; - m_axil_awaddr_reg <= m_axil_awaddr_next; - m_axil_awprot_reg <= m_axil_awprot_next; - m_axil_wdata_reg <= m_axil_wdata_next; - m_axil_wstrb_reg <= m_axil_wstrb_next; - end - end - - // - // AXI4 read interface conversion to AXI4-Lite - // - reg [1:0] r_state_reg, r_state_next; - - reg [AXI_ID_WIDTH-1:0] r_id_reg, r_id_next; - reg [ADDR_WIDTH-1:0] r_addr_reg, r_addr_next; - reg [DATA_WIDTH-1:0] r_data_reg, r_data_next; - reg [1:0] r_resp_reg, r_resp_next; - reg [7:0] r_burst_reg, r_burst_next; - reg [2:0] r_burst_size_reg, r_burst_size_next; - reg [7:0] r_master_burst_reg, r_master_burst_next; - reg [2:0] r_master_burst_size_reg, r_master_burst_size_next; - - reg s_axi_arready_reg, s_axi_arready_next; - reg [AXI_ID_WIDTH-1:0] s_axi_rid_reg, s_axi_rid_next; - reg [DATA_WIDTH-1:0] s_axi_rdata_reg, s_axi_rdata_next; - reg [1:0] s_axi_rresp_reg, s_axi_rresp_next; - reg s_axi_rlast_reg, s_axi_rlast_next; - reg s_axi_rvalid_reg, s_axi_rvalid_next; - - reg [ADDR_WIDTH-1:0] m_axil_araddr_reg, m_axil_araddr_next; - reg [2:0] m_axil_arprot_reg, m_axil_arprot_next; - reg m_axil_arvalid_reg, m_axil_arvalid_next; - reg m_axil_rready_reg, m_axil_rready_next; - - assign s_axi_arready_o = s_axi_arready_reg; - assign s_axi_rid_o = s_axi_rid_reg; - assign s_axi_rdata_o = s_axi_rdata_reg; - assign s_axi_rresp_o = s_axi_rresp_reg; - assign s_axi_rlast_o = s_axi_rlast_reg; - assign s_axi_rvalid_o = s_axi_rvalid_reg; - - assign m_axil_araddr = m_axil_araddr_reg; - assign m_axil_arprot = m_axil_arprot_reg; - assign m_axil_arvalid = m_axil_arvalid_reg; - assign m_axil_rready = m_axil_rready_reg; - - always @* begin - r_state_next = STATE_IDLE; - - r_id_next = r_id_reg; - r_addr_next = r_addr_reg; - r_data_next = r_data_reg; - r_resp_next = r_resp_reg; - r_burst_next = r_burst_reg; - r_burst_size_next = r_burst_size_reg; - r_master_burst_next = r_master_burst_reg; - r_master_burst_size_next = r_master_burst_size_reg; - - s_axi_arready_next = 1'b0; - s_axi_rid_next = s_axi_rid_reg; - s_axi_rdata_next = s_axi_rdata_reg; - s_axi_rresp_next = s_axi_rresp_reg; - s_axi_rlast_next = s_axi_rlast_reg; - s_axi_rvalid_next = s_axi_rvalid_reg & ~s_axi_rready_i; - m_axil_araddr_next = m_axil_araddr_reg; - m_axil_arprot_next = m_axil_arprot_reg; - m_axil_arvalid_next = m_axil_arvalid_reg & ~m_axil_arready; - m_axil_rready_next = 1'b0; - - case (r_state_reg) - default: begin // STATE_IDLE - // idle state; wait for new burst - s_axi_arready_next = ~m_axil_arvalid; - - if (s_axi_arready_o & s_axi_arvalid_i) begin - s_axi_arready_next = 1'b0; - r_id_next = s_axi_arid_i; - m_axil_araddr_next = s_axi_araddr_i; - r_addr_next = s_axi_araddr_i; - r_burst_next = s_axi_arlen_i; - r_burst_size_next = s_axi_arsize_i; - m_axil_arprot_next = s_axi_arprot_i; - m_axil_arvalid_next = 1'b1; - m_axil_rready_next = 1'b0; - r_state_next = STATE_DATA; - end else begin - r_state_next = STATE_IDLE; - end - end - STATE_DATA: begin - // data state; transfer read data - m_axil_rready_next = ~s_axi_rvalid_o & ~m_axil_arvalid; - - if (m_axil_rready & m_axil_rvalid) begin - s_axi_rid_next = r_id_reg; - s_axi_rdata_next = m_axil_rdata; - s_axi_rresp_next = m_axil_rresp; - s_axi_rlast_next = 1'b0; - s_axi_rvalid_next = 1'b1; - r_burst_next = r_burst_reg - 1; - r_addr_next = r_addr_reg + (1 << r_burst_size_reg); - if (r_burst_reg == 0) begin - // last data word, return to idle - m_axil_rready_next = 1'b0; - s_axi_rlast_next = 1'b1; - s_axi_arready_next = ~m_axil_arvalid; - r_state_next = STATE_IDLE; - end else begin - // start new AXI lite read - m_axil_araddr_next = r_addr_next; - m_axil_arvalid_next = 1'b1; - m_axil_rready_next = 1'b0; - r_state_next = STATE_DATA; - end - end else begin - r_state_next = STATE_DATA; - end - end - endcase - end - - always @(posedge clk_i, posedge arst_i) begin - if (arst_i) begin - r_state_reg <= STATE_IDLE; - s_axi_arready_reg <= 1'b0; - s_axi_rvalid_reg <= 1'b0; - m_axil_arvalid_reg <= 1'b0; - m_axil_rready_reg <= 1'b0; - - r_id_reg <= {AXI_ID_WIDTH{1'b0}}; - r_addr_reg <= {ADDR_WIDTH{1'b0}}; - r_data_reg <= {DATA_WIDTH{1'b0}}; - r_resp_reg <= 2'd0; - r_burst_reg <= 8'd0; - r_burst_size_reg <= 3'd0; - r_master_burst_reg <= 8'd0; - r_master_burst_size_reg <= 3'd0; - - s_axi_rid_reg <= {AXI_ID_WIDTH{1'b0}}; - s_axi_rdata_reg <= {DATA_WIDTH{1'b0}}; - s_axi_rresp_reg <= 2'd0; - s_axi_rlast_reg <= 1'b0; - m_axil_araddr_reg <= {ADDR_WIDTH{1'b0}}; - m_axil_arprot_reg <= 3'd0; - end else begin - r_state_reg <= r_state_next; - s_axi_arready_reg <= s_axi_arready_next; - s_axi_rvalid_reg <= s_axi_rvalid_next; - m_axil_arvalid_reg <= m_axil_arvalid_next; - m_axil_rready_reg <= m_axil_rready_next; - - r_id_reg <= r_id_next; - r_addr_reg <= r_addr_next; - r_data_reg <= r_data_next; - r_resp_reg <= r_resp_next; - r_burst_reg <= r_burst_next; - r_burst_size_reg <= r_burst_size_next; - r_master_burst_reg <= r_master_burst_next; - r_master_burst_size_reg <= r_master_burst_size_next; - - s_axi_rid_reg <= s_axi_rid_next; - s_axi_rdata_reg <= s_axi_rdata_next; - s_axi_rresp_reg <= s_axi_rresp_next; - s_axi_rlast_reg <= s_axi_rlast_next; - m_axil_araddr_reg <= m_axil_araddr_next; - m_axil_arprot_reg <= m_axil_arprot_next; - end - end - -endmodule diff --git a/lib/hardware/buses/axi_interconnect/axi_interconnect.py b/lib/hardware/buses/axi_interconnect/axi_interconnect.py deleted file mode 100644 index 6caaf1ac7..000000000 --- a/lib/hardware/buses/axi_interconnect/axi_interconnect.py +++ /dev/null @@ -1,131 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "confs": [ - { - "name": "ID_WIDTH", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "ID bus width", - }, - { - "name": "DATA_WIDTH", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "ADDR_WIDTH", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "M_ADDR_WIDTH", - "type": "P", - "val": "{M_COUNT{{M_REGIONS{32'd24}}}}", - "min": "NA", - "max": "NA", - "descr": "Master address bus width", - }, - { - "name": "S_COUNT", - "type": "P", - "val": "4", - "min": "NA", - "max": "NA", - "descr": "Number of slave interfaces", - }, - { - "name": "M_COUNT", - "type": "P", - "val": "4", - "min": "NA", - "max": "NA", - "descr": "Number of master interfaces", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Synchronous reset", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "s_axi_s", - "interface": { - "type": "axi", - "subtype": "slave", - "port_prefix": "s_", - "mult": "S_COUNT", - "ID_W": "ID_WIDTH", - "ADDR_W": "ADDR_WIDTH", - "DATA_W": "DATA_WIDTH", - "LEN_W": "8", - "LOCK_W": 1, - }, - "descr": "AXI slave interface", - "signals": [ - {"name": "s_axi_awuser", "width": "S_COUNT", "direction": "input"}, - {"name": "s_axi_wuser", "width": "S_COUNT", "direction": "input"}, - {"name": "s_axi_aruser", "width": "S_COUNT", "direction": "input"}, - ], - }, - { - "name": "m_axi_m", - "interface": { - "type": "axi", - "subtype": "master", - "port_prefix": "m_", - "mult": "M_COUNT", - "ID_W": "ID_WIDTH", - "ADDR_W": "ADDR_WIDTH", - "DATA_W": "DATA_WIDTH", - "LEN_W": "8", - "LOCK_W": 1, - }, - "descr": "AXI master interface", - "signals": [ - {"name": "m_axi_buser", "width": "M_COUNT", "direction": "input"}, - {"name": "m_axi_ruser", "width": "M_COUNT", "direction": "input"}, - ], - }, - ], - "blocks": [ - { - "core_name": "arbiter", - "instance_name": "arbiter_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/axi_interconnect/hardware/src/axi_interconnect.v b/lib/hardware/buses/axi_interconnect/hardware/src/axi_interconnect.v deleted file mode 100644 index fffa04311..000000000 --- a/lib/hardware/buses/axi_interconnect/hardware/src/axi_interconnect.v +++ /dev/null @@ -1,971 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Alex Forencich -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* -Changes made (2023 Pedro Antunes): -- formated the code with Verible -- removed intialization in "reg" type signals declaration. -- added reset to registers that did not previously have a reset value. -*/ - -// Language: Verilog 2001 - -`timescale 1ns / 1ps - -/* - * AXI4 interconnect - */ -module axi_interconnect #( - // Number of AXI inputs (slave interfaces) - parameter S_COUNT = 4, - // Number of AXI outputs (master interfaces) - parameter M_COUNT = 4, - // Width of data bus in bits - parameter DATA_WIDTH = 32, - // Width of address bus in bits - parameter ADDR_WIDTH = 32, - // Width of wstrb (width of data bus in words) - parameter STRB_WIDTH = (DATA_WIDTH / 8), - // Width of ID signal - parameter ID_WIDTH = 8, - // Propagate awuser signal - parameter AWUSER_ENABLE = 0, - // Width of awuser signal - parameter AWUSER_WIDTH = 1, - // Propagate wuser signal - parameter WUSER_ENABLE = 0, - // Width of wuser signal - parameter WUSER_WIDTH = 1, - // Propagate buser signal - parameter BUSER_ENABLE = 0, - // Width of buser signal - parameter BUSER_WIDTH = 1, - // Propagate aruser signal - parameter ARUSER_ENABLE = 0, - // Width of aruser signal - parameter ARUSER_WIDTH = 1, - // Propagate ruser signal - parameter RUSER_ENABLE = 0, - // Width of ruser signal - parameter RUSER_WIDTH = 1, - // Propagate ID field - parameter FORWARD_ID = 0, - // Number of regions per master interface - parameter M_REGIONS = 1, - // Master interface base addresses - // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits - // set to zero for default addressing based on M_ADDR_WIDTH - parameter M_BASE_ADDR = 0, - // Master interface address widths - // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits - parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, - // Read connections between interfaces - // M_COUNT concatenated fields of S_COUNT bits - parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, - // Write connections between interfaces - // M_COUNT concatenated fields of S_COUNT bits - parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, - // Secure master (fail operations based on awprot/arprot) - // M_COUNT bits - parameter M_SECURE = {M_COUNT{1'b0}} -) ( - input wire clk_i, - input wire rst_i, - - /* - * AXI slave interfaces - */ - input wire [ S_COUNT*ID_WIDTH-1:0] s_axi_awid_i, - input wire [ S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr_i, - input wire [ S_COUNT*8-1:0] s_axi_awlen_i, - input wire [ S_COUNT*3-1:0] s_axi_awsize_i, - input wire [ S_COUNT*2-1:0] s_axi_awburst_i, - input wire [ S_COUNT-1:0] s_axi_awlock_i, - input wire [ S_COUNT*4-1:0] s_axi_awcache_i, - input wire [ S_COUNT*3-1:0] s_axi_awprot_i, - input wire [ S_COUNT*4-1:0] s_axi_awqos_i, - input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser_i, - input wire [ S_COUNT-1:0] s_axi_awvalid_i, - output wire [ S_COUNT-1:0] s_axi_awready_o, - input wire [ S_COUNT*DATA_WIDTH-1:0] s_axi_wdata_i, - input wire [ S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb_i, - input wire [ S_COUNT-1:0] s_axi_wlast_i, - input wire [ S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser_i, - input wire [ S_COUNT-1:0] s_axi_wvalid_i, - output wire [ S_COUNT-1:0] s_axi_wready_o, - output wire [ S_COUNT*ID_WIDTH-1:0] s_axi_bid_o, - output wire [ S_COUNT*2-1:0] s_axi_bresp_o, - output wire [ S_COUNT*BUSER_WIDTH-1:0] s_axi_buser_o, - output wire [ S_COUNT-1:0] s_axi_bvalid_o, - input wire [ S_COUNT-1:0] s_axi_bready_i, - input wire [ S_COUNT*ID_WIDTH-1:0] s_axi_arid_i, - input wire [ S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr_i, - input wire [ S_COUNT*8-1:0] s_axi_arlen_i, - input wire [ S_COUNT*3-1:0] s_axi_arsize_i, - input wire [ S_COUNT*2-1:0] s_axi_arburst_i, - input wire [ S_COUNT-1:0] s_axi_arlock_i, - input wire [ S_COUNT*4-1:0] s_axi_arcache_i, - input wire [ S_COUNT*3-1:0] s_axi_arprot_i, - input wire [ S_COUNT*4-1:0] s_axi_arqos_i, - input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser_i, - input wire [ S_COUNT-1:0] s_axi_arvalid_i, - output wire [ S_COUNT-1:0] s_axi_arready_o, - output wire [ S_COUNT*ID_WIDTH-1:0] s_axi_rid_o, - output wire [ S_COUNT*DATA_WIDTH-1:0] s_axi_rdata_o, - output wire [ S_COUNT*2-1:0] s_axi_rresp_o, - output wire [ S_COUNT-1:0] s_axi_rlast_o, - output wire [ S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser_o, - output wire [ S_COUNT-1:0] s_axi_rvalid_o, - input wire [ S_COUNT-1:0] s_axi_rready_i, - - /* - * AXI master interfaces - */ - output wire [ M_COUNT*ID_WIDTH-1:0] m_axi_awid_o, - output wire [ M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr_o, - output wire [ M_COUNT*8-1:0] m_axi_awlen_o, - output wire [ M_COUNT*3-1:0] m_axi_awsize_o, - output wire [ M_COUNT*2-1:0] m_axi_awburst_o, - output wire [ M_COUNT-1:0] m_axi_awlock_o, - output wire [ M_COUNT*4-1:0] m_axi_awcache_o, - output wire [ M_COUNT*3-1:0] m_axi_awprot_o, - output wire [ M_COUNT*4-1:0] m_axi_awqos_o, - output wire [ M_COUNT*4-1:0] m_axi_awregion_o, - output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser_o, - output wire [ M_COUNT-1:0] m_axi_awvalid_o, - input wire [ M_COUNT-1:0] m_axi_awready_i, - output wire [ M_COUNT*DATA_WIDTH-1:0] m_axi_wdata_o, - output wire [ M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb_o, - output wire [ M_COUNT-1:0] m_axi_wlast_o, - output wire [ M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser_o, - output wire [ M_COUNT-1:0] m_axi_wvalid_o, - input wire [ M_COUNT-1:0] m_axi_wready_i, - input wire [ M_COUNT*ID_WIDTH-1:0] m_axi_bid_i, - input wire [ M_COUNT*2-1:0] m_axi_bresp_i, - input wire [ M_COUNT*BUSER_WIDTH-1:0] m_axi_buser_i, - input wire [ M_COUNT-1:0] m_axi_bvalid_i, - output wire [ M_COUNT-1:0] m_axi_bready_o, - output wire [ M_COUNT*ID_WIDTH-1:0] m_axi_arid_o, - output wire [ M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr_o, - output wire [ M_COUNT*8-1:0] m_axi_arlen_o, - output wire [ M_COUNT*3-1:0] m_axi_arsize_o, - output wire [ M_COUNT*2-1:0] m_axi_arburst_o, - output wire [ M_COUNT-1:0] m_axi_arlock_o, - output wire [ M_COUNT*4-1:0] m_axi_arcache_o, - output wire [ M_COUNT*3-1:0] m_axi_arprot_o, - output wire [ M_COUNT*4-1:0] m_axi_arqos_o, - output wire [ M_COUNT*4-1:0] m_axi_arregion_o, - output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser_o, - output wire [ M_COUNT-1:0] m_axi_arvalid_o, - input wire [ M_COUNT-1:0] m_axi_arready_i, - input wire [ M_COUNT*ID_WIDTH-1:0] m_axi_rid_i, - input wire [ M_COUNT*DATA_WIDTH-1:0] m_axi_rdata_i, - input wire [ M_COUNT*2-1:0] m_axi_rresp_i, - input wire [ M_COUNT-1:0] m_axi_rlast_i, - input wire [ M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser_i, - input wire [ M_COUNT-1:0] m_axi_rvalid_i, - output wire [ M_COUNT-1:0] m_axi_rready_o -); - - localparam CL_S_COUNT = $clog2(S_COUNT); - localparam CL_M_COUNT = $clog2(M_COUNT); - - localparam AUSER_WIDTH = AWUSER_WIDTH > ARUSER_WIDTH ? AWUSER_WIDTH : ARUSER_WIDTH; - - // default address computation - function automatic [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input integer dummy); - integer i; - reg [ADDR_WIDTH-1:0] base; - begin - calcBaseAddrs = {M_COUNT * M_REGIONS * ADDR_WIDTH{1'b0}}; - base = 0; - for (i = 1; i < M_COUNT * M_REGIONS; i = i + 1) begin - if (M_ADDR_WIDTH[i*32+:32]) begin - base = base + 2 ** M_ADDR_WIDTH[(i-1)*32+:32]; // increment - base = base - (base % 2 ** M_ADDR_WIDTH[i*32+:32]); // align - calcBaseAddrs[i*ADDR_WIDTH+:ADDR_WIDTH] = base; - end - end - end - endfunction - - localparam M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); - - integer i, j; - - // check configuration -`ifndef SYNTHESIS - initial begin - if (M_REGIONS < 1 || M_REGIONS > 16) begin - $error("Error: M_REGIONS must be between 1 and 16 (instance %m)"); - $finish(); - end - - for (i = 0; i < M_COUNT * M_REGIONS; i = i + 1) begin - if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin - $error("Error: address width out of range (instance %m)"); - $finish(); - end - end - - $display("Addressing configuration for axi_interconnect instance %m"); - for (i = 0; i < M_COUNT * M_REGIONS; i = i + 1) begin - if (M_ADDR_WIDTH[i*32+:32]) begin - $display( - "%2d (%2d): %x / %2d -- %x-%x", i / M_REGIONS, i % M_REGIONS, - M_BASE_ADDR_INT[i*ADDR_WIDTH+:ADDR_WIDTH], M_ADDR_WIDTH[i*32+:32], - M_BASE_ADDR_INT[i*ADDR_WIDTH+:ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32+:32]), - M_BASE_ADDR_INT[i*ADDR_WIDTH+:ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32+:32]))); - end - end - - for (i = 0; i < M_COUNT * M_REGIONS; i = i + 1) begin - for (j = i + 1; j < M_COUNT * M_REGIONS; j = j + 1) begin - if (M_ADDR_WIDTH[i*32+:32] && M_ADDR_WIDTH[j*32+:32]) begin - if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin - $display("Overlapping regions:"); - $display( - "%2d (%2d): %x / %2d -- %x-%x", i / M_REGIONS, i % M_REGIONS, - M_BASE_ADDR_INT[i*ADDR_WIDTH+:ADDR_WIDTH], M_ADDR_WIDTH[i*32+:32], - M_BASE_ADDR_INT[i*ADDR_WIDTH+:ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32+:32]), - M_BASE_ADDR_INT[i*ADDR_WIDTH+:ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32+:32]))); - $display( - "%2d (%2d): %x / %2d -- %x-%x", j / M_REGIONS, j % M_REGIONS, - M_BASE_ADDR_INT[j*ADDR_WIDTH+:ADDR_WIDTH], M_ADDR_WIDTH[j*32+:32], - M_BASE_ADDR_INT[j*ADDR_WIDTH+:ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32+:32]), - M_BASE_ADDR_INT[j*ADDR_WIDTH+:ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32+:32]))); - $error("Error: address ranges overlap (instance %m)"); - $finish(); - end - end - end - end - end -`endif - - localparam [2:0] - STATE_IDLE = 3'd0, - STATE_DECODE = 3'd1, - STATE_WRITE = 3'd2, - STATE_WRITE_RESP = 3'd3, - STATE_WRITE_DROP = 3'd4, - STATE_READ = 3'd5, - STATE_READ_DROP = 3'd6, - STATE_WAIT_IDLE = 3'd7; - - reg [2:0] state_reg, state_next; - - reg match; - - reg [CL_M_COUNT-1:0] m_select_reg, m_select_next; - reg [ID_WIDTH-1:0] axi_id_reg, axi_id_next; - reg [ADDR_WIDTH-1:0] axi_addr_reg, axi_addr_next; - reg axi_addr_valid_reg, axi_addr_valid_next; - reg [7:0] axi_len_reg, axi_len_next; - reg [2:0] axi_size_reg, axi_size_next; - reg [1:0] axi_burst_reg, axi_burst_next; - reg axi_lock_reg, axi_lock_next; - reg [3:0] axi_cache_reg, axi_cache_next; - reg [2:0] axi_prot_reg, axi_prot_next; - reg [3:0] axi_qos_reg, axi_qos_next; - reg [3:0] axi_region_reg, axi_region_next; - reg [AUSER_WIDTH-1:0] axi_auser_reg, axi_auser_next; - reg [1:0] axi_bresp_reg, axi_bresp_next; - reg [BUSER_WIDTH-1:0] axi_buser_reg, axi_buser_next; - - reg [S_COUNT-1:0] s_axi_awready_reg, s_axi_awready_next; - reg [S_COUNT-1:0] s_axi_wready_reg, s_axi_wready_next; - reg [S_COUNT-1:0] s_axi_bvalid_reg, s_axi_bvalid_next; - reg [S_COUNT-1:0] s_axi_arready_reg, s_axi_arready_next; - - reg [M_COUNT-1:0] m_axi_awvalid_reg, m_axi_awvalid_next; - reg [M_COUNT-1:0] m_axi_bready_reg, m_axi_bready_next; - reg [M_COUNT-1:0] m_axi_arvalid_reg, m_axi_arvalid_next; - reg [M_COUNT-1:0] m_axi_rready_reg, m_axi_rready_next; - - // internal datapath - reg [ ID_WIDTH-1:0] s_axi_rid_int; - reg [ DATA_WIDTH-1:0] s_axi_rdata_int; - reg [ 1:0] s_axi_rresp_int; - reg s_axi_rlast_int; - reg [RUSER_WIDTH-1:0] s_axi_ruser_int; - reg s_axi_rvalid_int; - reg s_axi_rready_int_reg; - wire s_axi_rready_int_early; - - reg [ DATA_WIDTH-1:0] m_axi_wdata_int; - reg [ STRB_WIDTH-1:0] m_axi_wstrb_int; - reg m_axi_wlast_int; - reg [WUSER_WIDTH-1:0] m_axi_wuser_int; - reg m_axi_wvalid_int; - reg m_axi_wready_int_reg; - wire m_axi_wready_int_early; - - assign s_axi_awready_o = s_axi_awready_reg; - assign s_axi_wready_o = s_axi_wready_reg; - assign s_axi_bid_o = {S_COUNT{axi_id_reg}}; - assign s_axi_bresp_o = {S_COUNT{axi_bresp_reg}}; - assign s_axi_buser_o = {S_COUNT{BUSER_ENABLE ? axi_buser_reg : {BUSER_WIDTH{1'b0}}}}; - assign s_axi_bvalid_o = s_axi_bvalid_reg; - assign s_axi_arready_o = s_axi_arready_reg; - - assign m_axi_awid_o = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; - assign m_axi_awaddr_o = {M_COUNT{axi_addr_reg}}; - assign m_axi_awlen_o = {M_COUNT{axi_len_reg}}; - assign m_axi_awsize_o = {M_COUNT{axi_size_reg}}; - assign m_axi_awburst_o = {M_COUNT{axi_burst_reg}}; - assign m_axi_awlock_o = {M_COUNT{axi_lock_reg}}; - assign m_axi_awcache_o = {M_COUNT{axi_cache_reg}}; - assign m_axi_awprot_o = {M_COUNT{axi_prot_reg}}; - assign m_axi_awqos_o = {M_COUNT{axi_qos_reg}}; - assign m_axi_awregion_o = {M_COUNT{axi_region_reg}}; - assign m_axi_awuser_o = {M_COUNT{AWUSER_ENABLE ? axi_auser_reg[AWUSER_WIDTH-1:0] : {AWUSER_WIDTH{1'b0}}}}; - assign m_axi_awvalid_o = m_axi_awvalid_reg; - assign m_axi_bready_o = m_axi_bready_reg; - assign m_axi_arid_o = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; - assign m_axi_araddr_o = {M_COUNT{axi_addr_reg}}; - assign m_axi_arlen_o = {M_COUNT{axi_len_reg}}; - assign m_axi_arsize_o = {M_COUNT{axi_size_reg}}; - assign m_axi_arburst_o = {M_COUNT{axi_burst_reg}}; - assign m_axi_arlock_o = {M_COUNT{axi_lock_reg}}; - assign m_axi_arcache_o = {M_COUNT{axi_cache_reg}}; - assign m_axi_arprot_o = {M_COUNT{axi_prot_reg}}; - assign m_axi_arqos_o = {M_COUNT{axi_qos_reg}}; - assign m_axi_arregion_o = {M_COUNT{axi_region_reg}}; - assign m_axi_aruser_o = {M_COUNT{ARUSER_ENABLE ? axi_auser_reg[ARUSER_WIDTH-1:0] : {ARUSER_WIDTH{1'b0}}}}; - assign m_axi_arvalid_o = m_axi_arvalid_reg; - assign m_axi_rready_o = m_axi_rready_reg; - - // slave side mux - wire [(CL_S_COUNT > 0 ? CL_S_COUNT-1 : 0):0] s_select; - - wire [ID_WIDTH-1:0] current_s_axi_awid = s_axi_awid_i[s_select*ID_WIDTH+:ID_WIDTH]; - wire [ADDR_WIDTH-1:0] current_s_axi_awaddr = s_axi_awaddr_i[s_select*ADDR_WIDTH+:ADDR_WIDTH]; - wire [7:0] current_s_axi_awlen = s_axi_awlen_i[s_select*8+:8]; - wire [2:0] current_s_axi_awsize = s_axi_awsize_i[s_select*3+:3]; - wire [1:0] current_s_axi_awburst = s_axi_awburst_i[s_select*2+:2]; - wire current_s_axi_awlock = s_axi_awlock_i[s_select]; - wire [3:0] current_s_axi_awcache = s_axi_awcache_i[s_select*4+:4]; - wire [2:0] current_s_axi_awprot = s_axi_awprot_i[s_select*3+:3]; - wire [3:0] current_s_axi_awqos = s_axi_awqos_i[s_select*4+:4]; - wire [AWUSER_WIDTH-1:0] current_s_axi_awuser = s_axi_awuser_i[s_select*AWUSER_WIDTH+:AWUSER_WIDTH]; - wire current_s_axi_awvalid = s_axi_awvalid_i[s_select]; - wire current_s_axi_awready = s_axi_awready_o[s_select]; - wire [DATA_WIDTH-1:0] current_s_axi_wdata = s_axi_wdata_i[s_select*DATA_WIDTH+:DATA_WIDTH]; - wire [STRB_WIDTH-1:0] current_s_axi_wstrb = s_axi_wstrb_i[s_select*STRB_WIDTH+:STRB_WIDTH]; - wire current_s_axi_wlast = s_axi_wlast_i[s_select]; - wire [WUSER_WIDTH-1:0] current_s_axi_wuser = s_axi_wuser_i[s_select*WUSER_WIDTH+:WUSER_WIDTH]; - wire current_s_axi_wvalid = s_axi_wvalid_i[s_select]; - wire current_s_axi_wready = s_axi_wready_o[s_select]; - wire [ID_WIDTH-1:0] current_s_axi_bid = s_axi_bid_o[s_select*ID_WIDTH+:ID_WIDTH]; - wire [1:0] current_s_axi_bresp = s_axi_bresp_o[s_select*2+:2]; - wire [BUSER_WIDTH-1:0] current_s_axi_buser = s_axi_buser_o[s_select*BUSER_WIDTH+:BUSER_WIDTH]; - wire current_s_axi_bvalid = s_axi_bvalid_o[s_select]; - wire current_s_axi_bready = s_axi_bready_i[s_select]; - wire [ID_WIDTH-1:0] current_s_axi_arid = s_axi_arid_i[s_select*ID_WIDTH+:ID_WIDTH]; - wire [ADDR_WIDTH-1:0] current_s_axi_araddr = s_axi_araddr_i[s_select*ADDR_WIDTH+:ADDR_WIDTH]; - wire [7:0] current_s_axi_arlen = s_axi_arlen_i[s_select*8+:8]; - wire [2:0] current_s_axi_arsize = s_axi_arsize_i[s_select*3+:3]; - wire [1:0] current_s_axi_arburst = s_axi_arburst_i[s_select*2+:2]; - wire current_s_axi_arlock = s_axi_arlock_i[s_select]; - wire [3:0] current_s_axi_arcache = s_axi_arcache_i[s_select*4+:4]; - wire [2:0] current_s_axi_arprot = s_axi_arprot_i[s_select*3+:3]; - wire [3:0] current_s_axi_arqos = s_axi_arqos_i[s_select*4+:4]; - wire [ARUSER_WIDTH-1:0] current_s_axi_aruser = s_axi_aruser_i[s_select*ARUSER_WIDTH+:ARUSER_WIDTH]; - wire current_s_axi_arvalid = s_axi_arvalid_i[s_select]; - wire current_s_axi_arready = s_axi_arready_o[s_select]; - wire [ID_WIDTH-1:0] current_s_axi_rid = s_axi_rid_o[s_select*ID_WIDTH+:ID_WIDTH]; - wire [DATA_WIDTH-1:0] current_s_axi_rdata = s_axi_rdata_o[s_select*DATA_WIDTH+:DATA_WIDTH]; - wire [1:0] current_s_axi_rresp = s_axi_rresp_o[s_select*2+:2]; - wire current_s_axi_rlast = s_axi_rlast_o[s_select]; - wire [RUSER_WIDTH-1:0] current_s_axi_ruser = s_axi_ruser_o[s_select*RUSER_WIDTH+:RUSER_WIDTH]; - wire current_s_axi_rvalid = s_axi_rvalid_o[s_select]; - wire current_s_axi_rready = s_axi_rready_i[s_select]; - - // master side mux - wire [ID_WIDTH-1:0] current_m_axi_awid = m_axi_awid_o[m_select_reg*ID_WIDTH+:ID_WIDTH]; - wire [ADDR_WIDTH-1:0] current_m_axi_awaddr = m_axi_awaddr_o[m_select_reg*ADDR_WIDTH+:ADDR_WIDTH]; - wire [7:0] current_m_axi_awlen = m_axi_awlen_o[m_select_reg*8+:8]; - wire [2:0] current_m_axi_awsize = m_axi_awsize_o[m_select_reg*3+:3]; - wire [1:0] current_m_axi_awburst = m_axi_awburst_o[m_select_reg*2+:2]; - wire current_m_axi_awlock = m_axi_awlock_o[m_select_reg]; - wire [3:0] current_m_axi_awcache = m_axi_awcache_o[m_select_reg*4+:4]; - wire [2:0] current_m_axi_awprot = m_axi_awprot_o[m_select_reg*3+:3]; - wire [3:0] current_m_axi_awqos = m_axi_awqos_o[m_select_reg*4+:4]; - wire [3:0] current_m_axi_awregion = m_axi_awregion_o[m_select_reg*4+:4]; - wire [AWUSER_WIDTH-1:0] current_m_axi_awuser = m_axi_awuser_o[m_select_reg*AWUSER_WIDTH +: AWUSER_WIDTH]; - wire current_m_axi_awvalid = m_axi_awvalid_o[m_select_reg]; - wire current_m_axi_awready = m_axi_awready_i[m_select_reg]; - wire [DATA_WIDTH-1:0] current_m_axi_wdata = m_axi_wdata_o[m_select_reg*DATA_WIDTH+:DATA_WIDTH]; - wire [STRB_WIDTH-1:0] current_m_axi_wstrb = m_axi_wstrb_o[m_select_reg*STRB_WIDTH+:STRB_WIDTH]; - wire current_m_axi_wlast = m_axi_wlast_o[m_select_reg]; - wire [WUSER_WIDTH-1:0] current_m_axi_wuser = m_axi_wuser_o[m_select_reg*WUSER_WIDTH+:WUSER_WIDTH]; - wire current_m_axi_wvalid = m_axi_wvalid_o[m_select_reg]; - wire current_m_axi_wready = m_axi_wready_i[m_select_reg]; - wire [ID_WIDTH-1:0] current_m_axi_bid = m_axi_bid_i[m_select_reg*ID_WIDTH+:ID_WIDTH]; - wire [1:0] current_m_axi_bresp = m_axi_bresp_i[m_select_reg*2+:2]; - wire [BUSER_WIDTH-1:0] current_m_axi_buser = m_axi_buser_i[m_select_reg*BUSER_WIDTH+:BUSER_WIDTH]; - wire current_m_axi_bvalid = m_axi_bvalid_i[m_select_reg]; - wire current_m_axi_bready = m_axi_bready_o[m_select_reg]; - wire [ID_WIDTH-1:0] current_m_axi_arid = m_axi_arid_o[m_select_reg*ID_WIDTH+:ID_WIDTH]; - wire [ADDR_WIDTH-1:0] current_m_axi_araddr = m_axi_araddr_o[m_select_reg*ADDR_WIDTH+:ADDR_WIDTH]; - wire [7:0] current_m_axi_arlen = m_axi_arlen_o[m_select_reg*8+:8]; - wire [2:0] current_m_axi_arsize = m_axi_arsize_o[m_select_reg*3+:3]; - wire [1:0] current_m_axi_arburst = m_axi_arburst_o[m_select_reg*2+:2]; - wire current_m_axi_arlock = m_axi_arlock_o[m_select_reg]; - wire [3:0] current_m_axi_arcache = m_axi_arcache_o[m_select_reg*4+:4]; - wire [2:0] current_m_axi_arprot = m_axi_arprot_o[m_select_reg*3+:3]; - wire [3:0] current_m_axi_arqos = m_axi_arqos_o[m_select_reg*4+:4]; - wire [3:0] current_m_axi_arregion = m_axi_arregion_o[m_select_reg*4+:4]; - wire [ARUSER_WIDTH-1:0] current_m_axi_aruser = m_axi_aruser_o[m_select_reg*ARUSER_WIDTH +: ARUSER_WIDTH]; - wire current_m_axi_arvalid = m_axi_arvalid_o[m_select_reg]; - wire current_m_axi_arready = m_axi_arready_i[m_select_reg]; - wire [ID_WIDTH-1:0] current_m_axi_rid = m_axi_rid_i[m_select_reg*ID_WIDTH+:ID_WIDTH]; - wire [DATA_WIDTH-1:0] current_m_axi_rdata = m_axi_rdata_i[m_select_reg*DATA_WIDTH+:DATA_WIDTH]; - wire [1:0] current_m_axi_rresp = m_axi_rresp_i[m_select_reg*2+:2]; - wire current_m_axi_rlast = m_axi_rlast_i[m_select_reg]; - wire [RUSER_WIDTH-1:0] current_m_axi_ruser = m_axi_ruser_i[m_select_reg*RUSER_WIDTH+:RUSER_WIDTH]; - wire current_m_axi_rvalid = m_axi_rvalid_i[m_select_reg]; - wire current_m_axi_rready = m_axi_rready_o[m_select_reg]; - - // arbiter instance - wire [S_COUNT*2-1:0] request; - wire [S_COUNT*2-1:0] acknowledge; - wire [S_COUNT*2-1:0] grant; - wire grant_valid; - wire [CL_S_COUNT:0] grant_encoded; - - wire read = grant_encoded[0]; - assign s_select = grant_encoded >> 1; - - arbiter #( - .PORTS (S_COUNT * 2), - .TYPE ("ROUND_ROBIN"), - .BLOCK ("ACKNOWLEDGE"), - .LSB_PRIORITY("HIGH") - ) arb_inst ( - .clk (clk_i), - .rst (rst_i), - .request (request), - .acknowledge (acknowledge), - .grant (grant), - .grant_valid (grant_valid), - .grant_encoded(grant_encoded) - ); - - genvar n; - - // request generation - generate - for (n = 0; n < S_COUNT; n = n + 1) begin : req_gen - assign request[2*n] = s_axi_awvalid_i[n]; - assign request[2*n+1] = s_axi_arvalid_i[n]; - end - endgenerate - - // acknowledge generation - generate - for (n = 0; n < S_COUNT; n = n + 1) begin : ack_gen - assign acknowledge[2*n] = grant[2*n] && s_axi_bvalid_o[n] && s_axi_bready_i[n]; - assign acknowledge[2*n+1] = grant[2*n+1] && s_axi_rvalid_o[n] && s_axi_rready_i[n] && s_axi_rlast_o[n]; - end - endgenerate - - always @* begin - state_next = STATE_IDLE; - - match = 1'b0; - - m_select_next = m_select_reg; - axi_id_next = axi_id_reg; - axi_addr_next = axi_addr_reg; - axi_addr_valid_next = axi_addr_valid_reg; - axi_len_next = axi_len_reg; - axi_size_next = axi_size_reg; - axi_burst_next = axi_burst_reg; - axi_lock_next = axi_lock_reg; - axi_cache_next = axi_cache_reg; - axi_prot_next = axi_prot_reg; - axi_qos_next = axi_qos_reg; - axi_region_next = axi_region_reg; - axi_auser_next = axi_auser_reg; - axi_bresp_next = axi_bresp_reg; - axi_buser_next = axi_buser_reg; - - s_axi_awready_next = 0; - s_axi_wready_next = 0; - s_axi_bvalid_next = s_axi_bvalid_reg & ~s_axi_bready_i; - s_axi_arready_next = 0; - - m_axi_awvalid_next = m_axi_awvalid_reg & ~m_axi_awready_i; - m_axi_bready_next = 0; - m_axi_arvalid_next = m_axi_arvalid_reg & ~m_axi_arready_i; - m_axi_rready_next = 0; - - s_axi_rid_int = axi_id_reg; - s_axi_rdata_int = current_m_axi_rdata; - s_axi_rresp_int = current_m_axi_rresp; - s_axi_rlast_int = current_m_axi_rlast; - s_axi_ruser_int = current_m_axi_ruser; - s_axi_rvalid_int = 1'b0; - - m_axi_wdata_int = current_s_axi_wdata; - m_axi_wstrb_int = current_s_axi_wstrb; - m_axi_wlast_int = current_s_axi_wlast; - m_axi_wuser_int = current_s_axi_wuser; - m_axi_wvalid_int = 1'b0; - - case (state_reg) - STATE_IDLE: begin - // idle state; wait for arbitration - - if (grant_valid) begin - - axi_addr_valid_next = 1'b1; - - if (read) begin - // reading - axi_addr_next = current_s_axi_araddr; - axi_prot_next = current_s_axi_arprot; - axi_id_next = current_s_axi_arid; - axi_addr_next = current_s_axi_araddr; - axi_len_next = current_s_axi_arlen; - axi_size_next = current_s_axi_arsize; - axi_burst_next = current_s_axi_arburst; - axi_lock_next = current_s_axi_arlock; - axi_cache_next = current_s_axi_arcache; - axi_prot_next = current_s_axi_arprot; - axi_qos_next = current_s_axi_arqos; - axi_auser_next = current_s_axi_aruser; - s_axi_arready_next[s_select] = 1'b1; - end else begin - // writing - axi_addr_next = current_s_axi_awaddr; - axi_prot_next = current_s_axi_awprot; - axi_id_next = current_s_axi_awid; - axi_addr_next = current_s_axi_awaddr; - axi_len_next = current_s_axi_awlen; - axi_size_next = current_s_axi_awsize; - axi_burst_next = current_s_axi_awburst; - axi_lock_next = current_s_axi_awlock; - axi_cache_next = current_s_axi_awcache; - axi_prot_next = current_s_axi_awprot; - axi_qos_next = current_s_axi_awqos; - axi_auser_next = current_s_axi_awuser; - s_axi_awready_next[s_select] = 1'b1; - end - - state_next = STATE_DECODE; - end else begin - state_next = STATE_IDLE; - end - end - STATE_DECODE: begin - // decode state; determine master interface - - match = 1'b0; - for (i = 0; i < M_COUNT; i = i + 1) begin - for (j = 0; j < M_REGIONS; j = j + 1) begin - if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !axi_prot_reg[1]) && ((read ? M_CONNECT_READ : M_CONNECT_WRITE) & (1 << (s_select+i*S_COUNT))) && (axi_addr_reg >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin - m_select_next = i; - axi_region_next = j; - match = 1'b1; - end - end - end - - if (match) begin - if (read) begin - // reading - m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; - state_next = STATE_READ; - end else begin - // writing - s_axi_wready_next[s_select] = m_axi_wready_int_early; - state_next = STATE_WRITE; - end - end else begin - // no match; return decode error - if (read) begin - // reading - state_next = STATE_READ_DROP; - end else begin - // writing - axi_bresp_next = 2'b11; - s_axi_wready_next[s_select] = 1'b1; - state_next = STATE_WRITE_DROP; - end - end - end - STATE_WRITE: begin - // write state; store and forward write data - s_axi_wready_next[s_select] = m_axi_wready_int_early; - - if (axi_addr_valid_reg) begin - m_axi_awvalid_next[m_select_reg] = 1'b1; - end - axi_addr_valid_next = 1'b0; - - if (current_s_axi_wready && current_s_axi_wvalid) begin - m_axi_wdata_int = current_s_axi_wdata; - m_axi_wstrb_int = current_s_axi_wstrb; - m_axi_wlast_int = current_s_axi_wlast; - m_axi_wuser_int = current_s_axi_wuser; - m_axi_wvalid_int = 1'b1; - - if (current_s_axi_wlast) begin - s_axi_wready_next[s_select] = 1'b0; - m_axi_bready_next[m_select_reg] = 1'b1; - state_next = STATE_WRITE_RESP; - end else begin - state_next = STATE_WRITE; - end - end else begin - state_next = STATE_WRITE; - end - end - STATE_WRITE_RESP: begin - // write response state; store and forward write response - m_axi_bready_next[m_select_reg] = 1'b1; - - if (current_m_axi_bready && current_m_axi_bvalid) begin - m_axi_bready_next[m_select_reg] = 1'b0; - axi_bresp_next = current_m_axi_bresp; - s_axi_bvalid_next[s_select] = 1'b1; - state_next = STATE_WAIT_IDLE; - end else begin - state_next = STATE_WRITE_RESP; - end - end - STATE_WRITE_DROP: begin - // write drop state; drop write data - s_axi_wready_next[s_select] = 1'b1; - - axi_addr_valid_next = 1'b0; - - if (current_s_axi_wready && current_s_axi_wvalid) begin - s_axi_wready_next[s_select] = 1'b0; - s_axi_bvalid_next[s_select] = 1'b1; - state_next = STATE_WAIT_IDLE; - end else begin - state_next = STATE_WRITE_DROP; - end - end - STATE_READ: begin - // read state; store and forward read response - m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; - - if (axi_addr_valid_reg) begin - m_axi_arvalid_next[m_select_reg] = 1'b1; - end - axi_addr_valid_next = 1'b0; - - if (current_m_axi_rready && current_m_axi_rvalid) begin - s_axi_rid_int = axi_id_reg; - s_axi_rdata_int = current_m_axi_rdata; - s_axi_rresp_int = current_m_axi_rresp; - s_axi_rlast_int = current_m_axi_rlast; - s_axi_ruser_int = current_m_axi_ruser; - s_axi_rvalid_int = 1'b1; - - if (current_m_axi_rlast) begin - m_axi_rready_next[m_select_reg] = 1'b0; - state_next = STATE_WAIT_IDLE; - end else begin - state_next = STATE_READ; - end - end else begin - state_next = STATE_READ; - end - end - STATE_READ_DROP: begin - // read drop state; generate decode error read response - - s_axi_rid_int = axi_id_reg; - s_axi_rdata_int = {DATA_WIDTH{1'b0}}; - s_axi_rresp_int = 2'b11; - s_axi_rlast_int = axi_len_reg == 0; - s_axi_ruser_int = {RUSER_WIDTH{1'b0}}; - s_axi_rvalid_int = 1'b1; - - if (s_axi_rready_int_reg) begin - axi_len_next = axi_len_reg - 1; - if (axi_len_reg == 0) begin - state_next = STATE_WAIT_IDLE; - end else begin - state_next = STATE_READ_DROP; - end - end else begin - state_next = STATE_READ_DROP; - end - end - STATE_WAIT_IDLE: begin - // wait for idle state; wait untl grant valid is deasserted - - if (!grant_valid || acknowledge) begin - state_next = STATE_IDLE; - end else begin - state_next = STATE_WAIT_IDLE; - end - end - default: ; // Do nothing - endcase - end - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - state_reg <= STATE_IDLE; - - s_axi_awready_reg <= 0; - s_axi_wready_reg <= 0; - s_axi_bvalid_reg <= 0; - s_axi_arready_reg <= 0; - - m_axi_awvalid_reg <= 0; - m_axi_bready_reg <= 0; - m_axi_arvalid_reg <= 0; - m_axi_rready_reg <= 0; - - m_select_reg <= 2'd0; - axi_id_reg <= {ID_WIDTH{1'b0}}; - axi_addr_reg <= {ADDR_WIDTH{1'b0}}; - axi_addr_valid_reg <= 1'b0; - axi_len_reg <= {8{1'b0}}; - axi_size_reg <= {3{1'b0}}; - axi_burst_reg <= {2{1'b0}}; - axi_lock_reg <= 1'b0; - axi_cache_reg <= {4{1'b0}}; - axi_prot_reg <= {3{1'b0}}; - axi_qos_reg <= {4{1'b0}}; - axi_region_reg <= {4{1'b0}}; - axi_auser_reg <= {ARUSER_WIDTH{1'b0}}; - axi_bresp_reg <= {2{1'b0}}; - axi_buser_reg <= {BUSER_WIDTH{1'b0}}; - end else begin - state_reg <= state_next; - - s_axi_awready_reg <= s_axi_awready_next; - s_axi_wready_reg <= s_axi_wready_next; - s_axi_bvalid_reg <= s_axi_bvalid_next; - s_axi_arready_reg <= s_axi_arready_next; - - m_axi_awvalid_reg <= m_axi_awvalid_next; - m_axi_bready_reg <= m_axi_bready_next; - m_axi_arvalid_reg <= m_axi_arvalid_next; - m_axi_rready_reg <= m_axi_rready_next; - - m_select_reg <= m_select_next; - axi_id_reg <= axi_id_next; - axi_addr_reg <= axi_addr_next; - axi_addr_valid_reg <= axi_addr_valid_next; - axi_len_reg <= axi_len_next; - axi_size_reg <= axi_size_next; - axi_burst_reg <= axi_burst_next; - axi_lock_reg <= axi_lock_next; - axi_cache_reg <= axi_cache_next; - axi_prot_reg <= axi_prot_next; - axi_qos_reg <= axi_qos_next; - axi_region_reg <= axi_region_next; - axi_auser_reg <= axi_auser_next; - axi_bresp_reg <= axi_bresp_next; - axi_buser_reg <= axi_buser_next; - end - end - - // output datapath logic (R channel) - reg [ ID_WIDTH-1:0] s_axi_rid_reg; - reg [ DATA_WIDTH-1:0] s_axi_rdata_reg; - reg [ 1:0] s_axi_rresp_reg; - reg s_axi_rlast_reg; - reg [RUSER_WIDTH-1:0] s_axi_ruser_reg; - reg [S_COUNT-1:0] s_axi_rvalid_reg, s_axi_rvalid_next; - - reg [ ID_WIDTH-1:0] temp_s_axi_rid_reg; - reg [ DATA_WIDTH-1:0] temp_s_axi_rdata_reg; - reg [ 1:0] temp_s_axi_rresp_reg; - reg temp_s_axi_rlast_reg; - reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg; - reg temp_s_axi_rvalid_reg, temp_s_axi_rvalid_next; - - // datapath control - reg store_axi_r_int_to_output; - reg store_axi_r_int_to_temp; - reg store_axi_r_temp_to_output; - - assign s_axi_rid_o = {S_COUNT{s_axi_rid_reg}}; - assign s_axi_rdata_o = {S_COUNT{s_axi_rdata_reg}}; - assign s_axi_rresp_o = {S_COUNT{s_axi_rresp_reg}}; - assign s_axi_rlast_o = {S_COUNT{s_axi_rlast_reg}}; - assign s_axi_ruser_o = {S_COUNT{RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}}}; - assign s_axi_rvalid_o = s_axi_rvalid_reg; - - // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) - assign s_axi_rready_int_early = current_s_axi_rready | (~temp_s_axi_rvalid_reg & (~current_s_axi_rvalid | ~s_axi_rvalid_int)); - - always @* begin - // transfer sink ready state to source - s_axi_rvalid_next = s_axi_rvalid_reg; - temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; - - store_axi_r_int_to_output = 1'b0; - store_axi_r_int_to_temp = 1'b0; - store_axi_r_temp_to_output = 1'b0; - - if (s_axi_rready_int_reg) begin - // input is ready - if (current_s_axi_rready | ~current_s_axi_rvalid) begin - // output is ready or currently not valid, transfer data to output - s_axi_rvalid_next[s_select] = s_axi_rvalid_int; - store_axi_r_int_to_output = 1'b1; - end else begin - // output is not ready, store input in temp - temp_s_axi_rvalid_next = s_axi_rvalid_int; - store_axi_r_int_to_temp = 1'b1; - end - end else if (current_s_axi_rready) begin - // input is not ready, but output is ready - s_axi_rvalid_next[s_select] = temp_s_axi_rvalid_reg; - temp_s_axi_rvalid_next = 1'b0; - store_axi_r_temp_to_output = 1'b1; - end - end - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - s_axi_rvalid_reg <= 1'b0; - s_axi_rready_int_reg <= 1'b0; - temp_s_axi_rvalid_reg <= 1'b0; - s_axi_rid_reg <= {ID_WIDTH{1'b0}}; - s_axi_rdata_reg <= {DATA_WIDTH{1'b0}}; - s_axi_rresp_reg <= 2'd0; - s_axi_rlast_reg <= 1'b0; - s_axi_ruser_reg <= {RUSER_WIDTH{1'b0}}; - temp_s_axi_rid_reg <= {ID_WIDTH{1'b0}}; - temp_s_axi_rdata_reg <= {DATA_WIDTH{1'b0}}; - temp_s_axi_rresp_reg <= 2'd0; - temp_s_axi_rlast_reg <= 1'b0; - temp_s_axi_ruser_reg <= {RUSER_WIDTH{1'b0}}; - end else begin - s_axi_rvalid_reg <= s_axi_rvalid_next; - s_axi_rready_int_reg <= s_axi_rready_int_early; - temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; - // datapath - if (store_axi_r_int_to_output) begin - s_axi_rid_reg <= s_axi_rid_int; - s_axi_rdata_reg <= s_axi_rdata_int; - s_axi_rresp_reg <= s_axi_rresp_int; - s_axi_rlast_reg <= s_axi_rlast_int; - s_axi_ruser_reg <= s_axi_ruser_int; - end else if (store_axi_r_temp_to_output) begin - s_axi_rid_reg <= temp_s_axi_rid_reg; - s_axi_rdata_reg <= temp_s_axi_rdata_reg; - s_axi_rresp_reg <= temp_s_axi_rresp_reg; - s_axi_rlast_reg <= temp_s_axi_rlast_reg; - s_axi_ruser_reg <= temp_s_axi_ruser_reg; - end - if (store_axi_r_int_to_temp) begin - temp_s_axi_rid_reg <= s_axi_rid_int; - temp_s_axi_rdata_reg <= s_axi_rdata_int; - temp_s_axi_rresp_reg <= s_axi_rresp_int; - temp_s_axi_rlast_reg <= s_axi_rlast_int; - temp_s_axi_ruser_reg <= s_axi_ruser_int; - end - end - end - - // output datapath logic (W channel) - reg [ DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; - reg [ STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; - reg m_axi_wlast_reg = 1'b0; - reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = 1'b0; - reg [M_COUNT-1:0] m_axi_wvalid_reg, m_axi_wvalid_next; - - reg [ DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; - reg [ STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; - reg temp_m_axi_wlast_reg = 1'b0; - reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = 1'b0; - reg temp_m_axi_wvalid_reg, temp_m_axi_wvalid_next; - - // datapath control - reg store_axi_w_int_to_output; - reg store_axi_w_int_to_temp; - reg store_axi_w_temp_to_output; - - assign m_axi_wdata_o = {M_COUNT{m_axi_wdata_reg}}; - assign m_axi_wstrb_o = {M_COUNT{m_axi_wstrb_reg}}; - assign m_axi_wlast_o = {M_COUNT{m_axi_wlast_reg}}; - assign m_axi_wuser_o = {M_COUNT{WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}}}; - assign m_axi_wvalid_o = m_axi_wvalid_reg; - - // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) - assign m_axi_wready_int_early = current_m_axi_wready | (~temp_m_axi_wvalid_reg & (~current_m_axi_wvalid | ~m_axi_wvalid_int)); - - always @* begin - // transfer sink ready state to source - m_axi_wvalid_next = m_axi_wvalid_reg; - temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; - - store_axi_w_int_to_output = 1'b0; - store_axi_w_int_to_temp = 1'b0; - store_axi_w_temp_to_output = 1'b0; - - if (m_axi_wready_int_reg) begin - // input is ready - if (current_m_axi_wready | ~current_m_axi_wvalid) begin - // output is ready or currently not valid, transfer data to output - m_axi_wvalid_next[m_select_reg] = m_axi_wvalid_int; - store_axi_w_int_to_output = 1'b1; - end else begin - // output is not ready, store input in temp - temp_m_axi_wvalid_next = m_axi_wvalid_int; - store_axi_w_int_to_temp = 1'b1; - end - end else if (current_m_axi_wready) begin - // input is not ready, but output is ready - m_axi_wvalid_next[m_select_reg] = temp_m_axi_wvalid_reg; - temp_m_axi_wvalid_next = 1'b0; - store_axi_w_temp_to_output = 1'b1; - end - end - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - m_axi_wvalid_reg <= 1'b0; - m_axi_wready_int_reg <= 1'b0; - temp_m_axi_wvalid_reg <= 1'b0; - m_axi_wdata_reg <= {DATA_WIDTH{1'b0}}; - m_axi_wstrb_reg <= {STRB_WIDTH{1'b0}}; - m_axi_wlast_reg <= 1'b0; - m_axi_wuser_reg <= {WUSER_WIDTH{1'b0}}; - temp_m_axi_wdata_reg <= {DATA_WIDTH{1'b0}}; - temp_m_axi_wstrb_reg <= {STRB_WIDTH{1'b0}}; - temp_m_axi_wlast_reg <= 1'b0; - temp_m_axi_wuser_reg <= {WUSER_WIDTH{1'b0}}; - end else begin - m_axi_wvalid_reg <= m_axi_wvalid_next; - m_axi_wready_int_reg <= m_axi_wready_int_early; - temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; - // datapath - if (store_axi_w_int_to_output) begin - m_axi_wdata_reg <= m_axi_wdata_int; - m_axi_wstrb_reg <= m_axi_wstrb_int; - m_axi_wlast_reg <= m_axi_wlast_int; - m_axi_wuser_reg <= m_axi_wuser_int; - end else if (store_axi_w_temp_to_output) begin - m_axi_wdata_reg <= temp_m_axi_wdata_reg; - m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; - m_axi_wlast_reg <= temp_m_axi_wlast_reg; - m_axi_wuser_reg <= temp_m_axi_wuser_reg; - end - if (store_axi_w_int_to_temp) begin - temp_m_axi_wdata_reg <= m_axi_wdata_int; - temp_m_axi_wstrb_reg <= m_axi_wstrb_int; - temp_m_axi_wlast_reg <= m_axi_wlast_int; - temp_m_axi_wuser_reg <= m_axi_wuser_int; - end - end - end - -endmodule diff --git a/lib/hardware/buses/axi_interconnect_wrapper/axi_interconnect_wrapper.py b/lib/hardware/buses/axi_interconnect_wrapper/axi_interconnect_wrapper.py deleted file mode 100644 index a1349ce03..000000000 --- a/lib/hardware/buses/axi_interconnect_wrapper/axi_interconnect_wrapper.py +++ /dev/null @@ -1,306 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -AXI_IN_SIGNAL_NAMES = [ - ("araddr", "AXI_ADDR_W"), - ("arprot", 3), - ("arvalid", 1), - ("rready", 1), - ("arid", "AXI_ID_W"), - ("arlen", 8), - ("arsize", 3), - ("arburst", 2), - ("arlock", 1), - ("arcache", 4), - ("arqos", 4), - ("awaddr", "AXI_ADDR_W"), - ("awprot", 3), - ("awvalid", 1), - ("wdata", "AXI_DATA_W"), - ("wstrb", "AXI_DATA_W / 8"), - ("wvalid", 1), - ("bready", 1), - ("awid", "AXI_ID_W"), - ("awlen", 8), - ("awsize", 3), - ("awburst", 2), - ("awlock", 1), - ("awcache", 4), - ("awqos", 4), - ("wlast", 1), -] - -AXI_OUT_SIGNAL_NAMES = [ - ("arready", 1), - ("rdata", "AXI_DATA_W"), - ("rresp", 2), - ("rvalid", 1), - ("rid", "AXI_ID_W"), - ("rlast", 1), - ("awready", 1), - ("wready", 1), - ("bresp", 2), - ("bvalid", 1), - ("bid", "AXI_ID_W"), -] - - -def setup(py_params_dict): - """Wrapper for `axi_interconnect` core. - Python parameters: - - num_slaves: number of slave interfaces - - masters: dictionary with name and address width of each master - """ - # Each generated wrapper must have a unique name (can't have two verilog modules with same name). - assert "name" in py_params_dict, print( - "Error: Missing name for generated interconnect wrapper module." - ) - # Number of slave interfaces (number of masters to connect to) - N_SLAVES = ( - int(py_params_dict["num_slaves"]) if "num_slaves" in py_params_dict else 1 - ) - # Dictionary with name and address width of each master - MASTERS = ( - py_params_dict["masters"] - if "masters" in py_params_dict - else {"m0": "AXI_ADDR_W"} - ) - - attributes_dict = { - "name": py_params_dict["name"], - "version": "0.1", - # - # AXI Parameters - # - "confs": [ - { - "name": "AXI_ID_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI ID bus width", - }, - { - "name": "AXI_ADDR_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI address bus width", - }, - { - "name": "AXI_DATA_W", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": "AXI data bus width", - }, - ], - # - # Ports - # - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Synchronous reset", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - ], - } - slave_axi_ports = [] - for i in range(N_SLAVES): - slave_axi_ports += [ - { - "name": f"s{i}_axi_s", - "descr": f"Slave {i} interface", - "interface": { - "type": "axi", - "subtype": "slave", - "port_prefix": f"s{i}_", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1, - }, - }, - ] - master_axi_ports = [] - master_addr_w_parameter = "" - for name, width in MASTERS.items(): - attributes_dict["confs"].append( - { - "name": f"{name.upper()}_ADDR_W", - "type": "P", - "val": width, - "min": "1", - "max": "32", - "descr": f"{name.upper()} address bus width. Can be smaller than address range of master, but not larger.", - } - ) - master_axi_ports += [ - { - "name": f"{name}_axi_m", - "descr": f"Master '{name}' axi interface", - "interface": { - "type": "axi", - "subtype": "master", - "port_prefix": f"{name}_", - "ID_W": "AXI_ID_W", - "ADDR_W": f"{name.upper()}_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1, - }, - }, - ] - try: - width_str = "32'd" + str(int(width)) - except ValueError: - width_str = width - master_addr_w_parameter = f"{width_str}," + master_addr_w_parameter - master_addr_w_parameter = master_addr_w_parameter[:-1] - if len(MASTERS) > 1: - master_addr_w_parameter = "{" + master_addr_w_parameter + "}" - attributes_dict["ports"] += slave_axi_ports + master_axi_ports - # - # Wires - # - attributes_dict["wires"] = [ - { - "name": "interconnect_s_axi", - "descr": "AXI slave bus for interconnect", - "interface": { - "type": "axi", - "wire_prefix": "intercon_s_", - "mult": N_SLAVES, - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1, - }, - "signals": [ - {"name": "intercon_s_axi_awuser", "width": N_SLAVES}, - {"name": "intercon_s_axi_wuser", "width": N_SLAVES}, - {"name": "intercon_s_axi_aruser", "width": N_SLAVES}, - ], - }, - { - "name": "interconnect_m_axi", - "descr": "AXI master bus for interconnect", - "interface": { - "type": "axi", - "wire_prefix": "intercon_m_", - "mult": len(MASTERS), - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1, - }, - "signals": [ - {"name": "intercon_m_axi_buser", "width": len(MASTERS)}, - {"name": "intercon_m_axi_ruser", "width": len(MASTERS)}, - ], - }, - ] - # - # Blocks - # - attributes_dict["blocks"] = [ - { - "core_name": "axi_interconnect", - "instance_name": "axi_interconnect_core", - "instance_description": "Interconnect core", - "parameters": { - "ID_WIDTH": "AXI_ID_W", - "DATA_WIDTH": "AXI_DATA_W", - "ADDR_WIDTH": "AXI_ADDR_W", - "S_COUNT": N_SLAVES, - "M_COUNT": len(MASTERS), - "M_ADDR_WIDTH": master_addr_w_parameter, - }, - "connect": { - "clk_i": "clk_i", - "rst_i": "rst_i", - "s_axi_s": "interconnect_s_axi", - "m_axi_m": "interconnect_m_axi", - }, - }, - ] - - # Connect all Slave AXI interfaces to interconnect - verilog_code = " // Connect all slave AXI interfaces to interconnect\n" - for sig_name, _ in AXI_IN_SIGNAL_NAMES: - assign_str = "" - for port in slave_axi_ports: - prefix = "" - if "port_prefix" in port["interface"]: - prefix = port["interface"]["port_prefix"] - assign_str = f"{prefix}axi_{sig_name}_i, " + assign_str - assign_str = assign_str[:-2] - verilog_code += ( - f" assign intercon_s_axi_{sig_name} = {{" + assign_str + "};\n" - ) - - for sig_name, sig_size in AXI_OUT_SIGNAL_NAMES: - for idx, port in enumerate(slave_axi_ports): - prefix = "" - if "port_prefix" in port["interface"]: - prefix = port["interface"]["port_prefix"] - bit_select = "" - if type(sig_size) is not int or sig_size > 1: - bit_select = f"[{idx}*{sig_size}+:{sig_size}]" - elif len(slave_axi_ports) > 1: - bit_select = f"[{idx}]" - verilog_code += f" assign {prefix}axi_{sig_name}_o = intercon_s_axi_{sig_name}{bit_select}; \n" - - # Connect all Master AXI interfaces to interconnect - verilog_code += " // Connect all master AXI interfaces to interconnect\n" - for sig_name, _ in AXI_OUT_SIGNAL_NAMES: - assign_str = "" - for master_name in MASTERS: - prefix = f"{master_name}_" - assign_str = f"{prefix}axi_{sig_name}_i, " + assign_str - assign_str = assign_str[:-2] - verilog_code += ( - f" assign intercon_m_axi_{sig_name} = {{" + assign_str + "};\n" - ) - - for sig_name, sig_size in AXI_IN_SIGNAL_NAMES: - for idx, master_name in enumerate(MASTERS): - prefix = f"{master_name}_" - output_size = sig_size - if sig_name.endswith("addr"): - output_size = f"{name.upper()}_ADDR_W" - bit_select = "" - if type(sig_size) is not int or sig_size > 1: - bit_select = f"[{idx}*{sig_size}+:{output_size}]" - elif len(master_axi_ports) > 1: - bit_select = f"[{idx}]" - verilog_code += f" assign {prefix}axi_{sig_name}_o = intercon_m_axi_{sig_name}{bit_select}; \n" - - attributes_dict["snippets"] = [ - { - "verilog_code": verilog_code, - } - ] - - return attributes_dict diff --git a/lib/hardware/buses/axil2iob/axil2iob.py b/lib/hardware/buses/axil2iob/axil2iob.py deleted file mode 100644 index a0a57fd55..000000000 --- a/lib/hardware/buses/axil2iob/axil2iob.py +++ /dev/null @@ -1,48 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "axil_s", - "interface": { - "type": "axil", - "subtype": "slave", - "ADDR_W": "AXIL_ADDR_W", - "DATA_W": "AXIL_DATA_W", - }, - "descr": "AXIL interface", - }, - { - "name": "iob_m", - "interface": { - "type": "iob", - "subtype": "master", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - "descr": "CPU native interface", - }, - ], - "blocks": [ - { - "core_name": "iob_reg_e", - "instance_name": "iob_reg_e_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/axil2iob/hardware/src/axil2iob.v b/lib/hardware/buses/axil2iob/hardware/src/axil2iob.v deleted file mode 100644 index a88328f42..000000000 --- a/lib/hardware/buses/axil2iob/hardware/src/axil2iob.v +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module axil2iob #( - parameter AXIL_ADDR_W = 21, // AXI Lite address bus width in bits - parameter AXIL_DATA_W = 21, // AXI Lite data bus width in bits - parameter ADDR_W = AXIL_ADDR_W, // IOb address bus width in bits - parameter DATA_W = AXIL_DATA_W // IOb data bus width in bits -) ( - `include "axil2iob_io.vs" -); - - localparam WSTRB_W = DATA_W / 8; - - // COMPUTE AXIL OUTPUTS - - // write address channel - assign axil_awready_o = iob_ready_i; - - // write channel - assign axil_wready_o = iob_ready_i; - - // write response - assign axil_bresp_o = 2'b0; - wire axil_bvalid_nxt; - //bvalid will toggle in the two situations below: - assign axil_bvalid_nxt = (|axil_wstrb_i) ? iob_ready_i & iob_valid_o : 1'b0; - - // read address - assign axil_arready_o = iob_ready_i; - - // read channel - assign axil_rresp_o = 2'b0; - - //rvalid - assign axil_rvalid_o = iob_rvalid_i; - - //rdata - assign axil_rdata_o = iob_rdata_i; - - // COMPUTE IOb OUTPUTS - - assign iob_valid_o = (axil_wvalid_i & (|axil_wstrb_i)) | axil_arvalid_i; - assign iob_addr_o = axil_arvalid_i ? axil_araddr_i : axil_awaddr_i; - assign iob_wdata_o = axil_wdata_i; - assign iob_wstrb_o = axil_arvalid_i ? {WSTRB_W{1'b0}} : axil_wstrb_i; - - iob_reg #( - .DATA_W (1), - .RST_VAL(0) - ) iob_reg_bvalid ( - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i(arst_i), - .data_i(axil_bvalid_nxt), - .data_o(axil_bvalid_o) - ); - -endmodule diff --git a/lib/hardware/buses/axil_tasks/axil_tasks.vh b/lib/hardware/buses/axil_tasks/axil_tasks.vh deleted file mode 100644 index 6ffea0cab..000000000 --- a/lib/hardware/buses/axil_tasks/axil_tasks.vh +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -// -// AXI-Lite write and read -// -//axil_address_write(addr, data, width, awvalid, wvalid, awaddr, wdata, wstrb) -task axil_write; - input [AXIL_ADDR_W-1:0] addr; - input [AXIL_DATA_W-1:0] data; - input [$clog2(AXIL_DATA_W):0] width; - - localparam DATA_W = AXIL_DATA_W; - - begin - @(posedge clk) #1 axil_awvalid_i = 1; //sync and assign - axil_wvalid_i = 1; - axil_awaddr_i = `IOB_WORD_ADDR(addr); - axil_wdata_i = `IOB_GET_WDATA(addr, data); - axil_wstrb_i = `IOB_GET_WSTRB(addr, width); - - while (!axil_awready_o) #1; - if (axil_wready_o) begin - @(posedge clk) #1 axil_awvalid_i = 0; - axil_wvalid_i = 0; //awvalid must remain high one cycle before low - end else begin - @(posedge clk) #1 axil_awvalid_i = 0; //awvalid must remain high one cycle before low - while (!axil_wready_o) #1; - @(posedge clk) #1 axil_wvalid_i = 0; - end - end -endtask - - -//axil_read (addr, data, width) -task axil_read; - input [AXIL_ADDR_W-1:0] addr; - output [AXIL_DATA_W-1:0] data; - input [$clog2(AXIL_DATA_W):0] width; - - localparam DATA_W = AXIL_DATA_W; - - begin - @(posedge clk) #1 axil_arvalid_i = 1; //sync and assign - axil_araddr_i = `IOB_WORD_ADDR(addr); - axil_wstrb_i = 0; - - while (!axil_arready_o) #1; - @(posedge clk) #1 axil_arvalid_i = 0; //arvalid must remain high one cycle before low - - while (!axil_rvalid_o) #1; - data = `IOB_GET_RDATA(addr, axil_rdata_o, width); //sample data - @(posedge clk) #1; - end -endtask diff --git a/lib/hardware/buses/axis2axi/axis2axi.py b/lib/hardware/buses/axis2axi/axis2axi.py deleted file mode 100644 index c04470ea2..000000000 --- a/lib/hardware/buses/axis2axi/axis2axi.py +++ /dev/null @@ -1,234 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "rst_i", - "descr": "Synchronous reset interface", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "config_in", - "descr": "AXI Stream input configuration interface", - "signals": [ - { - "name": "config_in_addr", - "direction": "input", - "width": "ADDR_W", - "descr": "", - }, - { - "name": "config_in_valid", - "direction": "input", - "width": 1, - "descr": "", - }, - { - "name": "config_in_ready", - "direction": "output", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "config_out", - "descr": "AXI Stream output configuration interface", - "signals": [ - { - "name": "config_out_addr", - "direction": "input", - "width": "ADDR_W", - "descr": "", - }, - { - "name": "config_out_length", - "direction": "input", - "width": "ADDR_W", - "descr": "", - }, - { - "name": "config_out_valid", - "direction": "input", - "width": 1, - "descr": "", - }, - { - "name": "config_out_ready", - "direction": "output", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "axis_in", - "descr": "AXI Stream input interface", - "signals": [ - { - "name": "axis_in_data", - "direction": "input", - "width": "DATA_W", - "descr": "", - }, - { - "name": "axis_in_valid", - "direction": "input", - "width": 1, - "descr": "", - }, - { - "name": "axis_in_ready", - "direction": "output", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "axis_out", - "descr": "AXI Stream output interface", - "signals": [ - { - "name": "axis_out_data", - "direction": "output", - "width": "DATA_W", - "descr": "", - }, - { - "name": "axis_out_valid", - "direction": "output", - "width": 1, - "descr": "", - }, - { - "name": "axis_out_ready", - "direction": "input", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "axi_m", - "interface": { - "type": "axi", - "subtype": "master", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - "descr": "AXI master interface", - }, - { - "name": "extmem", - "descr": "External memory interface", - "signals": [ - { - "name": "ext_mem_w_en", - "direction": "output", - "width": 1, - "descr": "Memory write enable", - }, - { - "name": "ext_mem_w_addr", - "direction": "output", - "width": "BUFFER_W", - "descr": "Memory write address", - }, - { - "name": "ext_mem_w_data", - "direction": "output", - "width": "DATA_W", - "descr": "Memory write data", - }, - { - "name": "ext_mem_r_en", - "direction": "output", - "width": 1, - "descr": "Memory read enable", - }, - { - "name": "ext_mem_r_addr", - "direction": "output", - "width": "BUFFER_W", - "descr": "Memory read address", - }, - { - "name": "ext_mem_r_data", - "direction": "input", - "width": "DATA_W", - "descr": "Memory read data", - }, - ], - }, - # Not real ports of axis2axi - # { - # "name": "axi_write", - # "descr": "AXI write interface", - # "signals": [], - # }, - # { - # "name": "axi_read", - # "descr": "AXI read interface", - # "signals": [], - # }, - ], - "blocks": [ - { - "core_name": "iob_fifo_sync", - "instance_name": "iob_fifo_sync_inst", - }, - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - }, - { - "core_name": "iob_reg_r", - "instance_name": "iob_reg_r_inst", - }, - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - # For simulation - { - "core_name": "axi_ram", - "instance_name": "axi_ram_inst", - }, - { - "core_name": "iob_ram_at2p", - "instance_name": "iob_ram_at2p_inst", - }, - { - "core_name": "axis2axi_in", - "instance_name": "axis2axi_in_inst", - }, - { - "core_name": "axis2axi_out", - "instance_name": "axis2axi_out_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/axis2axi/hardware/simulation/src/axidelay.v b/lib/hardware/buses/axis2axi/hardware/simulation/src/axidelay.v deleted file mode 100644 index 4bdcbb84a..000000000 --- a/lib/hardware/buses/axis2axi/hardware/simulation/src/axidelay.v +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -// A generic axi like handshake delay for testbenches. -// For axi, the write and read cases are "different" in name usage. -// Use the modules below for axi connections -module axidelay #( - parameter MAX_DELAY = 3 -) ( - // Master interface. Connect to a slave interface - output reg m_valid_o, - input m_ready_i, - - // Slave interface. Connect to a master interface - input s_valid_i, - output reg s_ready_o, - - input clk_i, - input rst_i -); - - generate - if (MAX_DELAY == 0) begin - always @* begin - s_ready_o = m_ready_i; - m_valid_o = s_valid_i; - end - end else begin - reg [$clog2(MAX_DELAY):0] counter; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - counter <= 0; - end else begin - if (counter == 0 && m_valid_o && m_ready_i) begin - counter <= ($urandom % MAX_DELAY); - end - - if (counter) counter <= counter - 1; - end - end - - always @* begin - s_ready_o = 1'b0; - m_valid_o = 1'b0; - - if (counter == 0) begin - s_ready_o = m_ready_i; - m_valid_o = s_valid_i; - end - end - end - endgenerate - -endmodule - -// A simple interface change, make it easier to figure out the connections for the AXI Read case -// An AXI read is controlled by the slave. The AXI slave is the master of the Read channel -module axidelayRead #( - parameter MAX_DELAY = 3 -) ( - // Connect directly to the same named axi read wires in the master interface - output m_rvalid_o, - input m_rready_i, - - // Connect directly to the same named axi read wires in the slave interface - input s_rvalid_i, - output s_rready_o, - - input clk_i, - input rst_i -); - - axidelay #( - .MAX_DELAY(MAX_DELAY) - ) Read ( - .s_valid_i(s_rvalid_i), - .s_ready_o(s_rready_o), - - .m_valid_o(m_rvalid_o), - .m_ready_i(m_rready_i), - - .clk_i(clk_i), - .rst_i(rst_i) - ); - -endmodule - -// A simple interface change, make it easier to figure out the connections for the AXI Write case -// An AXI write is controlled by the master. No change to the default handshake -module axidelayWrite #( - parameter MAX_DELAY = 3 -) ( - // Connect directly to the same named axi write wires in the master interface - input m_wvalid_i, - output m_wready_o, - - // Connect directly to the same named axi write wires in the slave interface - output s_wvalid_o, - input s_wready_i, - - input clk_i, - input rst_i -); - - axidelay #( - .MAX_DELAY(MAX_DELAY) - ) Write ( - .s_valid_i(m_wvalid_i), - .s_ready_o(m_wready_o), - - .m_valid_o(s_wvalid_o), - .m_ready_i(s_wready_i), - - .clk_i(clk_i), - .rst_i(rst_i) - ); - -endmodule diff --git a/lib/hardware/buses/axis2axi/hardware/simulation/src/axis2axi_tb.v b/lib/hardware/buses/axis2axi/hardware/simulation/src/axis2axi_tb.v deleted file mode 100644 index 665aebc6e..000000000 --- a/lib/hardware/buses/axis2axi/hardware/simulation/src/axis2axi_tb.v +++ /dev/null @@ -1,524 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - - -`define CLK_PER 10 - -//`define AXIS_2_AXI_MANUAL_TB 1 - -module axis2axi_tb; - - // Change this parameters between tests to check. - // - parameter ADDR_W = 24; - parameter DATA_W = 32; - parameter BURST_W = - 2; // Change burst size. A BURST_W of 0 is allowed, the AXI interface sends one value at a time - - // Change this parameters to add a delay, either to the AXI stream or to the AXI connection (0 is valid and will not add any delay) - parameter DELAY_AXIS_IN = 3; - parameter DELAY_AXIS_OUT = 3; - parameter DELAY_AXI_READ = 5; - parameter DELAY_AXI_WRITE = 5; - - // Do not change these - parameter AXI_LEN_W = 8; - parameter AXI_ID_W = 1; - - // Clock - reg clk = 1; - always #(`CLK_PER / 2) clk = ~clk; - - // Reset - reg rst = 0; - - // Control I/F - reg [ADDR_W-1:0] config_in_addr; - reg [ADDR_W-1:0] config_out_addr; - reg [ADDR_W-1:0] config_out_length; - - reg config_in_valid; - reg config_out_valid; - wire config_in_ready; - wire config_out_ready; - - // AXI Stream in - reg [DATA_W-1:0] axis_in_data; - reg axis_in_valid; - wire axis_in_ready; - - // AXI Stream out - wire [DATA_W-1:0] axis_out_data; - wire non_delayed_axis_out_valid; - wire non_delayed_axis_out_ready; - - wire delayed_axis_out_valid; - reg delayed_axis_out_ready; - - // AXI-4 full master I/F - wire ddr_axi_awid; //Address write channel ID - wire [ADDR_W-1:0] ddr_axi_awaddr; //Address write channel address - wire [8-1:0] ddr_axi_awlen; //Address write channel burst length - wire [3-1:0] ddr_axi_awsize - ; //Address write channel burst size. This signal indicates the size of each transfer in the burst - wire [2-1:0] ddr_axi_awburst; //Address write channel burst type - wire [2-1:0] ddr_axi_awlock; //Address write channel lock type - wire [4-1:0] ddr_axi_awcache - ; //Address write channel memory type. Transactions set with Normal Non-cacheable Modifiable and Bufferable (0011). - wire [3-1:0] ddr_axi_awprot - ; //Address write channel protection type. Transactions set with Normal, Secure, and Data attributes (000). - wire [4-1:0] ddr_axi_awqos; //Address write channel quality of service - wire ddr_axi_awvalid; //Address write channel valid - wire ddr_axi_awready; //Address write channel ready - wire ddr_axi_wid; //Write channel ID - wire [DATA_W-1:0] ddr_axi_wdata; //Write channel data - wire [(DATA_W/8)-1:0] ddr_axi_wstrb; //Write channel write strobe - wire ddr_axi_wlast; //Write channel last word flag - wire ddr_axi_bid; //Write response channel ID - wire [2-1:0] ddr_axi_bresp; //Write response channel response - wire ddr_axi_bvalid; //Write response channel valid - wire ddr_axi_bready; //Write response channel ready - wire ddr_axi_arid; //Address read channel ID - wire [ADDR_W-1:0] ddr_axi_araddr; //Address read channel address - wire [8-1:0] ddr_axi_arlen; //Address read channel burst length - wire [3-1:0] ddr_axi_arsize - ; //Address read channel burst size. This signal indicates the size of each transfer in the burst - wire [2-1:0] ddr_axi_arburst; //Address read channel burst type - wire [2-1:0] ddr_axi_arlock; //Address read channel lock type - wire [4-1:0] ddr_axi_arcache - ; //Address read channel memory type. Transactions set with Normal Non-cacheable Modifiable and Bufferable (0011). - wire [3-1:0] ddr_axi_arprot - ; //Address read channel protection type. Transactions set with Normal, Secure, and Data attributes (000). - wire [4-1:0] ddr_axi_arqos; //Address read channel quality of service - wire ddr_axi_arvalid; //Address read channel valid - wire ddr_axi_arready; //Address read channel ready - wire ddr_axi_rid; //Read channel ID - wire [DATA_W-1:0] ddr_axi_rdata; //Read channel data - wire [2-1:0] ddr_axi_rresp; //Read channel response - wire ddr_axi_rlast; //Read channel last word - - // Iterators - integer i; - integer fd; - - initial begin - -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // - // Init signals - // - i = 0; - config_in_addr = 0; - config_out_addr = 0; - config_out_length = 0; - config_in_valid = 0; - config_out_valid = 0; - - axis_in_data = 0; - axis_in_valid = 0; - delayed_axis_out_ready = 0; - - // Assert reset - #100 rst = 1; - - // Deassert rst - repeat (10) @(posedge clk) #1; - rst = 0; - - // Wait an arbitray (10) number of cycles - repeat (10) @(posedge clk) #1; - - // Axi In Tests - AxiStreamInRun(16'h0000, 0, 4); - AxiStreamOutRun(16'h0000, 4); - - - AxiStreamInRun(16'h0100, 0, 16); - AxiStreamOutRun(16'h0100, 16); - - AxiStreamInRun(16'h0ffc, 0, 1); - AxiStreamOutRun(16'h0ffc, 1); - - AxiStreamInRun(16'h1ffc, 0, 2); - AxiStreamOutRun(16'h1ffc, 2); - - AxiStreamInRun(16'h2ffc, 0, 10); - AxiStreamOutRun(16'h2ffc, 10); - - AxiStreamInRun(16'h3fd8, 0, 40); - AxiStreamOutRun(16'h3fd8, 40); - - AxiStreamOutRun(16'h5000, 0); // A zero out run should produce no value - - repeat (100) @(posedge clk) #1; - - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - - repeat (10) @(posedge clk) #1; - - $finish(); - end - - task AxiStreamInRun(input [31:0] address, startValue, runLength); - begin -`ifdef AXIS_2_AXI_MANUAL_TB - $display("Making an AXI Stream In run from %h to %h", address, address + runLength * 4); -`endif - - config_in_addr = address; - config_in_valid = 1; - - while (!config_in_ready) @(posedge clk) #1; - - @(posedge clk) #1; - - config_in_valid = 0; - - @(posedge clk) #1; - - axis_in_valid = 1; - axis_in_data = startValue; - for (i = 0; i < runLength; i = i + 1) begin - while (!axis_in_ready) @(posedge clk) #1; - - if (axis_in_data != i) begin - $display("Error on run from %h to %h, index: %d", address, address + runLength * 4, - i); - $fatal(); - end - - @(posedge clk) #1; - axis_in_data = axis_in_data + 1; - end - axis_in_valid = 0; - - repeat (100) @(posedge clk) #1; - -`ifdef AXIS_2_AXI_MANUAL_TB - $display(""); -`endif - end - endtask - - integer readIndex; - task AxiStreamOutRun(input [31:0] address, runLength); - begin -`ifdef AXIS_2_AXI_MANUAL_TB - $display("Making an AXI Stream Out run from %h to %h", address, address + runLength * 4); -`endif - - config_out_addr = address; - config_out_length = runLength; - config_out_valid = 1; - - while (!config_out_ready) @(posedge clk) #1; - - @(posedge clk) #1; - - config_out_valid = 0; - - @(posedge clk) #1; - - delayed_axis_out_ready = 1; - readIndex = 0; -`ifdef AXIS_2_AXI_MANUAL_TB - $write("Values read:"); -`endif - while (!config_out_ready) begin - if (delayed_axis_out_valid) begin - if (axis_out_data != readIndex) begin - //$write("Error on run from %h to %h,index: %d",address,address + runLength * 4,readIndex); - //$fatal(); - end - readIndex = readIndex + 1; -`ifdef AXIS_2_AXI_MANUAL_TB - $write(" %02d", axis_out_data); -`endif - end - - @(posedge clk) #1; - end - - delayed_axis_out_ready = 0; - repeat (100) @(posedge clk) #1; -`ifdef AXIS_2_AXI_MANUAL_TB - $display("\n"); -`endif - end - endtask - -`ifdef AXIS_2_AXI_MANUAL_TB - // Detect writes and store them for display - reg [ 4:0] writeCounter; - reg [31:0] writtenData [7:0]; - reg [23:0] writtenAddr; - integer writeCounterIndex; - always @(posedge clk) begin - if (ddr_axi_awvalid && ddr_axi_awready) begin - writeCounter = 0; - writtenAddr = ddr_axi_awaddr; - end - - if (s_wvalid && s_wready) begin - writtenData[writeCounter] = ddr_axi_wdata; - writeCounter = writeCounter + 1; - if (ddr_axi_wlast) begin - $write("Written to address %h:", ddr_axi_awaddr); - for ( - writeCounterIndex = 0; - writeCounterIndex < writeCounter; - writeCounterIndex = writeCounterIndex + 1 - ) begin - $write(" %02d", writtenData[writeCounterIndex]); - end - $display(""); - writeCounter = 0; - end - end - end -`endif - - // External memory instantiation - wire ext_mem_w_en, ext_mem_r_en; - wire [31:0] ext_mem_w_data, ext_mem_r_data; - wire [BURST_W:0] ext_mem_w_addr, ext_mem_r_addr; - - iob_ram_at2p #( - .DATA_W(32), - .ADDR_W(BURST_W + 1) - ) memory ( - .w_clk_i (clk), - .w_en_i (ext_mem_w_en), - .w_addr_i(ext_mem_w_addr), - .w_data_i(ext_mem_w_data), - - .r_clk_i (clk), - .r_en_i (ext_mem_r_en), - .r_addr_i(ext_mem_r_addr), - .r_data_o(ext_mem_r_data) - ); - - // Insert delays between AXI like handshake interfaces - wire m_rvalid, m_rready, s_rvalid, s_rready; - axidelayRead #( - .MAX_DELAY(DELAY_AXI_READ) - ) delayRead ( - // Connect directly to the same named axi read wires in the master interface - .m_rvalid_o(m_rvalid), - .m_rready_i(m_rready), - - // Connect directly to the same named axi read wires in the slave interface - .s_rvalid_i(s_rvalid), - .s_rready_o(s_rready), - - .clk_i(clk), - .rst_i(rst) - ); - - wire m_wvalid, m_wready, s_wvalid, s_wready; - axidelayWrite #( - .MAX_DELAY(DELAY_AXI_WRITE) - ) delayWrite ( - // Connect directly to the same named axi write wires in the master interface - .m_wvalid_i(m_wvalid), - .m_wready_o(m_wready), - - // Connect directly to the same named axi write wires in the slave interface - .s_wvalid_o(s_wvalid), - .s_wready_i(s_wready), - - .clk_i(clk), - .rst_i(rst) - ); - - wire delayed_axis_in_valid, delayed_axis_in_ready; - axidelay #( - .MAX_DELAY(DELAY_AXIS_IN) - ) delayIn ( - // Master interface. Connect to a slave interface - .m_valid_o(delayed_axis_in_valid), - .m_ready_i(delayed_axis_in_ready), - - // Slave interface. Connect to a master interface - .s_valid_i(axis_in_valid), - .s_ready_o(axis_in_ready), - - .clk_i(clk), - .rst_i(rst) - ); - - axidelay #( - .MAX_DELAY(DELAY_AXIS_OUT) - ) delayOut ( - // Master interface. Connect to a slave interface - .m_valid_o(delayed_axis_out_valid), - .m_ready_i(delayed_axis_out_ready), - - // Slave interface. Connect to a master interface - .s_valid_i(non_delayed_axis_out_valid), - .s_ready_o(non_delayed_axis_out_ready), - - .clk_i(clk), - .rst_i(rst) - ); - - axis2axi #( - .ADDR_W(ADDR_W), - .DATA_W(DATA_W), - .AXI_LEN_W (AXI_LEN_W), - .AXI_ID_W (AXI_ID_W), - .BURST_W (BURST_W) - ) uut ( - // Memory interface - .ext_mem_w_en_o (ext_mem_w_en), - .ext_mem_w_data_o(ext_mem_w_data), - .ext_mem_w_addr_o(ext_mem_w_addr), - .ext_mem_r_en_o (ext_mem_r_en), - .ext_mem_r_addr_o(ext_mem_r_addr), - .ext_mem_r_data_i(ext_mem_r_data), - - // - // Control I/F - // - .config_in_addr_i (config_in_addr), - .config_in_valid_i(config_in_valid), - .config_in_ready_o(config_in_ready), - - .config_out_addr_i (config_out_addr), - .config_out_length_i(config_out_length), - .config_out_valid_i (config_out_valid), - .config_out_ready_o (config_out_ready), - - // AXI Stream In - .axis_in_data_i (axis_in_data), - .axis_in_valid_i(delayed_axis_in_valid), - .axis_in_ready_o(delayed_axis_in_ready), - - // AXI Stream Out - .axis_out_data_o (axis_out_data), - .axis_out_valid_o(non_delayed_axis_out_valid), - .axis_out_ready_i(non_delayed_axis_out_ready), - - // - // AXI-4 full master I/F - // - .axi_awid_o(ddr_axi_awid), //Address write channel ID - .axi_awaddr_o(ddr_axi_awaddr), //Address write channel address - .axi_awlen_o(ddr_axi_awlen), //Address write channel burst length - .axi_awsize_o(ddr_axi_awsize), //Address write channel burst size. This signal indicates the size of each transfer in the burst - .axi_awburst_o(ddr_axi_awburst), //Address write channel burst type - .axi_awlock_o(ddr_axi_awlock), //Address write channel lock type - .axi_awcache_o(ddr_axi_awcache), //Address write channel memory type. Transactions set with Normal Non-cacheable Modifiable and Bufferable (0011). - .axi_awprot_o(ddr_axi_awprot), //Address write channel protection type. Transactions set with Normal, Secure, and Data attributes (000). - .axi_awqos_o(ddr_axi_awqos), //Address write channel quality of service - .axi_awvalid_o(ddr_axi_awvalid), //Address write channel valid - .axi_awready_i(ddr_axi_awready), //Address write channel ready - //.axi_wid_o(ddr_axi_wid), //Write channel ID - .axi_wdata_o(ddr_axi_wdata), //Write channel data - .axi_wstrb_o(ddr_axi_wstrb), //Write channel write strobe - .axi_wlast_o(ddr_axi_wlast), //Write channel last word flag - .axi_wvalid_o(m_wvalid), //Write channel valid - .axi_wready_i(m_wready), //Write channel ready - .axi_bid_i(ddr_axi_bid), //Write response channel ID - .axi_bresp_i(ddr_axi_bresp), //Write response channel response - .axi_bvalid_i(ddr_axi_bvalid), //Write response channel valid - .axi_bready_o(ddr_axi_bready), //Write response channel ready - .axi_arid_o(ddr_axi_arid), //Address read channel ID - .axi_araddr_o(ddr_axi_araddr), //Address read channel address - .axi_arlen_o(ddr_axi_arlen), //Address read channel burst length - .axi_arsize_o(ddr_axi_arsize), //Address read channel burst size. This signal indicates the size of each transfer in the burst - .axi_arburst_o(ddr_axi_arburst), //Address read channel burst type - .axi_arlock_o(ddr_axi_arlock), //Address read channel lock type - .axi_arcache_o(ddr_axi_arcache), //Address read channel memory type. Transactions set with Normal Non-cacheable Modifiable and Bufferable (0011). - .axi_arprot_o(ddr_axi_arprot), //Address read channel protection type. Transactions set with Normal, Secure, and Data attributes (000). - .axi_arqos_o(ddr_axi_arqos), //Address read channel quality of service - .axi_arvalid_o(ddr_axi_arvalid), //Address read channel valid - .axi_arready_i(ddr_axi_arready), //Address read channel ready - .axi_rid_i(ddr_axi_rid), //Read channel ID - .axi_rdata_i(ddr_axi_rdata), //Read channel data - .axi_rresp_i(ddr_axi_rresp), //Read channel response - .axi_rlast_i(ddr_axi_rlast), //Read channel last word - .axi_rvalid_i(m_rvalid), //Read channel valid - .axi_rready_o(m_rready), //Read channel ready - - .clk_i (clk), - .cke_i (1'b1), - .rst_i (rst), - .arst_i(1'b0) - ); - - axi_ram #( - .ID_WIDTH (1), - .DATA_WIDTH(DATA_W), - .ADDR_WIDTH(ADDR_W) - ) axi_ram0 ( - .clk_i(clk), - .rst_i(rst), - - // - // AXI-4 full master interface - // - - // Address write - .axi_awid_i (ddr_axi_awid), - .axi_awaddr_i (ddr_axi_awaddr), - .axi_awlen_i (ddr_axi_awlen), - .axi_awsize_i (ddr_axi_awsize), - .axi_awburst_i(ddr_axi_awburst), - .axi_awlock_i (ddr_axi_awlock), - .axi_awprot_i (ddr_axi_awprot), - .axi_awqos_i (ddr_axi_awqos), - .axi_awcache_i(ddr_axi_awcache), - .axi_awvalid_i(ddr_axi_awvalid), - .axi_awready_o(ddr_axi_awready), - - // Write - .axi_wvalid_i(s_wvalid), - .axi_wdata_i (ddr_axi_wdata), - .axi_wstrb_i (ddr_axi_wstrb), - .axi_wlast_i (ddr_axi_wlast), - .axi_wready_o(s_wready), - - // Write response - .axi_bid_o (ddr_axi_bid), - .axi_bvalid_o(ddr_axi_bvalid), - .axi_bresp_o (ddr_axi_bresp), - .axi_bready_i(ddr_axi_bready), - - // Address read - .axi_arid_i (ddr_axi_arid), - .axi_araddr_i (ddr_axi_araddr), - .axi_arlen_i (ddr_axi_arlen), - .axi_arsize_i (ddr_axi_arsize), - .axi_arburst_i(ddr_axi_arburst), - .axi_arlock_i (ddr_axi_arlock), - .axi_arcache_i(ddr_axi_arcache), - .axi_arprot_i (ddr_axi_arprot), - .axi_arqos_i (ddr_axi_arqos), - .axi_arvalid_i(ddr_axi_arvalid), - .axi_arready_o(ddr_axi_arready), - - // Read - .axi_rid_o (ddr_axi_rid), - .axi_rvalid_o(s_rvalid), - .axi_rdata_o (ddr_axi_rdata), - .axi_rlast_o (ddr_axi_rlast), - .axi_rresp_o (ddr_axi_rresp), - .axi_rready_i(s_rready) - ); - -endmodule diff --git a/lib/hardware/buses/axis2axi/hardware/src/axis2axi.v b/lib/hardware/buses/axis2axi/hardware/src/axis2axi.v deleted file mode 100644 index 179797d99..000000000 --- a/lib/hardware/buses/axis2axi/hardware/src/axis2axi.v +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -/* Important: This unit has not been tested on a FPGA. Only simulation. Take care - -Simple AXI Stream (AXIS) to AXI adapter - This unit breaks down an AXIS into multiple bursts of AXI. - For AXIS In, this is performed transparently. For AXIS Out, a length argument is required. - Address (and length) are set by using the config_in or config_out interfaces. They both use a AXI like handshake process [if (valid && ready) == 1 then start transfer]. - The config_ready interfaces can also be used directly to probe the state of the transfer. When asserted, they indicate that the unit has finished all the data transfers. - Both AXIS In and AXIS Out operate individually and can work simultaneously (these units can also be instantiated individually, check axis2axi_in.v and axis2axi_out.v) - 4k boundaries are handled automatically. - -AXIS In: - After configuring the config_in values, the axis_in interface can be used. There is no limit to the amount of data that can be sent. - -AXIS Out: - After configuring the config_out values, the unit will start producing data in the axis_out interface. - Length is given as the amount of dwords. A length of 1 means that one transfer is performed. (A length of zero does nothing) - If the axis_out interface is stalled permanently before completing the full transfer, the unit might block the entire system, as it will continue to keep the AXI connection alive. - If for some reason the user realises that it requested a length bigger then need, the user still needs to keep consuming data out of the axis_out interface. Only when config_out_ready_o is asserted has the transfer fully completed - -Very important: if the transfer goes over the maximum size, given by AXI_ADDR_W, the transfer will wrap around and will start reading/writing to the lower addresses. -*/ - -module axis2axi #( - parameter ADDR_W = 0, - parameter DATA_W = 32, // We currently only support 4 byte transfers - parameter AXI_LEN_W = 8, - parameter AXI_ID_W = 1, - parameter BURST_W = 0, - parameter BUFFER_W = BURST_W + 1 -) ( - `include "axis2axi_io.vs" -); - - axis2axi_in #( - .AXI_ADDR_W(ADDR_W), - .AXI_DATA_W(DATA_W), - .AXI_LEN_W (AXI_LEN_W), - .AXI_ID_W (AXI_ID_W), - .BURST_W (BURST_W) - ) axis2axi_in_inst ( - .config_in_addr_i (config_in_addr_i), - .config_in_valid_i(config_in_valid_i), - .config_in_ready_o(config_in_ready_o), - - .ext_mem_w_en_o (ext_mem_w_en_o), - .ext_mem_w_data_o(ext_mem_w_data_o), - .ext_mem_w_addr_o(ext_mem_w_addr_o), - .ext_mem_r_en_o (ext_mem_r_en_o), - .ext_mem_r_addr_o(ext_mem_r_addr_o), - .ext_mem_r_data_i(ext_mem_r_data_i), - - .axis_in_data_i (axis_in_data_i), - .axis_in_valid_i(axis_in_valid_i), - .axis_in_ready_o(axis_in_ready_o), - - `include "axis2axi_in_axi_write_m_m_portmap.vs" - - .clk_i (clk_i), - .cke_i (cke_i), - .rst_i (rst_i), - .arst_i(arst_i) - ); - - axis2axi_out #( - .AXI_ADDR_W(ADDR_W), - .AXI_DATA_W(DATA_W), - .AXI_LEN_W (AXI_LEN_W), - .AXI_ID_W (AXI_ID_W), - .BURST_W (BURST_W) - ) axis2axi_out_inst ( - .config_out_addr_i (config_out_addr_i), - .config_out_length_i(config_out_length_i), - .config_out_valid_i (config_out_valid_i), - .config_out_ready_o (config_out_ready_o), - - .axis_out_data_o (axis_out_data_o), - .axis_out_valid_o(axis_out_valid_o), - .axis_out_ready_i(axis_out_ready_i), - - `include "axis2axi_out_axi_read_m_m_portmap.vs" - - .clk_i (clk_i), - .cke_i (cke_i), - .rst_i (rst_i), - .arst_i(arst_i) - ); - -endmodule diff --git a/lib/hardware/buses/axis2axi/submodules/axis2axi_in/axis2axi_in.py b/lib/hardware/buses/axis2axi/submodules/axis2axi_in/axis2axi_in.py deleted file mode 100644 index 94c363043..000000000 --- a/lib/hardware/buses/axis2axi/submodules/axis2axi_in/axis2axi_in.py +++ /dev/null @@ -1,135 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "rst_i", - "descr": "Synchronous reset interface", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "config_in", - "descr": "AXI Stream input configuration interface", - "signals": [ - { - "name": "config_in_addr", - "direction": "input", - "width": "AXI_ADDR_W", - "descr": "", - }, - { - "name": "config_in_valid", - "direction": "input", - "width": 1, - "descr": "", - }, - { - "name": "config_in_ready", - "direction": "output", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "axis_in", - "descr": "AXI Stream input interface", - "signals": [ - { - "name": "axis_in_data", - "direction": "input", - "width": "AXI_DATA_W", - "descr": "", - }, - { - "name": "axis_in_valid", - "direction": "input", - "width": 1, - "descr": "", - }, - { - "name": "axis_in_ready", - "direction": "output", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "axi_write_m", - "interface": { - "type": "axi_write", - "subtype": "master", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - }, - "descr": "AXI write interface", - }, - { - "name": "extmem", - "descr": "External memory interface", - "signals": [ - { - "name": "ext_mem_w_en", - "direction": "output", - "width": 1, - "descr": "Memory write enable", - }, - { - "name": "ext_mem_w_addr", - "direction": "output", - "width": "BUFFER_W", - "descr": "Memory write address", - }, - { - "name": "ext_mem_w_data", - "direction": "output", - "width": "AXI_DATA_W", - "descr": "Memory write data", - }, - { - "name": "ext_mem_r_en", - "direction": "output", - "width": 1, - "descr": "Memory read enable", - }, - { - "name": "ext_mem_r_addr", - "direction": "output", - "width": "BUFFER_W", - "descr": "Memory read address", - }, - { - "name": "ext_mem_r_data", - "direction": "input", - "width": "AXI_DATA_W", - "descr": "Memory read data", - }, - ], - }, - ], - "blocks": [], - } - - return attributes_dict diff --git a/lib/hardware/buses/axis2axi/submodules/axis2axi_in/hardware/src/axis2axi_in.v b/lib/hardware/buses/axis2axi/submodules/axis2axi_in/hardware/src/axis2axi_in.v deleted file mode 100644 index d1737ea3e..000000000 --- a/lib/hardware/buses/axis2axi/submodules/axis2axi_in/hardware/src/axis2axi_in.v +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - - -// Check axis2axi.v for information on how this unit works - -module axis2axi_in #( - parameter AXI_ADDR_W = 0, - parameter AXI_DATA_W = 32, // We currently only support 4 byte transfers - parameter AXI_LEN_W = 8, - parameter AXI_ID_W = 1, - parameter BURST_W = 0 -) ( - `include "axis2axi_in_io.vs" -); - - localparam BURST_SIZE = 2 ** BURST_W; - localparam BUFFER_W = BURST_W + 1; - localparam BUFFER_SIZE = 2 ** BUFFER_W; - - localparam WAIT_DATA = 2'h0, START_TRANSFER = 2'h1, TRANSFER = 2'h2, WAIT_BRESP = 2'h3; - - // Constants - assign axi_awid_o = 0; - assign axi_awsize_o = 2; - assign axi_awburst_o = 1; - assign axi_awlock_o = 0; - assign axi_awcache_o = 2; - assign axi_awprot_o = 2; - assign axi_awqos_o = 0; - assign axi_wstrb_o = 4'b1111; - assign axi_bready_o = 1'b1; - - // State regs - reg awvalid_int; - reg wvalid_int; - reg [ 1:0] state_nxt; - reg [AXI_ADDR_W-1:0] next_address; - - // Instantiation wires - wire [ 1:0] state; - wire [BURST_SIZE-1:0] transfer_count; - wire [AXI_ADDR_W-1:0] current_address; - wire [AXI_DATA_W-1:0] fifo_data; - wire fifo_empty, fifo_full; - wire [BUFFER_W:0] fifo_level; - wire [BURST_W:0] awlen_int; - - // Logical wires - wire doing_transfer = (state != WAIT_DATA); - wire normal_burst_possible = (fifo_level >= BURST_SIZE); - wire last_burst_possible = (fifo_level > 0 && fifo_level < BURST_SIZE && !axis_in_valid_i); - wire start_transfer = (normal_burst_possible || last_burst_possible) && !doing_transfer; - wire transfer = axi_wready_i && axi_wvalid_o; - wire read_next = (transfer && !axi_wlast_o); - wire last_transfer = (transfer_count == axi_awlen_o); - wire fifo_read_enable = (read_next || start_transfer) - ; // Start_transfer puts the first valid data on r_data and offsets fifo read by one cycle which lines up perfectly with the way the m_axi_wready signal works - - wire [BURST_W:0] burst_size; - - reg [BURST_W:0] non_boundary_burst_size; - always @* begin - non_boundary_burst_size = 0; - - if (last_burst_possible) begin - non_boundary_burst_size = fifo_level; - end - if (normal_burst_possible) non_boundary_burst_size = BURST_SIZE; - end - - generate - if (AXI_ADDR_W >= 13) begin // 4k boundary can only happen to LEN higher or equal to 13 - - wire [12:0] boundary_transfer_len = (13'h1000 - current_address[11:0]) >> 2; - - reg [BURST_W:0] boundary_burst_size; - always @* begin - boundary_burst_size = non_boundary_burst_size; - - if (non_boundary_burst_size > boundary_transfer_len) - boundary_burst_size = boundary_transfer_len; - end - - assign burst_size = boundary_burst_size; - - end else begin - assign burst_size = non_boundary_burst_size; - end - endgenerate - - wire [BURST_W:0] transfer_len = burst_size - 1; - - // Assignment to outputs - assign axi_awvalid_o = awvalid_int; - assign axi_awaddr_o = current_address; - assign axi_awlen_o = awlen_int; - assign axi_wvalid_o = wvalid_int; - assign axi_wdata_o = fifo_data; - assign axi_wlast_o = last_transfer; - - assign axis_in_ready_o = !fifo_full; - assign config_in_ready_o = (fifo_empty && state == WAIT_DATA); - - // Registers port logic - reg transfer_count_reg_rst; - reg transfer_count_reg_en; - reg axi_length_reg_en; - - // State machine - always @* begin - state_nxt = state; - awvalid_int = 1'b0; - wvalid_int = 1'b0; - next_address = current_address; - - transfer_count_reg_rst = 1'b0; - transfer_count_reg_en = 1'b0; - axi_length_reg_en = 1'b0; - - if (config_in_valid_i) next_address = config_in_addr_i; - - case (state) - WAIT_DATA: begin - if (start_transfer) begin - state_nxt = START_TRANSFER; - axi_length_reg_en = 1'b1; - end - transfer_count_reg_rst = 1'b1; - end - START_TRANSFER: begin - awvalid_int = 1'b1; - if (axi_awready_i) state_nxt = TRANSFER; - end - TRANSFER: begin - wvalid_int = 1'b1; // Since we can only send a burst of less or equal to the FIFO level, we can set m_axi_wvalid to 1. We always have a value to send - if (transfer && axi_wlast_o) begin - next_address = current_address + ((axi_awlen_o + 1) << 2); - state_nxt = WAIT_BRESP; - end - transfer_count_reg_en = transfer; - end - WAIT_BRESP: - if (axi_bvalid_i) begin - state_nxt = WAIT_DATA; - end - endcase - end - - iob_counter #(BURST_SIZE, 0) transfer_count_reg ( - `include "axis2axi_in_clk_en_rst_s_s_portmap.vs" - .rst_i (transfer_count_reg_rst), - .en_i (transfer_count_reg_en), - .data_o(transfer_count) - ); - iob_reg_re #(BURST_W + 1, 0) axi_length_reg ( - `include "axis2axi_in_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (axi_length_reg_en), - .data_i(transfer_len), - .data_o(awlen_int) - ); - iob_reg_r #(AXI_ADDR_W, 0) address_reg ( - `include "axis2axi_in_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(next_address), - .data_o(current_address) - ); - iob_reg_r #(2, 0) state_reg ( - `include "axis2axi_in_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(state_nxt), - .data_o(state) - ); - - iob_fifo_sync #( - .W_DATA_W(AXI_DATA_W), - .R_DATA_W(AXI_DATA_W), - .ADDR_W (BUFFER_W) - ) fifo ( - //write port - .ext_mem_w_en_o (ext_mem_w_en_o), - .ext_mem_w_data_o(ext_mem_w_data_o), - .ext_mem_w_addr_o(ext_mem_w_addr_o), - //read port - .ext_mem_r_en_o (ext_mem_r_en_o), - .ext_mem_r_addr_o(ext_mem_r_addr_o), - .ext_mem_r_data_i(ext_mem_r_data_i), - - //write port - .w_en_i (axis_in_valid_i), - .w_data_i(axis_in_data_i), - .w_full_o(fifo_full), - - //read port - .r_en_i (fifo_read_enable), - .r_data_o (fifo_data), - .r_empty_o(fifo_empty), - - //FIFO level - .level_o(fifo_level), - - .clk_i (clk_i), - .cke_i (cke_i), - .rst_i (rst_i), - .arst_i(arst_i) - ); - -endmodule diff --git a/lib/hardware/buses/axis2axi/submodules/axis2axi_out/axis2axi_out.py b/lib/hardware/buses/axis2axi/submodules/axis2axi_out/axis2axi_out.py deleted file mode 100644 index 23bd9c633..000000000 --- a/lib/hardware/buses/axis2axi/submodules/axis2axi_out/axis2axi_out.py +++ /dev/null @@ -1,99 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "rst_i", - "descr": "Synchronous reset interface", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "config_out", - "descr": "AXI Stream output configuration interface", - "signals": [ - { - "name": "config_out_addr", - "direction": "input", - "width": "AXI_ADDR_W", - "descr": "", - }, - { - "name": "config_out_length", - "direction": "input", - "width": "AXI_ADDR_W", - "descr": "", - }, - { - "name": "config_out_valid", - "direction": "input", - "width": 1, - "descr": "", - }, - { - "name": "config_out_ready", - "direction": "output", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "axis_out", - "descr": "AXI Stream output interface", - "signals": [ - { - "name": "axis_out_data", - "direction": "output", - "width": "AXI_DATA_W", - "descr": "", - }, - { - "name": "axis_out_valid", - "direction": "output", - "width": 1, - "descr": "", - }, - { - "name": "axis_out_ready", - "direction": "input", - "width": 1, - "descr": "", - }, - ], - }, - { - "name": "axi_read_m", - "interface": { - "type": "axi_read", - "subtype": "master", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - }, - "descr": "AXI read interface", - }, - ], - "blocks": [], - } - - return attributes_dict diff --git a/lib/hardware/buses/axis2axi/submodules/axis2axi_out/hardware/src/axis2axi_out.v b/lib/hardware/buses/axis2axi/submodules/axis2axi_out/hardware/src/axis2axi_out.v deleted file mode 100644 index ca5488088..000000000 --- a/lib/hardware/buses/axis2axi/submodules/axis2axi_out/hardware/src/axis2axi_out.v +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - - -// Check axis2axi for information on how this unit works - -module axis2axi_out #( - parameter AXI_ADDR_W = 0, - parameter AXI_DATA_W = 32, // We currently only support 4 byte transfers - parameter AXI_LEN_W = 8, - parameter AXI_ID_W = 1, - parameter BURST_W = 0 -) ( - `include "axis2axi_out_io.vs" -); - - localparam BURST_SIZE = 2 ** BURST_W; - localparam BUFFER_W = BURST_W + 1; - localparam BUFFER_SIZE = 2 ** BUFFER_W; - - localparam WAIT_START = 2'h0, BEGIN_LOCAL = 2'h1, TRANSFER = 2'h2, END_LOCAL = 2'h3; - - // Constants - assign axi_arid_o = 0; - assign axi_arsize_o = 2; - assign axi_arburst_o = 1; - assign axi_arlock_o = 0; - assign axi_arcache_o = 2; - assign axi_arprot_o = 2; - assign axi_arqos_o = 0; - - // Regs to assign to outputs - reg arvalid_int; - reg [AXI_ADDR_W-1:0] araddr_int; - wire [ BURST_W:0] arlen_int; - - // State regs - reg [ 1:0] state_nxt; - reg [AXI_ADDR_W-1:0] next_address, next_length; - - // Instantiation wires - wire [AXI_ADDR_W-1:0] current_address, current_length; - wire [1:0] state; - - // Logical wires and combinatorial regs - wire doing_global_transfer = (state != 2'h0); - wire doing_local_transfer = (state == 2'h3); - wire [15:0] boundary_transfer_len = (16'h1000 - current_address[11:0]) >> 2; - wire normal_burst_possible = (current_length >= BURST_SIZE); - wire last_burst_possible = (current_length < BURST_SIZE); - - wire [BURST_W:0] burst_size; - - reg [BURST_W:0] non_boundary_burst_size; - always @* begin - non_boundary_burst_size = 0; - - if (last_burst_possible) begin - non_boundary_burst_size = current_length; - end - if (normal_burst_possible) non_boundary_burst_size = BURST_SIZE; - end - - generate - if (AXI_ADDR_W >= 13) begin // 4k boundary can only happen to LEN higher or equal to 13 - - wire [12:0] boundary_transfer_len = (13'h1000 - current_address[11:0]) >> 2; - - reg [BURST_W:0] boundary_burst_size; - always @* begin - boundary_burst_size = non_boundary_burst_size; - - if (non_boundary_burst_size > boundary_transfer_len) - boundary_burst_size = boundary_transfer_len; - end - - assign burst_size = boundary_burst_size; - - end else begin - assign burst_size = non_boundary_burst_size; - end - endgenerate - - wire [BURST_W:0] transfer_len = burst_size - 1; - - // Assignment to outputs - assign axis_out_data_o = axi_rdata_i; - assign axis_out_valid_o = (doing_local_transfer && axi_rvalid_i); - assign axi_rready_o = (doing_local_transfer && axis_out_ready_i); - assign config_out_ready_o = (state == WAIT_START); - - assign axi_araddr_o = araddr_int; - assign axi_arlen_o = arlen_int; - assign axi_arvalid_o = arvalid_int; - - always @* begin - state_nxt = state; - arvalid_int = 1'b0; - next_address = current_address; - next_length = current_length; - - case (state) - WAIT_START: - if (config_out_valid_i) begin - next_address = config_out_addr_i; - next_length = config_out_length_i; - if (config_out_length_i == 0) state_nxt = WAIT_START; - else state_nxt = BEGIN_LOCAL; - end - BEGIN_LOCAL: begin - next_length = current_length - burst_size; - state_nxt = TRANSFER; - end - TRANSFER: begin - araddr_int = current_address; - arvalid_int = 1'b1; - if (axi_arready_i) state_nxt = END_LOCAL; - end - END_LOCAL: - if (axi_rlast_i && axi_rvalid_i && axi_rready_o) begin - next_address = current_address + ((axi_arlen_o + 1) << 2); - if (current_length == 0) state_nxt = WAIT_START; - else state_nxt = BEGIN_LOCAL; - end - endcase - end - - iob_reg_re #(BURST_W + 1, 0) axi_length_reg ( - `include "axis2axi_out_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (state == BEGIN_LOCAL), - .data_i(transfer_len), - .data_o(arlen_int) - ); - iob_reg_r #(AXI_ADDR_W, 0) address_reg ( - `include "axis2axi_out_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(next_address), - .data_o(current_address) - ); - iob_reg_r #(AXI_ADDR_W, 0) length_reg ( - `include "axis2axi_out_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(next_length), - .data_o(current_length) - ); - iob_reg_r #(2, 0) state_reg ( - `include "axis2axi_out_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(state_nxt), - .data_o(state) - ); - -endmodule diff --git a/lib/hardware/buses/axis2axi/waves.gtkw b/lib/hardware/buses/axis2axi/waves.gtkw deleted file mode 100644 index 672788414..000000000 --- a/lib/hardware/buses/axis2axi/waves.gtkw +++ /dev/null @@ -1,150 +0,0 @@ -[*] -[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI -[*] Thu Mar 9 17:27:45 2023 -[*] -[dumpfile] "/home/zettasticks/IOBundle/iob-lib/hardware/axis2axi/uut.vcd" -[dumpfile_mtime] "Thu Mar 9 17:22:31 2023" -[dumpfile_size] 126842 -[savefile] "/home/zettasticks/IOBundle/iob-lib/hardware/axis2axi/signals.gtkw" -[timestart] 0 -[size] 1848 1016 -[pos] -1 -1 -*-22.000000 7270000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -[treeopen] axis2axi_tb. -[treeopen] axis2axi_tb.uut. -[treeopen] axis2axi_tb.uut.axis2axi_in_inst. -[treeopen] axis2axi_tb.uut.axis2axi_in_inst.fifo. -[sst_width] 233 -[signals_width] 341 -[sst_expanded] 1 -[sst_vpaned_height] 579 -@421 -axis2axi_tb.i -@28 -axis2axi_tb.uut.axis2axi_in_inst.clk -@22 -axis2axi_tb.uut.axis2axi_in_inst.addr_in[23:0] -axis2axi_tb.uut.axis2axi_in_inst.axis_in_data[31:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.axis_in_ready -axis2axi_tb.uut.axis2axi_in_inst.axis_in_valid -@22 -axis2axi_tb.uut.axis2axi_in_inst.fifo.level[4:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.fifo_full -@22 -axis2axi_tb.uut.axis2axi_in_inst.fifo_data[31:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.fifo_empty -axis2axi_tb.uut.axis2axi_in_inst.fifo_full -axis2axi_tb.uut.axis2axi_in_inst.state[1:0] -@22 -axis2axi_tb.uut.axis2axi_in_inst.transfer_count[7:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.last_burst_possible -axis2axi_tb.uut.axis2axi_in_inst.normal_burst_possible -@22 -axis2axi_tb.uut.axis2axi_in_inst.current_address[23:0] -axis2axi_tb.uut.axis2axi_in_inst.burst_size[15:0] -axis2axi_tb.uut.axis2axi_in_inst.boundary_transfer_len[15:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.start_transfer -axis2axi_tb.uut.axis2axi_in_inst.doing_transfer -axis2axi_tb.uut.axis2axi_in_inst.last_transfer -@22 -axis2axi_tb.uut.axis2axi_in_inst.m_axi_awaddr[23:0] -axis2axi_tb.uut.axis2axi_in_inst.m_axi_awlen[7:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.m_axi_awready -axis2axi_tb.uut.axis2axi_in_inst.m_axi_awvalid -@22 -axis2axi_tb.uut.axis2axi_in_inst.m_axi_wdata[31:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.m_axi_wlast -axis2axi_tb.uut.axis2axi_in_inst.m_axi_wvalid -axis2axi_tb.uut.axis2axi_in_inst.m_axi_wready -axis2axi_tb.uut.axis2axi_in_inst.rst -axis2axi_tb.uut.axis2axi_in_inst.m_axi_bready -axis2axi_tb.uut.axis2axi_in_inst.m_axi_bvalid -axis2axi_tb.uut.axis2axi_in_inst.m_axi_bresp[1:0] -@200 --Fifo -@28 -axis2axi_tb.uut.axis2axi_in_inst.fifo.w_en -@22 -axis2axi_tb.uut.axis2axi_in_inst.fifo.w_data[31:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.fifo.r_en -@22 -axis2axi_tb.uut.axis2axi_in_inst.fifo.r_data[31:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.fifo.r_en_int -axis2axi_tb.uut.axis2axi_in_inst.fifo.iob_ram_t2p_asym0.w_en -@22 -axis2axi_tb.uut.axis2axi_in_inst.fifo.iob_ram_t2p_asym0.w_data[31:0] -@28 -axis2axi_tb.uut.axis2axi_in_inst.fifo.iob_ram_t2p_asym0.r_en -@22 -axis2axi_tb.uut.axis2axi_in_inst.fifo.iob_ram_t2p_asym0.r_data[31:0] -axis2axi_tb.uut.axis2axi_in_inst.fifo.iob_ram_t2p_asym0.data_rd_0[31:0] -@200 -- -@22 -axis2axi_tb.uut.axis2axi_out_inst.addr_out[23:0] -axis2axi_tb.uut.axis2axi_out_inst.araddr_int[23:0] -axis2axi_tb.uut.axis2axi_out_inst.arlen_int[7:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.arvalid_int -@22 -axis2axi_tb.uut.axis2axi_out_inst.axis_out_data[31:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.axis_out_ready -axis2axi_tb.uut.axis2axi_out_inst.axis_out_valid -@22 -axis2axi_tb.uut.axis2axi_out_inst.boundary_transfer_len[15:0] -axis2axi_tb.uut.axis2axi_out_inst.burst_size[15:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.clk -@22 -axis2axi_tb.uut.axis2axi_out_inst.current_address[23:0] -axis2axi_tb.uut.axis2axi_out_inst.current_length[23:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.doing_global_transfer -axis2axi_tb.uut.axis2axi_out_inst.doing_local_transfer -axis2axi_tb.uut.axis2axi_out_inst.last_burst_possible -@22 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_araddr[23:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arburst[1:0] -@22 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arcache[3:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arid -@22 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arlen[7:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arlock -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arprot[2:0] -@22 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arqos[3:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arready -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arsize[2:0] -axis2axi_tb.uut.axis2axi_out_inst.m_axi_arvalid -@22 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_rdata[31:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.m_axi_rid -axis2axi_tb.uut.axis2axi_out_inst.m_axi_rlast -axis2axi_tb.uut.axis2axi_out_inst.m_axi_rready -axis2axi_tb.uut.axis2axi_out_inst.m_axi_rresp[1:0] -axis2axi_tb.uut.axis2axi_out_inst.m_axi_rvalid -axis2axi_tb.uut.axis2axi_out_inst.normal_burst_possible -@22 -axis2axi_tb.uut.axis2axi_out_inst.out_length[23:0] -@28 -axis2axi_tb.uut.axis2axi_out_inst.rst -axis2axi_tb.uut.axis2axi_out_inst.set_out_config -axis2axi_tb.uut.axis2axi_out_inst.state[1:0] -[pattern_trace] 1 -[pattern_trace] 0 diff --git a/lib/hardware/buses/axis2axi/waves.gtkw.license b/lib/hardware/buses/axis2axi/waves.gtkw.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/buses/axis2axi/waves.gtkw.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/buses/axis2fifo/axis2fifo.py b/lib/hardware/buses/axis2fifo/axis2fifo.py deleted file mode 100644 index f30a638ad..000000000 --- a/lib/hardware/buses/axis2fifo/axis2fifo.py +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - }, - { - "core_name": "iob_edge_detect", - "instance_name": "iob_edge_detect_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/axis2fifo/hardware/src/axis2fifo.v b/lib/hardware/buses/axis2fifo/hardware/src/axis2fifo.v deleted file mode 100644 index fe6a65747..000000000 --- a/lib/hardware/buses/axis2fifo/hardware/src/axis2fifo.v +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module axis2fifo #( - parameter DATA_W = 0, - parameter AXIS_LEN_W = 0 -) ( - `include "axis2fifo_clk_en_rst_s_port.vs" - input rst_i, - input en_i, - output [AXIS_LEN_W-1:0] len_o, - output done_o, - - // AXIS I/F - input [DATA_W-1:0] axis_tdata_i, - input axis_tvalid_i, - output axis_tready_o, - input axis_tlast_i, - - // FIFO I/F - input fifo_full_i, - output [DATA_W-1:0] fifo_wdata_o, - output fifo_write_o -); - - wire axis_word_count_en; - - //tready - wire axis_tready_nxt; - assign axis_tready_nxt = (~fifo_full_i) & en_i; - iob_reg_r #( - .DATA_W (1), - .RST_VAL(1'b0) - ) tready_reg ( - `include "axis2fifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(axis_tready_nxt), - .data_o(axis_tready_o) - ); - - // tvalid register - wire axis_tvalid_reg; - wire in_regs_en; - assign in_regs_en = axis_tready_o & en_i; - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'b0) - ) tvalid_reg ( - `include "axis2fifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (in_regs_en), - .data_i(axis_tvalid_i), - .data_o(axis_tvalid_reg) - ); - - // tlast register - wire axis_tlast_reg; - wire axis_tlast_nxt; - assign axis_tlast_nxt = axis_tlast_i & axis_tvalid_i; - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'b0) - ) tlast_reg ( - `include "axis2fifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (in_regs_en), - .data_i(axis_tlast_nxt), - .data_o(axis_tlast_reg) - ); - - // tdata register - wire [DATA_W-1:0] axis_tdata_reg; - iob_reg_re #( - .DATA_W (DATA_W), - .RST_VAL({DATA_W{1'b0}}) - ) tdata_reg ( - `include "axis2fifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (in_regs_en), - .data_i(axis_tdata_i), - .data_o(axis_tdata_reg) - ); - - //word count enable - assign axis_word_count_en = fifo_write_o & (~done_o); - - //tdata word count - iob_counter #( - .DATA_W (AXIS_LEN_W), - .RST_VAL({AXIS_LEN_W{1'b0}}) - ) word_count_inst ( - `include "axis2fifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (axis_word_count_en), - .data_o(len_o) - ); - - //tlast detection - reg axis_tlast_int; - iob_edge_detect #( - .EDGE_TYPE("rising"), - .OUT_TYPE ("step") - ) tlast_detect ( - `include "axis2fifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .bit_i (axis_tlast_int), - .detected_o(done_o) - ); - - reg [DATA_W-1:0] fifo_wdata_int; - reg fifo_write_int; - - always @* begin - if (axis_tready_o) begin - fifo_wdata_int = axis_tdata_i; - fifo_write_int = axis_tvalid_i & axis_tready_o; - axis_tlast_int = axis_tlast_i; - end else begin // When fifo is full, we need to use the "saved" values - fifo_wdata_int = axis_tdata_reg; - fifo_write_int = axis_tvalid_reg & axis_tready_nxt; - axis_tlast_int = axis_tlast_reg; - end - end - - // Output FIFO I/F - assign fifo_wdata_o = fifo_wdata_int; - assign fifo_write_o = fifo_write_int; - -endmodule diff --git a/lib/hardware/buses/axis2fifo/waves.gtkw b/lib/hardware/buses/axis2fifo/waves.gtkw deleted file mode 100644 index 0ad9cb7e2..000000000 --- a/lib/hardware/buses/axis2fifo/waves.gtkw +++ /dev/null @@ -1,133 +0,0 @@ -[*] -[*] GTKWave Analyzer v3.3.86 (w)1999-2017 BSI -[*] Fri Feb 18 18:48:05 2022 -[*] -[dumpfile] "/home/jdlopes/git/iob-axi/uut.vcd" -[dumpfile_mtime] "Fri Feb 18 18:44:44 2022" -[dumpfile_size] 1306246 -[savefile] "/home/jdlopes/git/iob-axi/hardware/axis2fifo/waves.gtkw" -[timestart] 2775200 -[size] 1920 1017 -[pos] -1 -1 -*-15.797765 2870000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -[treeopen] axis2fifo_tb. -[treeopen] axis2fifo_tb.uut. -[sst_width] 258 -[signals_width] 286 -[sst_expanded] 1 -[sst_vpaned_height] 289 -@28 -axis2fifo_tb.clk -axis2fifo_tb.rst -@200 -- -@22 -axis2fifo_tb.start_addr[23:0] -@200 -- -@28 -axis2fifo_tb.uut.run -axis2fifo_tb.uut.direction -axis2fifo_tb.uut.error -axis2fifo_tb.uut.ready -@200 -- -@22 -axis2fifo_tb.uut.addr_int[23:0] -axis2fifo_tb.uut.addr4k[21:0] -axis2fifo_tb.uut.addrRem[21:0] -axis2fifo_tb.uut.minAddr[21:0] -@200 -- -@201 --Native Slave I/F -@28 -axis2fifo_tb.uut.s_valid -@24 -axis2fifo_tb.uut.s_addr[23:0] -axis2fifo_tb.uut.s_wdata[31:0] -@22 -axis2fifo_tb.uut.s_wstrb[3:0] -@24 -axis2fifo_tb.uut.s_rdata[31:0] -@28 -axis2fifo_tb.uut.s_ready -@200 -- --AXI-4 Full Write Master I/F -@28 -axis2fifo_tb.uut.m_axi_awvalid -@c00022 -axis2fifo_tb.uut.m_axi_awaddr[23:0] -@28 -(0)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(1)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(2)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(3)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(4)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(5)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(6)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(7)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(8)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(9)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(10)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(11)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(12)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(13)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(14)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(15)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(16)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(17)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(18)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(19)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(20)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(21)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(22)axis2fifo_tb.uut.m_axi_awaddr[23:0] -(23)axis2fifo_tb.uut.m_axi_awaddr[23:0] -@1401200 --group_end -@24 -axis2fifo_tb.uut.m_axi_awlen[7:0] -@28 -axis2fifo_tb.uut.m_axi_awready -@200 -- -@28 -axis2fifo_tb.uut.m_axi_wvalid -@24 -axis2fifo_tb.uut.m_axi_wdata[31:0] -@22 -axis2fifo_tb.uut.m_axi_wstrb[3:0] -@28 -axis2fifo_tb.uut.m_axi_wlast -axis2fifo_tb.uut.m_axi_wready -@200 -- -@28 -axis2fifo_tb.uut.m_axi_bvalid -axis2fifo_tb.uut.m_axi_bresp[1:0] -axis2fifo_tb.uut.m_axi_bready -@200 -- --AXI-4 Full Read Master I/F -@28 -axis2fifo_tb.uut.axis2fifo_rd0.state -axis2fifo_tb.uut.m_axi_arvalid -@22 -axis2fifo_tb.uut.m_axi_araddr[23:0] -@24 -axis2fifo_tb.uut.m_axi_arlen[7:0] -@28 -axis2fifo_tb.uut.m_axi_arready -@200 -- -@28 -axis2fifo_tb.uut.m_axi_rvalid -@24 -axis2fifo_tb.uut.axis2fifo_rd0.m_axi_rdata[31:0] -@28 -axis2fifo_tb.uut.m_axi_rlast -axis2fifo_tb.uut.m_axi_rresp[1:0] -axis2fifo_tb.uut.m_axi_rready -[pattern_trace] 1 -[pattern_trace] 0 diff --git a/lib/hardware/buses/axis2fifo/waves.gtkw.license b/lib/hardware/buses/axis2fifo/waves.gtkw.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/buses/axis2fifo/waves.gtkw.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/buses/axis_tasks/axis_tasks.py b/lib/hardware/buses/axis_tasks/axis_tasks.py deleted file mode 100644 index 39fbb46d3..000000000 --- a/lib/hardware/buses/axis_tasks/axis_tasks.py +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - } - - return attributes_dict diff --git a/lib/hardware/buses/axis_tasks/hardware/src/axis_tasks.vs b/lib/hardware/buses/axis_tasks/hardware/src/axis_tasks.vs deleted file mode 100644 index 8a75492ee..000000000 --- a/lib/hardware/buses/axis_tasks/hardware/src/axis_tasks.vs +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -// -// Tasks for the AXI Stream protocol -// - -// Write data to AXI Stream -task axis_write; - input [AXIS_W_DATA_W-1:0] data; - input last; - - begin - @(posedge axis_w_clk) axis_w_tvalid = 1; //sync and assign - axis_w_tdata = data; - axis_w_tlast = last; - - #1 while (!axis_w_tready) #1; - - @(posedge axis_w_clk) axis_w_tvalid = 0; - end -endtask - -// Read data from AXI Stream -task axis_read; - output [AXIS_R_DATA_W-1:0] data; - output last; - - begin - @(posedge axis_r_clk) axis_r_tready = 1; - - #1 while (!axis_r_tvalid) #1; - - data = axis_r_tdata; - last = axis_r_tlast; - @(posedge axis_r_clk) #1 axis_r_tready = 0; - end -endtask diff --git a/lib/hardware/buses/bus_width_converter/bus_width_converter.py b/lib/hardware/buses/bus_width_converter/bus_width_converter.py deleted file mode 100644 index a8803caee..000000000 --- a/lib/hardware/buses/bus_width_converter/bus_width_converter.py +++ /dev/null @@ -1,166 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -interfaces = { - "iob": [ - ("valid", "output", 1), - ("addr", "output", "ADDR_W"), - ("wdata", "output", "DATA_W"), - ("wstrb", "output", "DATA_W / 8"), - ("rvalid", "input", 1), - ("rdata", "input", "DATA_W"), - ("ready", "input", 1), - ], - "axil": [ - ("awaddr", "output", "ADDR_W"), - ("awprot", "output", "PROT_W"), - ("awvalid", "output", 1), - ("awready", "input", 1), - ("wdata", "output", "DATA_W"), - ("wstrb", "output", "DATA_W / 8"), - ("wvalid", "output", 1), - ("wready", "input", 1), - ("bresp", "input", "RESP_W"), - ("bvalid", "input", 1), - ("bready", "output", 1), - ("araddr", "output", "ADDR_W"), - ("arprot", "output", "PROT_W"), - ("arvalid", "output", 1), - ("arready", "input", 1), - ("rdata", "input", "DATA_W"), - ("rresp", "input", "RESP_W"), - ("rvalid", "input", 1), - ("rready", "output", 1), - ], - "axi": [ - ("awaddr", "output", "ADDR_W"), - ("awprot", "output", "PROT_W"), - ("awvalid", "output", 1), - ("awready", "input", 1), - ("wdata", "output", "DATA_W"), - ("wstrb", "output", "DATA_W / 8"), - ("wvalid", "output", 1), - ("wready", "input", 1), - ("bresp", "input", "RESP_W"), - ("bvalid", "input", 1), - ("bready", "output", 1), - ("araddr", "output", "ADDR_W"), - ("arprot", "output", "PROT_W"), - ("arvalid", "output", 1), - ("arready", "input", 1), - ("rdata", "input", "DATA_W"), - ("rresp", "input", "RESP_W"), - ("rvalid", "input", 1), - ("rready", "output", 1), - ("awid", "output", "ID_W"), - ("awlen", "output", "LEN_W"), - ("awsize", "output", "SIZE_W"), - ("awburst", "output", "BURST_W"), - ("awlock", "output", "LOCK_W"), - ("awcache", "output", "CACHE_W"), - ("awqos", "output", "QOS_W"), - ("wlast", "output", 1), - ("bid", "input", "ID_W"), - ("arid", "output", "ID_W"), - ("arlen", "output", "LEN_W"), - ("arsize", "output", "SIZE_W"), - ("arburst", "output", "BURST_W"), - ("arlock", "output", "LOCK_W"), - ("arcache", "output", "CACHE_W"), - ("arqos", "output", "QOS_W"), - ("rid", "input", "ID_W"), - ("rlast", "input", 1), - ], -} - - -def setup(py_params_dict): - """Core purely made of wires to convert between two buses with different widths (to suppress verilog warnings). - Use verilog parameters to define widths of each bus. - :param str interface: Type of interface of buses. - """ - INTERFACE = py_params_dict["interface"] if "interface" in py_params_dict else "axil" - - wire_assigns = "" - parameter_names = [] - verilog_parameters = [] - master_interface_parameters = {} - slave_interface_parameters = {} - for signal in interfaces[INTERFACE]: - name = signal[0] - direction = signal[1] - width = signal[2] - - if type(width) is int: - bit_select = "" - elif direction == "output": - bit_select = f"[MASTER_{width}-1:0]" - else: - bit_select = f"[SLAVE_{width}-1:0]" - - # Connect both interfaces - wire_assigns += f""" - assign {INTERFACE}_{name}_o = {INTERFACE}_{name}_i{bit_select}; -""" - - # Only create verilog parameters for strings that represent widths - if type(width) is int or not width.endswith("_W"): - continue - - # Don't create a duplicate parameters - if width in parameter_names: - continue - parameter_names.append(width) - - # Set verilog parameters for each interface - verilog_parameters += [ - { - "name": f"SLAVE_{width}", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": f"Slave {width[:-2]} bus width", - }, - { - "name": f"MASTER_{width}", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - "descr": f"Master {width[:-2]} bus width", - }, - ] - # Set parameters for if_gen generation of each interface - slave_interface_parameters[width] = f"SLAVE_{width}" - master_interface_parameters[width] = f"MASTER_{width}" - - attributes_dict = { - "name": f"{INTERFACE}_bus_width_converter", - "version": "0.1", - "confs": verilog_parameters, - "ports": [ - { - "name": "slave", - "descr": "Slave interface (connects to master)", - "interface": { - "type": INTERFACE, - "subtype": "slave", - **slave_interface_parameters, - }, - }, - { - "name": "master", - "descr": "Master interface (connects to slave)", - "interface": { - "type": INTERFACE, - "subtype": "master", - **master_interface_parameters, - }, - }, - ], - "snippets": [{"verilog_code": wire_assigns}], - } - - return attributes_dict diff --git a/lib/hardware/buses/fifo2axis/fifo2axis.py b/lib/hardware/buses/fifo2axis/fifo2axis.py deleted file mode 100644 index cb4fb8a93..000000000 --- a/lib/hardware/buses/fifo2axis/fifo2axis.py +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - { - "core_name": "iob_modcnt", - "instance_name": "iob_modcnt_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/fifo2axis/hardware/src/fifo2axis.v b/lib/hardware/buses/fifo2axis/hardware/src/fifo2axis.v deleted file mode 100644 index 5eb392e78..000000000 --- a/lib/hardware/buses/fifo2axis/hardware/src/fifo2axis.v +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module fifo2axis #( - parameter DATA_W = 0, - parameter AXIS_LEN_W = 0 -) ( - `include "fifo2axis_clk_en_rst_s_port.vs" - input rst_i, - input en_i, - input [AXIS_LEN_W-1:0] len_i, - - // FIFO I/F - input fifo_empty_i, - output fifo_read_o, - input [DATA_W-1:0] fifo_rdata_i, - - // AXIS I/F - output axis_tvalid_o, - output [DATA_W-1:0] axis_tdata_o, - input axis_tready_i, - output axis_tlast_o -); - - wire [AXIS_LEN_W-1:0] axis_word_count; - - //FIFO read - wire axis_tvalid_int; - - wire pipe_en; - assign pipe_en = (axis_tready_i | (~axis_tvalid_o)) & en_i; - - assign fifo_read_o = ((~fifo_empty_i) & ((axis_tready_i & axis_tvalid_o) | (~axis_tvalid_o))) - & en_i; - - // valid_int register - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'd0) - ) valid_int_reg ( - `include "fifo2axis_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (pipe_en), - .data_i(fifo_read_o), - .data_o(axis_tvalid_int) - ); - - //FIFO tlast - wire axis_tlast_nxt; - wire [AXIS_LEN_W-1:0] len_int = len_i - 1; - assign axis_tlast_nxt = (axis_word_count == len_int); - - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'd0) - ) axis_tlast_reg ( - `include "fifo2axis_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (pipe_en), - .data_i(axis_tlast_nxt), - .data_o(axis_tlast_o) - ); - - //tdata word count - iob_modcnt #( - .DATA_W (AXIS_LEN_W), - .RST_VAL({AXIS_LEN_W{1'b1}}) // go to 0 after first enable - ) word_count_inst ( - `include "fifo2axis_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (fifo_read_o), - .mod_i (len_int), - .data_o(axis_word_count) - ); - - //tdata pipe register - iob_reg_re #( - .DATA_W (DATA_W), - .RST_VAL({DATA_W{1'd0}}) - ) axis_tdata_reg ( - `include "fifo2axis_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (pipe_en), - .data_i(fifo_rdata_i), - .data_o(axis_tdata_o) - ); - - //tvalid pipe register - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'd0) - ) axis_tvalid_reg ( - `include "fifo2axis_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (pipe_en), - .data_i(axis_tvalid_int), - .data_o(axis_tvalid_o) - ); - -endmodule diff --git a/lib/hardware/buses/fifo2axis/waves.gtkw b/lib/hardware/buses/fifo2axis/waves.gtkw deleted file mode 100644 index 9b208dfab..000000000 --- a/lib/hardware/buses/fifo2axis/waves.gtkw +++ /dev/null @@ -1,133 +0,0 @@ -[*] -[*] GTKWave Analyzer v3.3.86 (w)1999-2017 BSI -[*] Fri Feb 18 18:48:05 2022 -[*] -[dumpfile] "/home/jdlopes/git/iob-axi/uut.vcd" -[dumpfile_mtime] "Fri Feb 18 18:44:44 2022" -[dumpfile_size] 1306246 -[savefile] "/home/jdlopes/git/iob-axi/hardware/fifo2axis/waves.gtkw" -[timestart] 2775200 -[size] 1920 1017 -[pos] -1 -1 -*-15.797765 2870000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -[treeopen] fifo2axis_tb. -[treeopen] fifo2axis_tb.uut. -[sst_width] 258 -[signals_width] 286 -[sst_expanded] 1 -[sst_vpaned_height] 289 -@28 -fifo2axis_tb.clk -fifo2axis_tb.rst -@200 -- -@22 -fifo2axis_tb.start_addr[23:0] -@200 -- -@28 -fifo2axis_tb.uut.run -fifo2axis_tb.uut.direction -fifo2axis_tb.uut.error -fifo2axis_tb.uut.ready -@200 -- -@22 -fifo2axis_tb.uut.addr_int[23:0] -fifo2axis_tb.uut.addr4k[21:0] -fifo2axis_tb.uut.addrRem[21:0] -fifo2axis_tb.uut.minAddr[21:0] -@200 -- -@201 --Native Slave I/F -@28 -fifo2axis_tb.uut.s_valid -@24 -fifo2axis_tb.uut.s_addr[23:0] -fifo2axis_tb.uut.s_wdata[31:0] -@22 -fifo2axis_tb.uut.s_wstrb[3:0] -@24 -fifo2axis_tb.uut.s_rdata[31:0] -@28 -fifo2axis_tb.uut.s_ready -@200 -- --AXI-4 Full Write Master I/F -@28 -fifo2axis_tb.uut.m_axi_awvalid -@c00022 -fifo2axis_tb.uut.m_axi_awaddr[23:0] -@28 -(0)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(1)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(2)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(3)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(4)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(5)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(6)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(7)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(8)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(9)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(10)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(11)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(12)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(13)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(14)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(15)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(16)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(17)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(18)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(19)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(20)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(21)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(22)fifo2axis_tb.uut.m_axi_awaddr[23:0] -(23)fifo2axis_tb.uut.m_axi_awaddr[23:0] -@1401200 --group_end -@24 -fifo2axis_tb.uut.m_axi_awlen[7:0] -@28 -fifo2axis_tb.uut.m_axi_awready -@200 -- -@28 -fifo2axis_tb.uut.m_axi_wvalid -@24 -fifo2axis_tb.uut.m_axi_wdata[31:0] -@22 -fifo2axis_tb.uut.m_axi_wstrb[3:0] -@28 -fifo2axis_tb.uut.m_axi_wlast -fifo2axis_tb.uut.m_axi_wready -@200 -- -@28 -fifo2axis_tb.uut.m_axi_bvalid -fifo2axis_tb.uut.m_axi_bresp[1:0] -fifo2axis_tb.uut.m_axi_bready -@200 -- --AXI-4 Full Read Master I/F -@28 -fifo2axis_tb.uut.fifo2axis_rd0.state -fifo2axis_tb.uut.m_axi_arvalid -@22 -fifo2axis_tb.uut.m_axi_araddr[23:0] -@24 -fifo2axis_tb.uut.m_axi_arlen[7:0] -@28 -fifo2axis_tb.uut.m_axi_arready -@200 -- -@28 -fifo2axis_tb.uut.m_axi_rvalid -@24 -fifo2axis_tb.uut.fifo2axis_rd0.m_axi_rdata[31:0] -@28 -fifo2axis_tb.uut.m_axi_rlast -fifo2axis_tb.uut.m_axi_rresp[1:0] -fifo2axis_tb.uut.m_axi_rready -[pattern_trace] 1 -[pattern_trace] 0 diff --git a/lib/hardware/buses/fifo2axis/waves.gtkw.license b/lib/hardware/buses/fifo2axis/waves.gtkw.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/buses/fifo2axis/waves.gtkw.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/buses/iob2apb/iob2apb.py b/lib/hardware/buses/iob2apb/iob2apb.py deleted file mode 100644 index 250e26e5c..000000000 --- a/lib/hardware/buses/iob2apb/iob2apb.py +++ /dev/null @@ -1,189 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "APB_ADDR_W", - "type": "P", - "val": "22", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "APB_DATA_W", - "type": "P", - "val": "22", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "APB_ADDR_W", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - { - "name": "DATA_W", - "type": "P", - "val": "APB_DATA_W", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "iob_s", - "interface": { - "type": "iob", - "subtype": "slave", - }, - "descr": "CPU native interface", - }, - { - "name": "apb_m", - "interface": { - "type": "apb", - "subtype": "master", - }, - "descr": "APB interface", - }, - ], - "wires": [ - { - "name": "pc_int", - "descr": "pc_int wire", - "signals": [ - {"name": "pc_int", "width": 2}, - ], - }, - { - "name": "pc_nxt_int", - "descr": "pc_nxt_int wire", - "signals": [ - {"name": "pc_nxt_int", "width": 2}, - ], - }, - { - "name": "apb_rdata_int", - "descr": "apb_rdata_int wire", - "signals": [ - {"name": "apb_rdata_int", "width": 32}, - ], - }, - { - "name": "iob_rdata_int", - "descr": "iob_rdata_int wire", - "signals": [ - {"name": "iob_rdata_int", "width": 32}, - ], - }, - { - "name": "apb_ready_int", - "descr": "apb_ready_int wire", - "signals": [ - {"name": "apb_ready_int", "width": 1}, - ], - }, - { - "name": "iob_rvalid_int", - "descr": "iob_rvalid_int wire", - "signals": [ - {"name": "iob_rvalid_int", "width": 1}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg", - "instance_name": "pc_reg", - "parameters": { - "DATA_W": 2, - "RST_VAL": 0, - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "pc_nxt_int", - "data_o": "pc_int", - }, - }, - { - "core_name": "iob_reg", - "instance_name": "iob_rdata_reg", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": 0, - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "apb_rdata_int", - "data_o": "iob_rdata_int", - }, - }, - { - "core_name": "iob_reg", - "instance_name": "iob_rvalid_reg", - "parameters": { - "DATA_W": 1, - "RST_VAL": 0, - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "apb_ready_int", - "data_o": "iob_rvalid_int", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - reg [1:0] pc_nxt; - reg [1:0] apb_enable; - always @* begin - pc_nxt_int = pc_int + 1'b1; - apb_enable = 1'b0; - - case (pc_int) - WAIT_VALID: begin - if (!iob_valid_i) begin - pc_nxt_int = pc_int; - end else begin - apb_enable = 1'b1; - end - end - WAIT_READY: begin - apb_enable = 1'b1; - if (!apb_ready_i) begin - pc_nxt_int = pc_int; - end else if (apb_write_o) begin // No need to wait for rvalid - pc_nxt_int = WAIT_VALID; - end - end - default: begin - pc_nxt_int = WAIT_VALID; - end - endcase - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob2axi/hardware/simulation/src/iob2axi_tb.disable.v b/lib/hardware/buses/iob2axi/hardware/simulation/src/iob2axi_tb.disable.v deleted file mode 100644 index 56b6a9335..000000000 --- a/lib/hardware/buses/iob2axi/hardware/simulation/src/iob2axi_tb.disable.v +++ /dev/null @@ -1,232 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - -`define CLK_PER 10 - -module iob2axi_tb; - - parameter TEST_SZ = 1024; - - parameter ADDR_W = 24; - parameter DATA_W = 32; - - parameter AXI_ADDR_W = ADDR_W; - parameter AXI_DATA_W = DATA_W; - - // Clock - reg clk = 1; - always #(`CLK_PER / 2) clk = ~clk; - - // Reset - reg rst = 0; - - // - // DMA interface - // - - // Control I/F - reg run; - reg direction; - reg [ ADDR_W-1:0] start_addr; - wire ready; - wire error; - - // Native slave I/F - reg s_valid; - reg [ ADDR_W-1:0] s_addr; - reg [ DATA_W-1:0] s_wdata; - reg [DATA_W/8-1:0] s_wstrb; - wire [ DATA_W-1:0] s_rdata; - wire s_ready; - - // AXI-4 full master I/F - `include "ddr_axi_wire.vs" - - // Iterators - integer i, seq_ini; - - initial begin - -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // - // Init signals - // - run = 0; - start_addr = 0; - direction = 1; - - s_valid = 0; - s_addr = 0; - s_wdata = 0; - s_wstrb = 0; - - // - // Initialize memory - // - - // Assert reset - #100 rst = 1; - - // Deassert rst - repeat (10) @(posedge clk) #1; - rst = 0; - - // Wait an arbitray (10) number of cycles - repeat (10) @(posedge clk) #1; - - // - // Test starts here - // - - // Write test - - direction = 1; - start_addr = 'h8000 - (10 * DATA_W / 8); - - run = 1; - @(posedge clk) #1; - run = 0; - - s_valid = 1; - s_wstrb = 4'hf; - - // Number from which to start the incremental sequence to initialize the RAM - seq_ini = 32; - for (i = 0; i < TEST_SZ; i = i + 1) begin - s_addr = i; - s_wdata = i + seq_ini; - do @(posedge clk) #1; while (~s_ready); - end - s_valid = 0; - - repeat (20) @(posedge clk) #1; - - // Read test - direction = 0; - - run = 1; - @(posedge clk) #1; - run = 0; - - s_valid = 1; - s_wstrb = 4'h0; - - for (i = 0; i < TEST_SZ; i = i + 1) begin - s_addr = i; - do @(posedge clk) #1; while (~s_ready); - - if (s_rdata != i + seq_ini) begin - $display("ERROR: Test failed! At position %d, data=%h and s_rdata=%h.", i, i + seq_ini, - s_rdata); - end - end - s_valid = 0; - - while (~ready) @(posedge clk) #1; - - $display("INFO: Test completed successfully!"); - - repeat (10) @(posedge clk) #1; - - $finish(); - end - - iob2axi #( - .ADDR_W(ADDR_W), - .DATA_W(DATA_W) - ) uut ( - .clk_i(clk), - .rst_i(rst), - - // - // Control I/F - // - .run_i (run), - .direction_i(direction), - .addr_i (start_addr), - .ready_o (ready), - .error_o (error), - - // - // AXI-4 full master I/F - // - `include "m_ddr_axi_portmap.vs" - - // - // Native slave I/F - // - .s_valid_i(s_valid), - .s_addr_i (s_addr), - .s_wdata_i(s_wdata), - .s_wstrb_i(s_wstrb), - .s_rdata_o(s_rdata), - .s_ready_o(s_ready) - ); - - axi_ram #( - .ID_WIDTH (`AXI_ID_W), - .DATA_WIDTH(AXI_DATA_W), - .ADDR_WIDTH(AXI_ADDR_W) - ) axi_ram0 ( - .clk(clk), - .rst(rst), - - // - // AXI-4 full master interface - // - - // Address write - .s_axi_awid (ddr_axi_awid), - .s_axi_awaddr (ddr_axi_awaddr), - .s_axi_awlen (ddr_axi_awlen), - .s_axi_awsize (ddr_axi_awsize), - .s_axi_awburst(ddr_axi_awburst), - .s_axi_awlock (ddr_axi_awlock), - .s_axi_awprot (ddr_axi_awprot), - .s_axi_awcache(ddr_axi_awcache), - .s_axi_awvalid(ddr_axi_awvalid), - .s_axi_awready(ddr_axi_awready), - - // Write - .s_axi_wvalid(ddr_axi_wvalid), - .s_axi_wdata (ddr_axi_wdata), - .s_axi_wstrb (ddr_axi_wstrb), - .s_axi_wlast (ddr_axi_wlast), - .s_axi_wready(ddr_axi_wready), - - // Write response - .s_axi_bid (ddr_axi_bid), - .s_axi_bvalid(ddr_axi_bvalid), - .s_axi_bresp (ddr_axi_bresp), - .s_axi_bready(ddr_axi_bready), - - // Address read - .s_axi_arid (ddr_axi_arid), - .s_axi_araddr (ddr_axi_araddr), - .s_axi_arlen (ddr_axi_arlen), - .s_axi_arsize (ddr_axi_arsize), - .s_axi_arburst(ddr_axi_arburst), - .s_axi_arlock (ddr_axi_arlock), - .s_axi_arcache(ddr_axi_arcache), - .s_axi_arprot (ddr_axi_arprot), - .s_axi_arvalid(ddr_axi_arvalid), - .s_axi_arready(ddr_axi_arready), - - // Read - .s_axi_rid (ddr_axi_rid), - .s_axi_rvalid(ddr_axi_rvalid), - .s_axi_rdata (ddr_axi_rdata), - .s_axi_rlast (ddr_axi_rlast), - .s_axi_rresp (ddr_axi_rresp), - .s_axi_rready(ddr_axi_rready) - ); - -endmodule diff --git a/lib/hardware/buses/iob2axi/hardware/src/iob2axi.v b/lib/hardware/buses/iob2axi/hardware/src/iob2axi.v deleted file mode 100644 index 7b06ec5d6..000000000 --- a/lib/hardware/buses/iob2axi/hardware/src/iob2axi.v +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob2axi #( - parameter ADDR_W = 0, - parameter DATA_W = 0, - // AXI-4 Full I/F parameters - parameter AXI_ADDR_W = ADDR_W, - parameter AXI_DATA_W = DATA_W -) ( - // - // Control I/F - // - input run_i, - input direction_i, // 0 for reading, 1 for writing - input [ADDR_W-1:0] addr_i, - output ready_o, - output error_o, - - // - // Native Slave I/F - // - input s_valid_i, - input [ ADDR_W-1:0] s_addr_i, - input [ DATA_W-1:0] s_wdata_i, - input [DATA_W/8-1:0] s_wstrb_i, - output [ DATA_W-1:0] s_rdata_o, - output s_ready_o, - - // - // AXI-4 Full Master I/F - // - `include "iob2axi_m_axi_m_port.vs" - `include "iob2axi_clk_rst_s_port.vs" -); - - `include "iob_functions.vs" - - wire run_int; - wire run_wr, run_rd; - wire ready_int; - wire ready_rd, ready_wr; - wire error_rd, error_wr; - - wire in_fifo_ready, out_fifo_ready; - - wire rd_valid, wr_valid; - wire [ADDR_W-1:0] rd_addr, wr_addr; //** - wire [DATA_W-1:0] rd_wdata, wr_rdata; - wire [DATA_W/8-1:0] rd_wstrb, wr_rstrb; - wire rd_ready, wr_ready; - - assign ready_o = ready_int & ~run_int; - assign error_o = error_rd | error_wr; - - assign s_ready_o = |s_wstrb_i ? in_fifo_ready : out_fifo_ready; - - // - // Input FIFO - // - wire in_fifo_full; - wire in_fifo_wr = s_valid_i & |s_wstrb_i & ~in_fifo_full; - wire [DATA_W+DATA_W/8-1:0] in_fifo_wdata = {s_wdata_i, s_wstrb_i}; - - wire in_fifo_empty; - wire in_fifo_rd = wr_valid; - wire [DATA_W+DATA_W/8-1:0] in_fifo_rdata; - - wire [`AXI_LEN_W:0] in_fifo_level; - - reg in_fifo_empty_reg; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - in_fifo_empty_reg <= 1'b1; - end else begin - in_fifo_empty_reg <= in_fifo_empty; - end - end - - assign wr_rdata = in_fifo_rdata[DATA_W/8+:DATA_W]; - assign wr_rstrb = in_fifo_rdata[0+:DATA_W/8]; - - reg wr_ready_int; - assign wr_ready = wr_ready_int & ~in_fifo_empty_reg; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - wr_ready_int <= 1'b0; - end else begin - wr_ready_int <= wr_valid; - end - end - - reg in_fifo_ready_int; - assign in_fifo_ready = in_fifo_ready_int & ~in_fifo_full; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - in_fifo_ready_int <= 1'b0; - end else begin - in_fifo_ready_int <= s_valid_i & |s_wstrb_i; - end - end - - iob_fifo_sync #( - .W_DATA_W(DATA_W + DATA_W / 8), - .R_DATA_W(DATA_W + DATA_W / 8), - .ADDR_W (`AXI_LEN_W) - ) iob_fifo_sync0 ( - .clk_i(clk_i), - .rst_i(rst_i), - - .w_en_i (in_fifo_wr), - .w_data_i(in_fifo_wdata), - .w_full_o(in_fifo_full), - - .r_en_i (in_fifo_rd), - .r_data_o (in_fifo_rdata), - .r_empty_o(in_fifo_empty), - - .level_o(in_fifo_level) - ); - - // - // Output FIFO - // - wire out_fifo_full; - wire out_fifo_wr = rd_valid & |rd_wstrb & ~out_fifo_full; - wire [DATA_W-1:0] out_fifo_wdata = rd_wdata; - - wire out_fifo_empty; - wire out_fifo_rd = s_valid_i & ~|s_wstrb_i & ~out_fifo_empty; - wire [DATA_W-1:0] out_fifo_rdata; - - wire [`AXI_LEN_W:0] out_fifo_level; - wire [`AXI_LEN_W:0] out_fifo_capacity = {1'b1, `AXI_LEN_W'd0} - out_fifo_level; - - reg out_fifo_empty_reg; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - out_fifo_empty_reg <= 1'b1; - end else begin - out_fifo_empty_reg <= out_fifo_empty; - end - end - - reg rd_ready_int; - assign rd_ready = rd_ready_int & ~out_fifo_full; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - rd_ready_int <= 1'b0; - end else begin - rd_ready_int <= rd_valid; - end - end - - assign s_rdata_o = out_fifo_rdata; - - reg out_fifo_ready_int; - assign out_fifo_ready = out_fifo_ready_int & ~out_fifo_empty_reg; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - out_fifo_ready_int <= 1'b0; - end else begin - out_fifo_ready_int <= s_valid_i & ~|s_wstrb_i; - end - end - - iob_fifo_sync #( - .W_DATA_W(DATA_W), - .R_DATA_W(DATA_W), - .ADDR_W (`AXI_LEN_W) - ) iob_fifo_sync1 ( - .clk_i(clk_i), - .rst_i(rst_i), - - .w_en_i (out_fifo_wr), - .w_data_i(out_fifo_wdata), - .w_full_o(out_fifo_full), - - .r_en_i (out_fifo_rd), - .r_data_o (out_fifo_rdata), - .r_empty_o(out_fifo_empty), - - .level_o(out_fifo_level) - ); - - // - // Compute next run - // - wire [`AXI_LEN_W:0] length_int = direction ? in_fifo_level : out_fifo_capacity; - - reg [`AXI_LEN_W-1:0] count; - wire count_en = ~&count & |length_int; - - assign run_int = ready_int & |length_int & (length_int[`AXI_LEN_W] | &count); - assign run_wr = direction ? run_int : 1'b0; - assign run_rd = direction ? 1'b0 : run_int; - - assign ready_int = ready_wr & ready_rd; - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - count <= `AXI_LEN_W'd0; - end else if (run_int) begin - count <= `AXI_LEN_W'd0; - end else if (count_en) begin - count <= count + 1'b1; - end - end - - // - // Compute first address and burst length for the next data transfer - // - localparam WADDR_W = ADDR_W - $clog2(DATA_W / 8); // Word address width - - reg [`AXI_LEN_W-1:0] length_burst; - - reg [ADDR_W-1:0] addr_int; - reg [WADDR_W-1:0] addr_int_next; - wire [WADDR_W-1:0] addr4k = {addr_int[ADDR_W-1:12], {(12 - (ADDR_W - WADDR_W)) {1'b1}}}; - wire [WADDR_W-1:0] addrRem = addr_int[ADDR_W-1-:WADDR_W] + length_int - 1'b1; - wire [WADDR_W-1:0] minAddr = iob_min(addr4k, addrRem); - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - addr_int <= {ADDR_W{1'b0}}; - end else if (run_i) begin - addr_int <= addr_i; - end else if (run_int) begin - addr_int <= {addr_int_next, {(ADDR_W - WADDR_W) {1'b0}}}; - end - end - - always @* begin - addr_int_next = minAddr + 1'b1; - - if (minAddr == addr4k) begin - length_burst = addr4k - addr_int[ADDR_W-1-:WADDR_W]; - end else begin // minAddr == addrRem - length_burst = length_int - 1'b1; - end - end - - // - // AXI Read - // - iob2axi_rd #( - .ADDR_W(ADDR_W), - .DATA_W(DATA_W) - ) iob2axi_rd0 ( - .clk_i(clk_i), - .rst_i(rst_i), - - // Control I/F - .run_i (run_rd), - .addr_i (addr_int), - .length_i(length_burst), - .ready_o (ready_rd), - .error_o (error_rd), - - // AXI-4 full read master I/F - `include "iob2axi_m_m_axi_read_portmap.vs" - // Native Master Write I/F - .m_valid_o(rd_valid), - .m_addr_o (rd_addr), - .m_wdata_o(rd_wdata), - .m_wstrb_o(rd_wstrb), - .m_ready_i(rd_ready) - ); - - // - // AXI Write - // - iob2axi_wr #( - .ADDR_W(ADDR_W), - .DATA_W(DATA_W) - ) iob2axi_wr0 ( - .clk_i(clk_i), - .rst_i(rst_i), - - // Control I/F - .run_i (run_wr), - .addr_i (addr_int), - .length_i(length_burst), - .ready_o (ready_wr), - .error_o (error_wr), - - // AXI-4 full write master I/F - `include "iob2axi_m_m_axi_write_portmap.vs" - - // Native Master Read I/F - .m_valid_o(wr_valid), - .m_addr_o (wr_addr), - .m_rdata_i(wr_rdata), - .m_rstrb_i(wr_rstrb), - .m_ready_i(wr_ready) - ); - -endmodule diff --git a/lib/hardware/buses/iob2axi/hardware/src/iob2axi_rd.v b/lib/hardware/buses/iob2axi/hardware/src/iob2axi_rd.v deleted file mode 100644 index f0857a880..000000000 --- a/lib/hardware/buses/iob2axi/hardware/src/iob2axi_rd.v +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - -module iob2axi_rd #( - parameter ADDR_W = 0, - parameter DATA_W = 0, - // AXI-4 Full I/F parameters - parameter AXI_ADDR_W = ADDR_W, - parameter AXI_DATA_W = DATA_W -) ( - input clk_i, - input rst_i, - - // - // Control I/F - // - input run_i, - input [ ADDR_W-1:0] addr_i, - input [`AXI_LEN_W-1:0] length_i, - output reg ready_o, - output reg error_o, - - // - // AXI-4 Full Master Read I/F - // - `include "iob2axi_rd_m_axi_read_m_port.vs" - - // - // Native Master Write I/F - // - output reg m_valid_o, - output [ ADDR_W-1:0] m_addr_o, - output [ DATA_W-1:0] m_wdata_o, - output [DATA_W/8-1:0] m_wstrb_o, - input m_ready_i -); - - localparam axi_arsize = $clog2(DATA_W / 8); - - localparam ADDR_HS = 1'h0, READ = 1'h1; - - // State signals - reg state, state_nxt; - - // Counter, error and ready register signals - reg [`AXI_LEN_W-1:0] counter, counter_nxt; - reg error_nxt; - reg ready_nxt; - - reg m_axi_arvalid_int; - reg m_axi_rready_int; - - // Control register signals - reg [ ADDR_W-1:0] addr_reg; - reg [`AXI_LEN_W-1:0] length_reg; - - // Hold - reg m_valid_reg; - wire hold = m_valid_reg & ~m_ready_i; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - m_valid_reg <= 1'b0; - end else begin - m_valid_reg <= m_valid_o; - end - end - - reg [DATA_W-1:0] m_axi_rdata_reg; - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - m_axi_rdata_reg <= {DATA_W{1'b0}}; - end else if (~hold) begin - m_axi_rdata_reg <= m_axi_rdata_i; - end - end - - assign m_wdata_o = hold ? m_axi_rdata_reg : m_axi_rdata_i; - assign m_wstrb_o = {(DATA_W / 8) {1'b1}}; - - // Read address - assign m_axi_arid_o = `AXI_ID_W'd0; - assign m_axi_arvalid_o = m_axi_arvalid_int; - assign m_axi_araddr_o = run_i ? addr_i : addr_reg; - assign m_axi_arlen_o = run_i ? length_i : length_reg; - assign m_axi_arsize_o = axi_arsize; - assign m_axi_arburst_o = `AXI_BURST_W'd1; - assign m_axi_arlock_o = `AXI_LOCK_W'b0; - assign m_axi_arcache_o = `AXI_CACHE_W'd2; - assign m_axi_arprot_o = `AXI_PROT_W'd2; - assign m_axi_arqos_o = `AXI_QOS_W'd0; - - // Read - assign m_axi_rready_o = m_axi_rready_int; - - // Counter, error and ready registers - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - counter <= `AXI_LEN_W'd0; - error_o <= 1'b0; - ready_o <= 1'b1; - end else begin - counter <= counter_nxt; - error_o <= error_nxt; - ready_o <= ready_nxt; - end - end - - // Control registers - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - addr_reg <= {ADDR_W{1'b0}}; - length_reg <= `AXI_LEN_W'd0; - end else if (run_i) begin - addr_reg <= addr_i; - length_reg <= length_i; - end - end - - wire rst_valid_int = (state_nxt == ADDR_HS) ? 1'b1 : 1'b0; - reg arvalid_int; - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - arvalid_int <= 1'b0; - end else if (rst_valid_int) begin - arvalid_int <= 1'b1; - end else if (m_axi_arready_i) begin - arvalid_int <= 1'b0; - end - end - - // - // FSM - // - - // State register - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - state <= ADDR_HS; - end else begin - state <= state_nxt; - end - end - - // State machine - always @* begin - state_nxt = state; - - error_nxt = error_o; - ready_nxt = 1'b0; - counter_nxt = counter; - - m_valid_o = 1'b0; - - m_axi_arvalid_int = 1'b0; - m_axi_rready_int = 1'b0; - - case (state) - // Read address handshake - ADDR_HS: begin - counter_nxt = `AXI_LEN_W'd0; - ready_nxt = 1'b1; - - if (run_i) begin - m_axi_arvalid_int = 1'b1; - - if (m_axi_arready_i) begin - state_nxt = READ; - - ready_nxt = 1'b0; - end - end - end - // Read data - READ: begin - m_valid_o = m_axi_rvalid_i; - - m_axi_arvalid_int = arvalid_int; - m_axi_rready_int = ~hold; - - if (~hold & m_axi_rvalid_i) begin - if (counter == length_reg) begin - error_nxt = |{~m_axi_rlast_i, m_axi_rresp_i}; - - state_nxt = ADDR_HS; - end - - counter_nxt = counter + 1'b1; - end - end - endcase - end - -endmodule diff --git a/lib/hardware/buses/iob2axi/hardware/src/iob2axi_wr.v b/lib/hardware/buses/iob2axi/hardware/src/iob2axi_wr.v deleted file mode 100644 index b7ec82533..000000000 --- a/lib/hardware/buses/iob2axi/hardware/src/iob2axi_wr.v +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - -module iob2axi_wr #( - parameter ADDR_W = 0, - parameter DATA_W = 0, - // AXI-4 Full I/F parameters - parameter AXI_ADDR_W = ADDR_W, - parameter AXI_DATA_W = DATA_W -) ( - input clk_i, - input rst_i, - - // - // Control I/F - // - input run_i, - input [ ADDR_W-1:0] addr_i, - input [`AXI_LEN_W-1:0] length_i, - output reg ready_o, - output reg error_o, - - // - // AXI-4 Full Master Write I/F - // - `include "iob2axi_wr_m_axi_write_m_port.vs" - - // - // Native Master Read I/F - // - output reg m_valid_o, - output [ ADDR_W-1:0] m_addr_o, - input [ DATA_W-1:0] m_rdata_i, - input [DATA_W/8-1:0] m_rstrb_i, - input m_ready_i -); - - localparam axi_awsize = $clog2(DATA_W / 8); - - localparam ADDR_HS = 2'h0, WRITE = 2'h1, W_RESPONSE = 2'h2; - - // State signals - reg [1:0] state, state_nxt; - - // Counter, error and ready register signals - reg [`AXI_LEN_W:0] counter, counter_nxt; - reg error_nxt; - reg ready_nxt; - - reg m_axi_awvalid_int; - reg m_axi_wvalid_int; - reg m_axi_wlast_int; - reg m_axi_bready_int; - - // Control register signals - reg [ ADDR_W-1:0] addr_reg; - reg [`AXI_LEN_W-1:0] length_reg; - - // Write address - assign m_axi_awid_o = `AXI_ID_W'd0; - assign m_axi_awvalid_o = m_axi_awvalid_int; - assign m_axi_awaddr_o = run_i ? addr_i : addr_reg; - assign m_axi_awlen_o = run_i ? length_i : length_reg; - assign m_axi_awsize_o = axi_awsize; - assign m_axi_awburst_o = `AXI_BURST_W'd1; - assign m_axi_awlock_o = `AXI_LOCK_W'd0; - assign m_axi_awcache_o = `AXI_CACHE_W'd2; - assign m_axi_awprot_o = `AXI_PROT_W'd2; - assign m_axi_awqos_o = `AXI_QOS_W'd0; - - // Write - assign m_axi_wid_o = `AXI_ID_W'd0; - assign m_axi_wvalid_o = m_axi_wvalid_int; - assign m_axi_wdata_o = m_rdata_i; - assign m_axi_wstrb_o = m_rstrb_i; - assign m_axi_wlast_o = m_axi_wlast_int; - - // Write response - assign m_axi_bready_o = m_axi_bready_int; - - // Counter, error and ready registers - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - counter <= `AXI_LEN_W'd0; - error_o <= 1'b0; - ready_o <= 1'b1; - end else begin - counter <= counter_nxt; - error_o <= error_nxt; - ready_o <= ready_nxt; - end - end - - // Control registers - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - addr_reg <= {ADDR_W{1'b0}}; - length_reg <= `AXI_LEN_W'd0; - end else if (run_i) begin - addr_reg <= addr_i; - length_reg <= length_i; - end - end - - // Compute awvalid - wire rst_valid_int = (state_nxt == ADDR_HS) ? 1'b1 : 1'b0; - reg awvalid_int, wvalid_int; - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - awvalid_int <= 1'b0; - wvalid_int <= 1'b0; - end else if (rst_valid_int) begin - awvalid_int <= 1'b1; - wvalid_int <= 1'b0; - end else begin - if (m_axi_awready_i) begin - awvalid_int <= 1'b0; - end - if (m_ready_i) begin - wvalid_int <= 1'b1; - end - end - end - - // - // FSM - // - - // State register - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - state <= ADDR_HS; - end else begin - state <= state_nxt; - end - end - - // State machine - always @* begin - state_nxt = state; - - error_nxt = error_o; - ready_nxt = 1'b0; - counter_nxt = counter; - - m_valid_o = 1'b0; - - m_axi_awvalid_int = 1'b0; - m_axi_wvalid_int = 1'b0; - m_axi_wlast_int = 1'b0; - m_axi_bready_int = 1'b1; - - case (state) - // Write address handshake - ADDR_HS: begin - counter_nxt = `AXI_LEN_W'd0; - ready_nxt = 1'b1; - - if (run_i) begin - state_nxt = WRITE; - - m_valid_o = 1'b1; - m_axi_awvalid_int = 1'b1; - ready_nxt = 1'b0; - end - end - // Write data - WRITE: begin - m_valid_o = m_axi_wready_i; - - m_axi_awvalid_int = awvalid_int; - m_axi_wvalid_int = m_ready_i | wvalid_int; - - if (m_ready_i & m_axi_wready_i) begin - if (counter == length_reg) begin - m_valid_o = 1'b0; - m_axi_wlast_int = 1'b1; - state_nxt = W_RESPONSE; - end - - counter_nxt = counter + 1'b1; - end - end - // Write response - W_RESPONSE: begin - if (m_axi_bvalid_i) begin - error_nxt = |m_axi_bresp_i; - - state_nxt = ADDR_HS; - end - end - default: state_nxt = ADDR_HS; - endcase - end - -endmodule diff --git a/lib/hardware/buses/iob2axi/iob2axi.py b/lib/hardware/buses/iob2axi/iob2axi.py deleted file mode 100644 index 488fc2b48..000000000 --- a/lib/hardware/buses/iob2axi/iob2axi.py +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "m_axi_m_port", - "instance_name": "m_axi_m_port_inst", - }, - { - "core_name": "m_axi_write_m_port", - "instance_name": "m_axi_write_m_port_inst", - }, - { - "core_name": "m_axi_read_m_port", - "instance_name": "m_axi_read_m_port_inst", - }, - { - "core_name": "m_m_axi_write_portmap", - "instance_name": "m_m_axi_write_portmap_inst", - }, - { - "core_name": "m_m_axi_read_portmap", - "instance_name": "m_m_axi_read_portmap_inst", - }, - { - "core_name": "iob2axi_wr", - "instance_name": "iob2axi_wr_inst", - }, - { - "core_name": "iob2axi_rd", - "instance_name": "iob2axi_rd_inst", - }, - { - "core_name": "iob_fifo_sync", - "instance_name": "iob_fifo_sync_inst", - }, - { - "core_name": "iob_functions", - "instance_name": "iob_functions_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob2axi/waves.gtkw b/lib/hardware/buses/iob2axi/waves.gtkw deleted file mode 100644 index c3d51a2dd..000000000 --- a/lib/hardware/buses/iob2axi/waves.gtkw +++ /dev/null @@ -1,133 +0,0 @@ -[*] -[*] GTKWave Analyzer v3.3.86 (w)1999-2017 BSI -[*] Fri Feb 18 18:48:05 2022 -[*] -[dumpfile] "/home/jdlopes/git/iob-axi/uut.vcd" -[dumpfile_mtime] "Fri Feb 18 18:44:44 2022" -[dumpfile_size] 1306246 -[savefile] "/home/jdlopes/git/iob-axi/hardware/iob2axi/waves.gtkw" -[timestart] 2775200 -[size] 1920 1017 -[pos] -1 -1 -*-15.797765 2870000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -[treeopen] iob2axi_tb. -[treeopen] iob2axi_tb.uut. -[sst_width] 258 -[signals_width] 286 -[sst_expanded] 1 -[sst_vpaned_height] 289 -@28 -iob2axi_tb.clk -iob2axi_tb.rst -@200 -- -@22 -iob2axi_tb.start_addr[23:0] -@200 -- -@28 -iob2axi_tb.uut.run -iob2axi_tb.uut.direction -iob2axi_tb.uut.error -iob2axi_tb.uut.ready -@200 -- -@22 -iob2axi_tb.uut.addr_int[23:0] -iob2axi_tb.uut.addr4k[21:0] -iob2axi_tb.uut.addrRem[21:0] -iob2axi_tb.uut.minAddr[21:0] -@200 -- -@201 --Native Slave I/F -@28 -iob2axi_tb.uut.s_valid -@24 -iob2axi_tb.uut.s_addr[23:0] -iob2axi_tb.uut.s_wdata[31:0] -@22 -iob2axi_tb.uut.s_wstrb[3:0] -@24 -iob2axi_tb.uut.s_rdata[31:0] -@28 -iob2axi_tb.uut.s_ready -@200 -- --AXI-4 Full Write Master I/F -@28 -iob2axi_tb.uut.m_axi_awvalid -@c00022 -iob2axi_tb.uut.m_axi_awaddr[23:0] -@28 -(0)iob2axi_tb.uut.m_axi_awaddr[23:0] -(1)iob2axi_tb.uut.m_axi_awaddr[23:0] -(2)iob2axi_tb.uut.m_axi_awaddr[23:0] -(3)iob2axi_tb.uut.m_axi_awaddr[23:0] -(4)iob2axi_tb.uut.m_axi_awaddr[23:0] -(5)iob2axi_tb.uut.m_axi_awaddr[23:0] -(6)iob2axi_tb.uut.m_axi_awaddr[23:0] -(7)iob2axi_tb.uut.m_axi_awaddr[23:0] -(8)iob2axi_tb.uut.m_axi_awaddr[23:0] -(9)iob2axi_tb.uut.m_axi_awaddr[23:0] -(10)iob2axi_tb.uut.m_axi_awaddr[23:0] -(11)iob2axi_tb.uut.m_axi_awaddr[23:0] -(12)iob2axi_tb.uut.m_axi_awaddr[23:0] -(13)iob2axi_tb.uut.m_axi_awaddr[23:0] -(14)iob2axi_tb.uut.m_axi_awaddr[23:0] -(15)iob2axi_tb.uut.m_axi_awaddr[23:0] -(16)iob2axi_tb.uut.m_axi_awaddr[23:0] -(17)iob2axi_tb.uut.m_axi_awaddr[23:0] -(18)iob2axi_tb.uut.m_axi_awaddr[23:0] -(19)iob2axi_tb.uut.m_axi_awaddr[23:0] -(20)iob2axi_tb.uut.m_axi_awaddr[23:0] -(21)iob2axi_tb.uut.m_axi_awaddr[23:0] -(22)iob2axi_tb.uut.m_axi_awaddr[23:0] -(23)iob2axi_tb.uut.m_axi_awaddr[23:0] -@1401200 --group_end -@24 -iob2axi_tb.uut.m_axi_awlen[7:0] -@28 -iob2axi_tb.uut.m_axi_awready -@200 -- -@28 -iob2axi_tb.uut.m_axi_wvalid -@24 -iob2axi_tb.uut.m_axi_wdata[31:0] -@22 -iob2axi_tb.uut.m_axi_wstrb[3:0] -@28 -iob2axi_tb.uut.m_axi_wlast -iob2axi_tb.uut.m_axi_wready -@200 -- -@28 -iob2axi_tb.uut.m_axi_bvalid -iob2axi_tb.uut.m_axi_bresp[1:0] -iob2axi_tb.uut.m_axi_bready -@200 -- --AXI-4 Full Read Master I/F -@28 -iob2axi_tb.uut.iob2axi_rd0.state -iob2axi_tb.uut.m_axi_arvalid -@22 -iob2axi_tb.uut.m_axi_araddr[23:0] -@24 -iob2axi_tb.uut.m_axi_arlen[7:0] -@28 -iob2axi_tb.uut.m_axi_arready -@200 -- -@28 -iob2axi_tb.uut.m_axi_rvalid -@24 -iob2axi_tb.uut.iob2axi_rd0.m_axi_rdata[31:0] -@28 -iob2axi_tb.uut.m_axi_rlast -iob2axi_tb.uut.m_axi_rresp[1:0] -iob2axi_tb.uut.m_axi_rready -[pattern_trace] 1 -[pattern_trace] 0 diff --git a/lib/hardware/buses/iob2axi/waves.gtkw.license b/lib/hardware/buses/iob2axi/waves.gtkw.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/buses/iob2axi/waves.gtkw.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/buses/iob2axil/hardware/src/iob2axil.v b/lib/hardware/buses/iob2axil/hardware/src/iob2axil.v deleted file mode 100644 index 8b34d066a..000000000 --- a/lib/hardware/buses/iob2axil/hardware/src/iob2axil.v +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - - -module iob2axil #( - parameter AXIL_ADDR_W = 21, // AXI Lite address bus width in bits - parameter AXIL_DATA_W = 21, // AXI Lite data bus width in bits - parameter ADDR_W = AXIL_ADDR_W, // IOb address bus width in bits - parameter DATA_W = AXIL_DATA_W // IOb data bus width in bits -) ( - // AXI4 Lite master interface - output wire axil_awvalid_o, - input wire axil_awready_i, - output wire [ AXIL_ADDR_W-1:0] axil_awaddr_o, - output wire [ 2:0] axil_awprot_o, - output wire axil_wvalid_o, - input wire axil_wready_i, - output wire [ AXIL_DATA_W-1:0] axil_wdata_o, - output wire [AXIL_DATA_W/8-1:0] axil_wstrb_o, - input wire axil_bvalid_i, - output wire axil_bready_o, - input wire [ 1:0] axil_bresp_i, - output wire axil_arvalid_o, - input wire axil_arready_i, - output wire [ AXIL_ADDR_W-1:0] axil_araddr_o, - output wire [ 2:0] axil_arprot_o, - input wire axil_rvalid_i, - output wire axil_rready_o, - input wire [ AXIL_DATA_W-1:0] axil_rdata_i, - input wire [ 1:0] axil_rresp_i, - - // IOb slave interface - input wire iob_valid_i, - input wire [ ADDR_W-1:0] iob_addr_i, - input wire [ DATA_W-1:0] iob_wdata_i, - input wire [DATA_W/8-1:0] iob_wstrb_i, - output wire iob_rvalid_o, - output wire [ DATA_W-1:0] iob_rdata_o, - output wire iob_ready_o -); - - // - // COMPUTE IOb OUTPUTS - // - assign iob_rvalid_o = axil_rvalid_i; - assign iob_rdata_o = axil_rdata_i; - assign iob_ready_o = (~|iob_wstrb_i) ? (axil_wready_i|axil_awready_i): axil_arready_i; - - // - // COMPUTE AXIL OUTPUTS - // - - // write address - assign axil_awvalid_o = iob_valid_i & |iob_wstrb_i; - assign axil_awaddr_o = iob_addr_i; - assign axil_awprot_o = 3'd2; - - // write - assign axil_wvalid_o = iob_valid_i & |iob_wstrb_i; - assign axil_wdata_o = iob_wdata_i; - assign axil_wstrb_o = iob_wstrb_i; - - // write response - assign axil_bready_o = 1'b1; - - // read address - assign axil_arvalid_o = iob_valid_i & ~|iob_wstrb_i; - assign axil_araddr_o = iob_addr_i; - assign axil_arprot_o = 3'd2; - - // read - assign axil_rready_o = 1'b1; - -endmodule diff --git a/lib/hardware/buses/iob2axil/iob2axil.py b/lib/hardware/buses/iob2axil/iob2axil.py deleted file mode 100644 index 9a0ed457c..000000000 --- a/lib/hardware/buses/iob2axil/iob2axil.py +++ /dev/null @@ -1,68 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "confs": [ - { - "name": "AXIL_ADDR_W", - "descr": "", - "type": "P", - "val": "21", - "min": "1", - "max": "32", - }, - { - "name": "AXIL_DATA_W", - "descr": "", - "type": "P", - "val": "21", - "min": "1", - "max": "32", - }, - { - "name": "ADDR_W", - "descr": "", - "type": "P", - "val": "21", - "min": "1", - "max": "32", - }, - { - "name": "DATA_W", - "descr": "", - "type": "P", - "val": "21", - "min": "1", - "max": "32", - }, - ], - "ports": [ - { - "name": "iob_s", - "descr": "Slave IOb interface", - "interface": { - "type": "iob", - "subtype": "slave", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - { - "name": "axil_m", - "descr": "Master AXI Lite interface", - "interface": { - "type": "axil", - "subtype": "master", - "ADDR_W": "AXIL_ADDR_W", - "DATA_W": "AXIL_DATA_W", - }, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_asym_converter/hardware/src/iob_asym_converter.v b/lib/hardware/buses/iob_asym_converter/hardware/src/iob_asym_converter.v deleted file mode 100644 index 9737df080..000000000 --- a/lib/hardware/buses/iob_asym_converter/hardware/src/iob_asym_converter.v +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_asym_converter #( - parameter W_DATA_W = 21, - parameter R_DATA_W = 21, - parameter ADDR_W = 3, //higher ADDR_W lower DATA_W - //determine W_ADDR_W and R_ADDR_W - parameter MAXDATA_W = iob_max(W_DATA_W, R_DATA_W), - parameter MINDATA_W = iob_min(W_DATA_W, R_DATA_W), - parameter R = MAXDATA_W / MINDATA_W, - parameter MINADDR_W = ADDR_W - $clog2(R), //lower ADDR_W (higher DATA_W) - parameter W_ADDR_W = (W_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W, - parameter R_ADDR_W = (R_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W -) ( - `include "iob_asym_converter_io.vs" -); - - `include "iob_functions.vs" - - //Data is valid after read enable - wire r_data_valid_reg; - iob_reg_r #( - .DATA_W (1), - .RST_VAL(1'b0) - ) r_data_valid_reg_inst ( - `include "iob_asym_converter_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(r_en_i), - .data_o(r_data_valid_reg) - ); - - //Register read data from the memory - wire [MAXDATA_W-1:0] r_data_reg; - iob_reg_re #( - .DATA_W (MAXDATA_W), - .RST_VAL({MAXDATA_W{1'd0}}) - ) r_data_reg_inst ( - `include "iob_asym_converter_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (r_data_valid_reg), - .data_i(ext_mem_r_data_i), - .data_o(r_data_reg) - ); - - reg [MAXDATA_W-1:0] r_data_int; - always @* begin - if (r_data_valid_reg) begin - r_data_int = ext_mem_r_data_i; - end else begin - r_data_int = r_data_reg; - end - end - - //Generate the RAM based on the parameters - generate - if (W_DATA_W > R_DATA_W) begin : g_write_wider - //memory write port - assign ext_mem_w_en_o = {R{w_en_i}}; - assign ext_mem_w_addr_o = w_addr_i; - assign ext_mem_w_data_o = w_data_i; - - //register to hold the LSBs of r_addr - wire [$clog2(R)-1:0] r_addr_lsbs_reg; - iob_reg_e #( - .DATA_W ($clog2(R)), - .RST_VAL({$clog2(R) {1'd0}}) - ) r_addr_reg_inst ( - `include "iob_asym_converter_clk_en_rst_s_s_portmap.vs" - .en_i (r_en_i), - .data_i(r_addr_i[$clog2(R)-1:0]), - .data_o(r_addr_lsbs_reg) - ); - - //memory read port - assign ext_mem_r_en_o = {{(R - 1) {1'd0}}, r_en_i} << r_addr_i[$clog2(R)-1:0]; - assign ext_mem_r_addr_o = r_addr_i[R_ADDR_W-1:$clog2(R)]; - - wire [W_DATA_W-1:0] r_data; - assign r_data = r_data_int >> (r_addr_lsbs_reg * R_DATA_W); - assign r_data_o = r_data[R_DATA_W-1:0]; - - end else if (W_DATA_W < R_DATA_W) begin : g_read_wider - //memory write port - assign ext_mem_w_en_o = {{(R - 1) {1'd0}}, w_en_i} << w_addr_i[$clog2(R)-1:0]; - assign ext_mem_w_data_o = {{(R_DATA_W - W_DATA_W) {1'd0}}, w_data_i} << (w_addr_i[$clog2( - R - )-1:0] * W_DATA_W); - assign ext_mem_w_addr_o = w_addr_i[W_ADDR_W-1:$clog2(R)]; - - //memory read port - assign ext_mem_r_en_o = {R{r_en_i}}; - assign ext_mem_r_addr_o = r_addr_i; - assign r_data_o = r_data_int; - - end else begin : g_same_width - //W_DATA_W == R_DATA_W - //memory write port - assign ext_mem_w_en_o = w_en_i; - assign ext_mem_w_addr_o = w_addr_i; - assign ext_mem_w_data_o = w_data_i; - - //memory read port - assign ext_mem_r_en_o = r_en_i; - assign ext_mem_r_addr_o = r_addr_i; - assign r_data_o = r_data_int; - end - endgenerate -endmodule - diff --git a/lib/hardware/buses/iob_asym_converter/iob_asym_converter.py b/lib/hardware/buses/iob_asym_converter/iob_asym_converter.py deleted file mode 100644 index ac8bdfeb3..000000000 --- a/lib/hardware/buses/iob_asym_converter/iob_asym_converter.py +++ /dev/null @@ -1,140 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "rst_i", - "descr": "Synchronous reset interface", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "write_i", - "descr": "Write interface", - "signals": [ - { - "name": "w_en", - "direction": "input", - "width": 1, - "descr": "Write enable", - }, - { - "name": "w_addr", - "direction": "input", - "width": "W_ADDR_W", - "descr": "Write address", - }, - { - "name": "w_data", - "direction": "input", - "width": "W_DATA_W", - "descr": "Write data", - }, - ], - }, - { - "name": "read", - "descr": "Read interface", - "signals": [ - { - "name": "r_en", - "direction": "input", - "width": 1, - "descr": "Read enable", - }, - { - "name": "r_addr", - "direction": "input", - "width": "R_ADDR_W", - "descr": "Read address", - }, - { - "name": "r_data", - "direction": "output", - "width": "R_DATA_W", - "descr": "Read data", - }, - ], - }, - { - "name": "extmem", - "descr": "External memory interface", - "signals": [ - # Write port - { - "name": "ext_mem_w_en", - "direction": "output", - "width": "R", - "descr": "Memory write enable", - }, - { - "name": "ext_mem_w_addr", - "direction": "output", - "width": "MINADDR_W", - "descr": "Memory write address", - }, - { - "name": "ext_mem_w_data", - "direction": "output", - "width": "MAXDATA_W", - "descr": "Memory write data", - }, - # Read port - { - "name": "ext_mem_r_en", - "direction": "output", - "width": "R", - "descr": "Memory read enable", - }, - { - "name": "ext_mem_r_addr", - "direction": "output", - "width": "MINADDR_W", - "descr": "Memory read address", - }, - { - "name": "ext_mem_r_data", - "direction": "input", - "width": "MAXDATA_W", - "descr": "Memory read data", - }, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg_r", - "instance_name": "iob_reg_r_inst", - }, - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - { - "core_name": "iob_functions", - "instance_name": "iob_functions_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_axil_split/iob_axil_split.py b/lib/hardware/buses/iob_axil_split/iob_axil_split.py deleted file mode 100644 index 3417b618d..000000000 --- a/lib/hardware/buses/iob_axil_split/iob_axil_split.py +++ /dev/null @@ -1,281 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - assert "name" in py_params_dict, print( - "Error: Missing name for generated split module." - ) - assert "num_outputs" in py_params_dict, print( - "Error: Missing number of outputs for generated split module." - ) - - NUM_OUTPUTS = int(py_params_dict["num_outputs"]) - # Number of bits required for output selection - NBITS = (NUM_OUTPUTS - 1).bit_length() - - ADDR_W = int(py_params_dict["addr_w"]) if "addr_w" in py_params_dict else 32 - DATA_W = int(py_params_dict["data_w"]) if "data_w" in py_params_dict else 32 - DATA_SECTION_W = ( - int(py_params_dict["data_section_w"]) - if "data_section_w" in py_params_dict - else 8 - ) - PROT_W = int(py_params_dict["prot_w"]) if "prot_w" in py_params_dict else 3 - RESP_W = int(py_params_dict["resp_w"]) if "resp_w" in py_params_dict else 2 - - axil_signals = [ - ("axil_araddr", "input", ADDR_W), - ("axil_arprot", "input", PROT_W), - ("axil_arvalid", "input", 1), - ("axil_arready", "output", 1), - ("axil_rdata", "output", DATA_W), - ("axil_rresp", "output", RESP_W), - ("axil_rvalid", "output", 1), - ("axil_rready", "input", 1), - ("axil_awaddr", "input", ADDR_W), - ("axil_awprot", "input", PROT_W), - ("axil_awvalid", "input", 1), - ("axil_awready", "output", 1), - ("axil_wdata", "input", DATA_W), - ("axil_wstrb", "input", int(DATA_W / DATA_SECTION_W)), - ("axil_wvalid", "input", 1), - ("axil_wready", "output", 1), - ("axil_bresp", "output", RESP_W), - ("axil_bvalid", "output", 1), - ("axil_bready", "input", 1), - ] - - attributes_dict = { - "name": py_params_dict["name"], - "version": "0.1", - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and async reset", - }, - { - "name": "reset_i", - "descr": "Reset signal", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": "1", - }, - ], - }, - { - "name": "input_s", - "interface": { - "type": "axil", - "subtype": "slave", - "file_prefix": py_params_dict["name"] + "_input_", - "port_prefix": "input_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W, - }, - "descr": "Split input", - }, - ], - } - for port_idx in range(NUM_OUTPUTS): - attributes_dict["ports"].append( - { - "name": f"output_{port_idx}_m", - "interface": { - "type": "axil", - "subtype": "master", - "file_prefix": f"{py_params_dict['name']}_output{port_idx}_", - "port_prefix": f"output{port_idx}_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W - NBITS, - }, - "descr": "Split output interface", - }, - ) - attributes_dict["wires"] = [ - # Output selection signals - { - "name": "sel_reg_en_rst", - "descr": "Enable and reset signal for sel_reg", - "signals": [ - {"name": "sel_reg_en", "width": 1}, - {"name": "rst"}, - ], - }, - { - "name": "sel_reg_data_i", - "descr": "Input of sel_reg", - "signals": [ - {"name": "sel", "width": NBITS}, - ], - }, - { - "name": "sel_reg_data_o", - "descr": "Output of sel_reg", - "signals": [ - {"name": "sel_reg", "width": NBITS}, - ], - }, - { - "name": "output_sel", - "descr": "Select output interface", - "signals": [ - {"name": "sel"}, - ], - }, - { - "name": "output_sel_reg", - "descr": "Registered select output interface", - "signals": [ - {"name": "sel_reg"}, - ], - }, - ] - for signal, direction, width in axil_signals: - if direction == "input": - # Demux signals - attributes_dict["wires"] += [ - { - "name": "demux_" + signal + "_i", - "descr": f"Input of {signal} demux", - "signals": [ - { - "name": "input_" + signal, - }, - ], - }, - { - "name": "demux_" + signal + "_o", - "descr": f"Output of {signal} demux", - "signals": [ - { - "name": "demux_" + signal, - "width": NUM_OUTPUTS * width, - }, - ], - }, - ] - else: # output direction - # Mux signals - attributes_dict["wires"] += [ - { - "name": "mux_" + signal + "_i", - "descr": f"Input of {signal} demux", - "signals": [ - { - "name": "mux_" + signal, - "width": NUM_OUTPUTS * width, - }, - ], - }, - { - "name": "mux_" + signal + "_o", - "descr": f"Output of {signal} demux", - "signals": [ - { - "name": "input_" + signal, - }, - ], - }, - ] - attributes_dict["blocks"] = [ - { - "core_name": "iob_reg_re", - "instance_name": "sel_reg_re", - "parameters": { - "DATA_W": NBITS, - "RST_VAL": f"{NBITS}'b0", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "en_rst_i": "sel_reg_en_rst", - "data_i": "sel_reg_data_i", - "data_o": "sel_reg_data_o", - }, - }, - ] - for signal, direction, width in axil_signals: - if direction == "input": - # Demuxers - attributes_dict["blocks"].append( - { - "core_name": "iob_demux", - "instance_name": "iob_demux_" + signal, - "parameters": { - "DATA_W": width, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel", - "data_i": "demux_" + signal + "_i", - "data_o": "demux_" + signal + "_o", - }, - }, - ) - else: # output direction - # Muxers - attributes_dict["blocks"].append( - { - "core_name": "iob_mux", - "instance_name": "iob_mux_" + signal, - "parameters": { - "DATA_W": width, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel_reg", - "data_i": "mux_" + signal + "_i", - "data_o": "mux_" + signal + "_o", - }, - }, - ) - - attributes_dict["snippets"] = [ - { - # Extract output selection bits from address - "verilog_code": f""" - assign sel = input_axil_arvalid_i ? input_axil_araddr_i[{ADDR_W-1}-:{NBITS}] : input_axil_awaddr_i[{ADDR_W-1}-:{NBITS}]; - assign sel_reg_en = input_axil_arvalid_i | input_axil_awvalid_i; -""", - }, - ] - - verilog_code = "" - # Connect address signal - for port_idx in range(NUM_OUTPUTS): - verilog_code += f""" - assign output{port_idx}_axil_araddr_o = demux_axil_araddr[{port_idx*ADDR_W}+:{ADDR_W-NBITS}]; - assign output{port_idx}_axil_awaddr_o = demux_axil_awaddr[{port_idx*ADDR_W}+:{ADDR_W-NBITS}]; -""" - # Connect other signals - for signal, direction, width in axil_signals: - if signal in ["axil_araddr", "axil_awaddr"]: - continue - - if direction == "input": - # Connect demuxers outputs - for port_idx in range(NUM_OUTPUTS): - verilog_code += f""" - assign output{port_idx}_{signal}_o = demux_{signal}[{port_idx*width}+:{width}]; -""" - else: # Output direction - # Connect muxer inputs - verilog_code += f" assign mux_{signal} = {{" - for port_idx in range(NUM_OUTPUTS - 1, -1, -1): - verilog_code += f"output{port_idx}_{signal}_i, " - verilog_code = verilog_code[:-2] + "};\n" - # Create snippet with muxer and demuxer connections - attributes_dict["snippets"] += [ - { - "verilog_code": verilog_code, - }, - ] - - return attributes_dict diff --git a/lib/hardware/buses/iob_axistream_in/hardware/src/iob_axistream_in.v b/lib/hardware/buses/iob_axistream_in/hardware/src/iob_axistream_in.v deleted file mode 100644 index 75ce3afa6..000000000 --- a/lib/hardware/buses/iob_axistream_in/hardware/src/iob_axistream_in.v +++ /dev/null @@ -1,315 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`include "iob_axistream_in_conf.vh" -`include "iob_axistream_in_csrs_def.vh" - -module iob_axistream_in #( - `include "iob_axistream_in_params.vs" -) ( - `include "iob_axistream_in_io.vs" -); - - localparam R = DATA_W / TDATA_W; - localparam R_W = $clog2(R); - localparam RAM_ADDR_W = FIFO_ADDR_W - $clog2(R); - - //rst and enable synced to axis_clk - wire axis_sw_rst; - wire axis_sw_enable; - - //fifo write - wire axis_fifo_write; - wire axis_fifo_full; - - //tlast detected - wire axis_tlast; - wire axis_tlast_detected; - - //word counter - wire [ DATA_W-1:0] axis_word_count; - wire axis_word_count_en; - - - //fifo read - reg fifo_read; - wire [ DATA_W-1:0] fifo_data; - - //fifo RAM - wire ext_mem_w_clk; - wire [ R-1:0] ext_mem_w_en; - wire [RAM_ADDR_W-1:0] ext_mem_w_addr; - wire [ DATA_W-1:0] ext_mem_w_data; - wire ext_mem_r_clk; - wire [ R-1:0] ext_mem_r_en; - wire [RAM_ADDR_W-1:0] ext_mem_r_addr; - wire [ DATA_W-1:0] ext_mem_r_data; - - reg sys_tvalid; - - `include "iob_axistream_in_wires.vs" - - // configuration control and status register file. - `include "iob_axistream_in_blocks.vs" - - wire tlast_detected_reg; - - //CPU INTERFACE - assign data_rready_rd = ~fifo_empty_rd | sys_tvalid; - assign interrupt_o = fifo_level_rd >= fifo_threshold_wr; - assign data_rvalid_rd = sys_tvalid & (~mode_wr); - assign data_rdata_rd = fifo_data; - - //System Stream output interface - // System output valid only if in system stream mode - assign sys_tvalid_o = sys_tvalid & mode_wr; - assign sys_tdata_o = fifo_data; - - //FIFO read - wire fifo_read_pc; - reg fifo_read_pc_nxt; - always @* begin - //FIFO read FSM - fifo_read_pc_nxt = fifo_read_pc + 1'b1; - fifo_read = 1'b0; - sys_tvalid = 1'b0; - - case (fifo_read_pc) - 0: begin - if (fifo_empty_rd) begin - fifo_read_pc_nxt = fifo_read_pc; - end else if ((sys_tready_i && mode_wr) || (data_ren_rd && !mode_wr)) begin - fifo_read = 1'b1; - fifo_read_pc_nxt = 1'b1; - end else begin - fifo_read_pc_nxt = fifo_read_pc; - end - end - default: begin - sys_tvalid = 1'b1; - fifo_read_pc_nxt = 1'b0; - if ((sys_tready_i && mode_wr) || (data_ren_rd && !mode_wr)) begin - if (~fifo_empty_rd) begin - fifo_read = 1'b1; - fifo_read_pc_nxt = fifo_read_pc; - end - end - end - endcase - end - - wire ready_int; - // Ready if not full and, if in CSR mode, tlast not detected - assign ready_int = ~axis_fifo_full & axis_sw_enable & ~(~mode_wr & tlast_detected_reg); - - //word count enable - assign axis_word_count_en = axis_fifo_write & ~tlast_detected_reg; - - generate - if (R == 1) begin : gen_no_padding - //AXI Stream input interface - assign axis_tready_o = ready_int; - //FIFO write - assign axis_fifo_write = axis_tvalid_i & axis_tready_o; - end else begin : gen_padding - //FIFO write FSM - reg fifo_write_state_nxt; - wire fifo_write_state; - always @* begin - fifo_write_state_nxt = fifo_write_state; - case (fifo_write_state) - 0: begin // Idle - // If tvalid, fifo not full, tlast, and the write wont fill the DATA_W with TDATA_W - if (((axis_tvalid_i & ~axis_fifo_full) & axis_tlast_i) & - axis_word_count[0+:R_W] != {R_W{1'd1}}) begin - fifo_write_state_nxt = 1'b1; - end - end - default: begin // Padding - if (axis_word_count[0+:R_W] == {R_W{1'd1}} && ~axis_fifo_full) begin - fifo_write_state_nxt = 1'b0; - end - end - endcase - end - - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'd0) - ) fifo_write_state_reg ( - .clk_i (axis_clk_i), - .cke_i (axis_cke_i), - .arst_i(axis_arst_i), - .rst_i (axis_sw_rst), - .en_i (axis_sw_enable), - .data_i(fifo_write_state_nxt), - .data_o(fifo_write_state) - ); - - // Ready if not full, if in CSR mode, tlast not detected and not in padding state - assign axis_tready_o = ready_int & ~fifo_write_state; - //FIFO write if tvalid, tlast, not full or in padding state - assign axis_fifo_write = (axis_tvalid_i & axis_tready_o) | fifo_write_state; - end - endgenerate - - //tlast - assign axis_tlast = axis_tlast_i & axis_fifo_write; - - //FIFO read program counter - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'd0) - ) fifo_read_pc_reg ( - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i(arst_i), - .rst_i (axis_sw_rst), - .en_i (axis_sw_enable), - .data_i(fifo_read_pc_nxt), - .data_o(fifo_read_pc) - ); - - // received words counter - iob_counter #( - .DATA_W (DATA_W), - .RST_VAL(0) - ) word_count_inst ( - .clk_i (axis_clk_i), - .cke_i (axis_cke_i), - .arst_i(axis_arst_i), - .rst_i (axis_sw_rst), - .en_i (axis_word_count_en), - .data_o(axis_word_count) - ); - - - //Synchronizers from clk (csrs) to axis domain - iob_sync #( - .DATA_W (1), - .RST_VAL(1'd0) - ) sw_rst ( - .clk_i (axis_clk_i), - .arst_i (axis_arst_i), - .signal_i(soft_reset_wr), - .signal_o(axis_sw_rst) - ); - - iob_sync #( - .DATA_W (1), - .RST_VAL(1'd0) - ) sw_enable ( - .clk_i (axis_clk_i), - .arst_i (axis_arst_i), - .signal_i(enable_wr), - .signal_o(axis_sw_enable) - ); - - //Synchronizers from axis to clk domain (sw_regs) - iob_sync #( - .DATA_W (1), - .RST_VAL(1'd0) - ) tlast_detected_sync ( - .clk_i (clk_i), - .arst_i (arst_i), - .signal_i(tlast_detected_reg), - .signal_o(tlast_detected_rd) - ); - - iob_sync #( - .DATA_W (DATA_W), - .RST_VAL(0) - ) word_counter_sync ( - .clk_i (clk_i), - .arst_i (arst_i), - .signal_i(axis_word_count), - .signal_o(nwords_rd) - ); - - //tlast detection - iob_edge_detect #( - .EDGE_TYPE("rising"), - .OUT_TYPE ("step") - ) tlast_detect ( - .clk_i (axis_clk_i), - .cke_i (axis_cke_i), - .arst_i (axis_arst_i), - .rst_i (axis_sw_rst), - .bit_i (axis_tlast), - .detected_o(axis_tlast_detected) - ); - - iob_reg #( - .DATA_W (1), - .RST_VAL(1'd0) - ) tlast_detect_reg ( - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i(arst_i), - .data_i(axis_tlast_detected), - .data_o(tlast_detected_reg) - ); - - //FIFOs RAM - genvar p; - generate - for (p = 0; p < R; p = p + 1) begin : gen_fifo_ram - iob_ram_at2p #( - .DATA_W(TDATA_W), - .ADDR_W(RAM_ADDR_W) - ) iob_ram_at2p ( - .w_clk_i (ext_mem_w_clk), - .w_en_i (ext_mem_w_en[p]), - .w_addr_i(ext_mem_w_addr), - .w_data_i(ext_mem_w_data[p*TDATA_W+:TDATA_W]), - - .r_clk_i (ext_mem_r_clk), - .r_en_i (ext_mem_r_en[p]), - .r_addr_i(ext_mem_r_addr), - .r_data_o(ext_mem_r_data[p*TDATA_W+:TDATA_W]) - ); - end - endgenerate - - //async fifo - iob_fifo_async #( - .W_DATA_W(TDATA_W), - .R_DATA_W(DATA_W), - .ADDR_W (FIFO_ADDR_W) - ) data_fifo ( - .ext_mem_w_clk_o (ext_mem_w_clk), - .ext_mem_w_en_o (ext_mem_w_en), - .ext_mem_w_addr_o(ext_mem_w_addr), - .ext_mem_w_data_o(ext_mem_w_data), - .ext_mem_r_clk_o (ext_mem_r_clk), - .ext_mem_r_en_o (ext_mem_r_en), - .ext_mem_r_addr_o(ext_mem_r_addr), - .ext_mem_r_data_i(ext_mem_r_data), - //read port (sys clk domain) - .r_clk_i (clk_i), - .r_cke_i (cke_i), - .r_arst_i (arst_i), - .r_rst_i (soft_reset_wr), - .r_en_i (fifo_read), - .r_data_o (fifo_data), - .r_empty_o (fifo_empty_rd), - .r_full_o (fifo_full_rd), - .r_level_o (fifo_level_rd), - //write port (axis clk domain) - .w_clk_i (axis_clk_i), - .w_cke_i (axis_cke_i), - .w_arst_i (axis_arst_i), - .w_rst_i (axis_sw_rst), - .w_en_i (axis_fifo_write), - .w_data_i (axis_tdata_i), - .w_empty_o (), - .w_full_o (axis_fifo_full), - .w_level_o () - ); - -endmodule - - - diff --git a/lib/hardware/buses/iob_axistream_in/iob_axistream_in.py b/lib/hardware/buses/iob_axistream_in/iob_axistream_in.py deleted file mode 100755 index 67679f825..000000000 --- a/lib/hardware/buses/iob_axistream_in/iob_axistream_in.py +++ /dev/null @@ -1,398 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.3", - "board_list": ["cyclonev_gt_dk", "aes_ku040_db_g"], - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "32", - "max": "32", - "descr": "CPU data bus width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "`IOB_AXISTREAM_IN_CSRS_ADDR_W", - # "val": "5", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "TDATA_W", - "type": "P", - "val": "8", - "min": "1", - "max": "DATA_W", - "descr": "AXI stream data width", - }, - { - "name": "FIFO_ADDR_W", - "type": "P", - "val": "4", - "min": "NA", - "max": "16", - "descr": "FIFO depth (log2)", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "iob_s", - "interface": { - "type": "iob", - "subtype": "slave", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - "descr": "CPU native interface", - }, - { - "name": "interrupt_o", - "descr": "Interrupt signal", - "signals": [ - { - "name": "interrupt", - "direction": "output", - "width": "1", - "descr": "FIFO threshold interrupt signal", - }, - ], - }, - { - "name": "axistream", - "descr": "AXI Stream interface signals", - "signals": [ - { - "name": "axis_clk", - "direction": "input", - "width": "1", - "descr": "Clock.", - }, - { - "name": "axis_cke", - "direction": "input", - "width": "1", - "descr": "Clock enable", - }, - { - "name": "axis_arst", - "direction": "input", - "width": "1", - "descr": "Asynchronous and active high reset.", - }, - { - "name": "axis_tdata", - "direction": "input", - "width": "TDATA_W", - "descr": "Data.", - }, - { - "name": "axis_tvalid", - "direction": "input", - "width": "1", - "descr": "Valid.", - }, - { - "name": "axis_tready", - "direction": "output", - "width": "1", - "descr": "Ready.", - }, - { - "name": "axis_tlast", - "direction": "input", - "width": "1", - "descr": "Last word.", - }, - ], - }, - { - "name": "sys_axis", - "descr": "System AXI Stream interface.", - "signals": [ - { - "name": "sys_tdata", - "direction": "output", - "width": "DATA_W", - "descr": "Data.", - }, - { - "name": "sys_tvalid", - "direction": "output", - "width": "1", - "descr": "Valid.", - }, - { - "name": "sys_tready", - "direction": "input", - "width": "1", - "descr": "Ready.", - }, - ], - }, - ], - "wires": [ - { - "name": "csrs_iob", - "descr": "Internal CSRs IOb interface", - "interface": { - "type": "iob", - "wire_prefix": "csrs_", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - { - "name": "soft_reset", - "descr": "", - "signals": [ - {"name": "soft_reset_wr", "width": 1}, - ], - }, - { - "name": "enable", - "descr": "", - "signals": [ - {"name": "enable_wr", "width": 1}, - ], - }, - { - "name": "data", - "descr": "", - "signals": [ - {"name": "data_rdata_rd", "width": 32}, - {"name": "data_rvalid_rd", "width": 1}, - {"name": "data_ren_rd", "width": 1}, - {"name": "data_rready_rd", "width": 1}, - ], - }, - { - "name": "mode", - "descr": "", - "signals": [ - {"name": "mode_wr", "width": 1}, - ], - }, - { - "name": "nwords", - "descr": "", - "signals": [ - {"name": "nwords_rd", "width": "DATA_W"}, - ], - }, - { - "name": "tlast_detected", - "descr": "", - "signals": [ - {"name": "tlast_detected_rd", "width": 1}, - ], - }, - { - "name": "fifo_full", - "descr": "", - "signals": [ - {"name": "fifo_full_rd", "width": 1}, - ], - }, - { - "name": "fifo_empty", - "descr": "", - "signals": [ - {"name": "fifo_empty_rd", "width": 1}, - ], - }, - { - "name": "fifo_threshold", - "descr": "", - "signals": [ - {"name": "fifo_threshold_wr", "width": "FIFO_ADDR_W+1"}, - ], - }, - { - "name": "fifo_level", - "descr": "", - "signals": [ - {"name": "fifo_level_rd", "width": "FIFO_ADDR_W+1"}, - ], - }, - ], - "blocks": [ - { - "core_name": "csrs", - "instance_name": "csrs_inst", - "instance_description": "Control/Status Registers", - "csrs": [ - { - "name": "axistream", - "descr": "AXI Stream software accessible registers.", - "regs": [ - { - "name": "soft_reset", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Soft reset.", - }, - { - "name": "enable", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Enable peripheral.", - }, - { - "name": "data", - "type": "R", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": False, - "descr": "Data output.", - }, - { - "name": "mode", - "type": "W", - "n_bits": "1", - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Sets the operation mode: (0) data is read using CSR; (1) data is read using system axistream interface.", - }, - { - "name": "nwords", - "type": "R", - "n_bits": "DATA_W", - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Read the number of words (with TDATA_W bits) written to the FIFO.", - }, - { - "name": "tlast_detected", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Read the TLAST detected status.", - }, - ], - }, - { - "name": "fifo", - "descr": "FIFO related registers", - "regs": [ - { - "name": "fifo_full", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Full (1), or non-full (0).", - }, - { - "name": "fifo_empty", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Full (1), or non-full (0).", - }, - { - "name": "fifo_threshold", - "type": "W", - # FIXME: Fix csrs.py block of py2hwsw to support these parameters - # "n_bits": "FIFO_ADDR_W+1", - "n_bits": "4+1", - "rst_val": 8, - "log2n_items": 0, - "autoreg": True, - "descr": "FIFO threshold level for interrupt signal", - }, - { - "name": "fifo_level", - "type": "R", - # FIXME: Fix csrs.py block of py2hwsw to support these parameters - # "n_bits": "FIFO_ADDR_W+1", - "n_bits": "4+1", - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Current FIFO level", - }, - ], - }, - ], - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "iob_s", - "csrs_iob_o": "csrs_iob", - # Register interfaces - "soft_reset": "soft_reset", - "enable": "enable", - "data": "data", - "mode": "mode", - "nwords": "nwords", - "tlast_detected": "tlast_detected", - "fifo_full": "fifo_full", - "fifo_empty": "fifo_empty", - "fifo_threshold": "fifo_threshold", - "fifo_level": "fifo_level", - }, - }, - # TODO: Connect remaining blocks - { - "core_name": "iob_fifo_async", - "instance_name": "iob_fifo_async_inst", - "instantiate": False, - }, - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - "instantiate": False, - }, - { - "core_name": "iob_ram_at2p", - "instance_name": "iob_ram_at2p_inst", - "instantiate": False, - }, - { - "core_name": "iob_sync", - "instance_name": "iob_sync_inst", - "instantiate": False, - }, - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - "instantiate": False, - }, - { - "core_name": "iob_edge_detect", - "instance_name": "iob_edge_detect_inst", - "instantiate": False, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_axistream_in/software/linux/Readme.md b/lib/hardware/buses/iob_axistream_in/software/linux/Readme.md deleted file mode 100644 index 9fc5e6851..000000000 --- a/lib/hardware/buses/iob_axistream_in/software/linux/Readme.md +++ /dev/null @@ -1,21 +0,0 @@ - - -# IOb AXIStream In Linux Kernel Drivers -- Structure: - - `drivers/`: directory with linux kernel module drivers for - iob_axistream_in - - `iob_axistream_in_main.c`: driver source - - `[iob_axistream_in.h]` and `[iob_axistream_in_sysfs.h]`: header files - generated by: - ```bash - python3 .path/to/iob-linux/scripts/drivers.py iob_axistream_in -o [output_dir] - ``` - - `driver.mk`: makefile segment with `iob_axistream_in-obj:` target for driver - compilation - - `iob_axistream_in.dts`: device tree template with iob_axistream_in node - - manually add the `axistream_in` node to the system device tree so the - iob_axistream_in is recognized by the linux kernel diff --git a/lib/hardware/buses/iob_axistream_in/software/linux/drivers/driver.mk b/lib/hardware/buses/iob_axistream_in/software/linux/drivers/driver.mk deleted file mode 100644 index f01138534..000000000 --- a/lib/hardware/buses/iob_axistream_in/software/linux/drivers/driver.mk +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -iob_axistream_in-objs := iob_axistream_in_main.o iob_class/iob_class_utils.o diff --git a/lib/hardware/buses/iob_axistream_in/software/linux/drivers/iob_axistream_in_main.c b/lib/hardware/buses/iob_axistream_in/software/linux/drivers/iob_axistream_in_main.c deleted file mode 100644 index f659b3bd7..000000000 --- a/lib/hardware/buses/iob_axistream_in/software/linux/drivers/iob_axistream_in_main.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* iob_axistream_in_main.c: driver for iob_axistream_in - * using device platform. No hardcoded hardware address: - * 1. load driver: insmod iob_axistream_in.ko - * 2. run user app: ./user/user - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iob_axistream_in.h" -#include "iob_class/iob_class_utils.h" - -static int iob_axistream_in_probe(struct platform_device *); -static int iob_axistream_in_remove(struct platform_device *); - -static ssize_t iob_axistream_in_read(struct file *, char __user *, size_t, - loff_t *); -static ssize_t iob_axistream_in_write(struct file *, const char __user *, - size_t, loff_t *); -static loff_t iob_axistream_in_llseek(struct file *, loff_t, int); -static int iob_axistream_in_open(struct inode *, struct file *); -static int iob_axistream_in_release(struct inode *, struct file *); - -static struct iob_data iob_axistream_in_data = {0}; -DEFINE_MUTEX(iob_axistream_in_mutex); - -#include "iob_axistream_in_sysfs.h" - -static const struct file_operations iob_axistream_in_fops = { - .owner = THIS_MODULE, - .write = iob_axistream_in_write, - .read = iob_axistream_in_read, - .llseek = iob_axistream_in_llseek, - .open = iob_axistream_in_open, - .release = iob_axistream_in_release, -}; - -static const struct of_device_id of_iob_axistream_in_match[] = { - {.compatible = "iobundle,axistream_in0"}, - {}, -}; - -static struct platform_driver iob_axistream_in_driver = { - .driver = - { - .name = "iob_axistream_in", - .owner = THIS_MODULE, - .of_match_table = of_iob_axistream_in_match, - }, - .probe = iob_axistream_in_probe, - .remove = iob_axistream_in_remove, -}; - -// -// Module init and exit functions -// -static int iob_axistream_in_probe(struct platform_device *pdev) { - struct resource *res; - int result = 0; - - if (iob_axistream_in_data.device != NULL) { - pr_err("[Driver] %s: No more devices allowed!\n", - IOB_AXISTREAM_IN_DRIVER_NAME); - - return -ENODEV; - } - - pr_info("[Driver] %s: probing.\n", IOB_AXISTREAM_IN_DRIVER_NAME); - - // Get the I/O region base address - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - pr_err("[Driver]: Failed to get I/O resource!\n"); - result = -ENODEV; - goto r_get_resource; - } - - // Request and map the I/O region - iob_axistream_in_data.regbase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(iob_axistream_in_data.regbase)) { - result = PTR_ERR(iob_axistream_in_data.regbase); - goto r_ioremmap; - } - iob_axistream_in_data.regsize = resource_size(res); - - // Alocate char device - result = alloc_chrdev_region(&iob_axistream_in_data.devnum, 0, 1, - IOB_AXISTREAM_IN_DRIVER_NAME); - if (result) { - pr_err("%s: Failed to allocate device number!\n", - IOB_AXISTREAM_IN_DRIVER_NAME); - goto r_alloc_region; - } - - cdev_init(&iob_axistream_in_data.cdev, &iob_axistream_in_fops); - - result = - cdev_add(&iob_axistream_in_data.cdev, iob_axistream_in_data.devnum, 1); - if (result) { - pr_err("%s: Char device registration failed!\n", - IOB_AXISTREAM_IN_DRIVER_NAME); - goto r_cdev_add; - } - - // Create device class // todo: make a dummy driver just to create and own the - // class: https://stackoverflow.com/a/16365027/8228163 - if ((iob_axistream_in_data.class = - class_create(THIS_MODULE, IOB_AXISTREAM_IN_DRIVER_CLASS)) == NULL) { - printk("Device class can not be created!\n"); - goto r_class; - } - - // Create device file - iob_axistream_in_data.device = device_create( - iob_axistream_in_data.class, NULL, iob_axistream_in_data.devnum, NULL, - IOB_AXISTREAM_IN_DRIVER_NAME); - if (iob_axistream_in_data.device == NULL) { - printk("Can not create device file!\n"); - goto r_device; - } - - result = - iob_axistream_in_create_device_attr_files(iob_axistream_in_data.device); - if (result) { - pr_err("Cannot create device attribute file......\n"); - goto r_dev_file; - } - - dev_info(&pdev->dev, "initialized.\n"); - goto r_ok; - -r_dev_file: - iob_axistream_in_remove_device_attr_files(&iob_axistream_in_data); -r_device: - class_destroy(iob_axistream_in_data.class); -r_class: - cdev_del(&iob_axistream_in_data.cdev); -r_cdev_add: - unregister_chrdev_region(iob_axistream_in_data.devnum, 1); -r_alloc_region: - // iounmap is managed by devm -r_ioremmap: -r_get_resource: -r_ok: - - return result; -} - -static int iob_axistream_in_remove(struct platform_device *pdev) { - iob_axistream_in_remove_device_attr_files(&iob_axistream_in_data); - class_destroy(iob_axistream_in_data.class); - cdev_del(&iob_axistream_in_data.cdev); - unregister_chrdev_region(iob_axistream_in_data.devnum, 1); - // Note: no need for iounmap, since we are using devm_ioremap_resource() - - dev_info(&pdev->dev, "exiting.\n"); - - return 0; -} - -static int __init iob_axistream_in_init(void) { - pr_info("[Driver] %s: initializing.\n", IOB_AXISTREAM_IN_DRIVER_NAME); - - return platform_driver_register(&iob_axistream_in_driver); -} - -static void __exit iob_axistream_in_exit(void) { - pr_info("[Driver] %s: exiting.\n", IOB_AXISTREAM_IN_DRIVER_NAME); - platform_driver_unregister(&iob_axistream_in_driver); -} - -// -// File operations -// - -static int iob_axistream_in_open(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_axistream_in device opened\n"); - - if (!mutex_trylock(&iob_axistream_in_mutex)) { - pr_info("Another process is accessing the device\n"); - - return -EBUSY; - } - - return 0; -} - -static int iob_axistream_in_release(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_axistream_in device closed\n"); - - mutex_unlock(&iob_axistream_in_mutex); - - return 0; -} - -static ssize_t iob_axistream_in_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) { - int size = 0; - u32 value = 0; - - /* read value from register */ - switch (*ppos) { - case IOB_AXISTREAM_IN_DATA_ADDR: - value = - iob_data_read_reg(iob_axistream_in_data.regbase, - IOB_AXISTREAM_IN_DATA_ADDR, IOB_AXISTREAM_IN_DATA_W); - size = (IOB_AXISTREAM_IN_DATA_W >> 3); // bit to bytes - pr_info("[Driver] Read DATA!\n"); - break; - case IOB_AXISTREAM_IN_NWORDS_ADDR: - value = iob_data_read_reg(iob_axistream_in_data.regbase, - IOB_AXISTREAM_IN_NWORDS_ADDR, - IOB_AXISTREAM_IN_NWORDS_W); - size = (IOB_AXISTREAM_IN_NWORDS_W >> 3); // bit to bytes - pr_info("[Driver] Read NWORDS!\n"); - break; - case IOB_AXISTREAM_IN_TLAST_DETECTED_ADDR: - value = iob_data_read_reg(iob_axistream_in_data.regbase, - IOB_AXISTREAM_IN_TLAST_DETECTED_ADDR, - IOB_AXISTREAM_IN_TLAST_DETECTED_W); - size = (IOB_AXISTREAM_IN_TLAST_DETECTED_W >> 3); // bit to bytes - pr_info("[Driver] Read TLAST_DETECTED!\n"); - break; - case IOB_AXISTREAM_IN_FIFO_FULL_ADDR: - value = iob_data_read_reg(iob_axistream_in_data.regbase, - IOB_AXISTREAM_IN_FIFO_FULL_ADDR, - IOB_AXISTREAM_IN_FIFO_FULL_W); - size = (IOB_AXISTREAM_IN_FIFO_FULL_W >> 3); // bit to bytes - pr_info("[Driver] Read FIFO_FULL!\n"); - break; - case IOB_AXISTREAM_IN_FIFO_EMPTY_ADDR: - value = iob_data_read_reg(iob_axistream_in_data.regbase, - IOB_AXISTREAM_IN_FIFO_EMPTY_ADDR, - IOB_AXISTREAM_IN_FIFO_EMPTY_W); - size = (IOB_AXISTREAM_IN_FIFO_EMPTY_W >> 3); // bit to bytes - pr_info("[Driver] Read FIFO_EMPTY!\n"); - break; - case IOB_AXISTREAM_IN_FIFO_LEVEL_ADDR: - value = iob_data_read_reg(iob_axistream_in_data.regbase, - IOB_AXISTREAM_IN_FIFO_LEVEL_ADDR, - IOB_AXISTREAM_IN_FIFO_LEVEL_W); - size = (IOB_AXISTREAM_IN_FIFO_LEVEL_W >> 3); // bit to bytes - pr_info("[Driver] Read FIFO_LEVEL!\n"); - break; - case IOB_AXISTREAM_IN_VERSION_ADDR: - value = iob_data_read_reg(iob_axistream_in_data.regbase, - IOB_AXISTREAM_IN_VERSION_ADDR, - IOB_AXISTREAM_IN_VERSION_W); - size = (IOB_AXISTREAM_IN_VERSION_W >> 3); // bit to bytes - pr_info("[Driver] Read version!\n"); - break; - default: - // invalid address - no bytes read - return 0; - } - - // Read min between count and REG_SIZE - if (size > count) - size = count; - - if (copy_to_user(buf, &value, size)) - return -EFAULT; - - return count; -} - -static ssize_t iob_axistream_in_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) { - int size = 0; - u32 value = 0; - - switch (*ppos) { - case IOB_AXISTREAM_IN_SOFT_RESET_ADDR: - size = (IOB_AXISTREAM_IN_SOFT_RESET_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_in_data.regbase, value, - IOB_AXISTREAM_IN_SOFT_RESET_ADDR, - IOB_AXISTREAM_IN_SOFT_RESET_W); - pr_info("[Driver] SOFT_RESET iob_axistream_in: 0x%x\n", value); - break; - case IOB_AXISTREAM_IN_ENABLE_ADDR: - size = (IOB_AXISTREAM_IN_ENABLE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_in_data.regbase, value, - IOB_AXISTREAM_IN_ENABLE_ADDR, IOB_AXISTREAM_IN_ENABLE_W); - pr_info("[Driver] ENABLE iob_axistream_in: 0x%x\n", value); - break; - case IOB_AXISTREAM_IN_MODE_ADDR: - size = (IOB_AXISTREAM_IN_MODE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_in_data.regbase, value, - IOB_AXISTREAM_IN_MODE_ADDR, IOB_AXISTREAM_IN_MODE_W); - pr_info("[Driver] MODE iob_axistream_in: 0x%x\n", value); - break; - case IOB_AXISTREAM_IN_FIFO_THRESHOLD_ADDR: - size = (IOB_AXISTREAM_IN_FIFO_THRESHOLD_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_in_data.regbase, value, - IOB_AXISTREAM_IN_FIFO_THRESHOLD_ADDR, - IOB_AXISTREAM_IN_FIFO_THRESHOLD_W); - pr_info("[Driver] FIFO_THRESHOLD iob_axistream_in: 0x%x\n", value); - break; - default: - pr_info("[Driver] Invalid write address 0x%x\n", (unsigned int)*ppos); - // invalid address - no bytes written - return 0; - } - - return count; -} - -/* Custom lseek function - * check: lseek(2) man page for whence modes - */ -static loff_t iob_axistream_in_llseek(struct file *filp, loff_t offset, - int whence) { - loff_t new_pos = -1; - - switch (whence) { - case SEEK_SET: - new_pos = offset; - break; - case SEEK_CUR: - new_pos = filp->f_pos + offset; - break; - case SEEK_END: - new_pos = (1 << IOB_AXISTREAM_IN_CSRS_ADDR_W) + offset; - break; - default: - return -EINVAL; - } - - // Check for valid bounds - if (new_pos < 0 || new_pos > iob_axistream_in_data.regsize) { - return -EINVAL; - } - - // Update file position - filp->f_pos = new_pos; - - return new_pos; -} - -module_init(iob_axistream_in_init); -module_exit(iob_axistream_in_exit); - -MODULE_LICENSE("Dual MIT/GPL"); -MODULE_AUTHOR("IObundle"); -MODULE_DESCRIPTION("IOb-AXISTREAM-IN Drivers"); -MODULE_VERSION("0.10"); diff --git a/lib/hardware/buses/iob_axistream_in/software/linux/iob_axistream_in.dts b/lib/hardware/buses/iob_axistream_in/software/linux/iob_axistream_in.dts deleted file mode 100644 index 2ddffbfec..000000000 --- a/lib/hardware/buses/iob_axistream_in/software/linux/iob_axistream_in.dts +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* Copyright (c) 2024 IObundle */ - -/dts-v1/; - -/ { - #address-cells = <1>; - #size-cells = <1>; - model = "IOb-SoC, VexRiscv"; - compatible = "IOb-SoC, VexRiscv"; - // CPU - // Memory - // Choosen - soc { - #address-cells = <1>; - #size-cells = <1>; - compatible = "iobundle,iob-soc", "simple-bus"; - ranges; - - // Other SOC peripherals go here - - // Add this Node to the device tree - AXISTREAMIN0: axistream_in@/*AXISTREAMIN0_ADDR_MACRO*/ { - compatible = "iobundle,axistream_in0"; - reg = <0x/*AXISTREAMIN0_ADDR_MACRO*/ 0x20>; - }; - - }; -}; diff --git a/lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.c b/lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.c deleted file mode 100644 index f7aa76996..000000000 --- a/lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob-axistream-in.h" - -void iob_axis_in_reset() { - IOB_AXISTREAM_IN_SET_SOFT_RESET(1); - IOB_AXISTREAM_IN_SET_SOFT_RESET(0); -} - -uint32_t iob_axis_read(uint32_t *value) { - if (IOB_AXISTREAM_IN_GET_FIFO_EMPTY()) { - return 0; - } else { - *value = IOB_AXISTREAM_IN_GET_DATA(); - return 1; - } -} diff --git a/lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.h b/lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.h deleted file mode 100644 index 0eb0edab4..000000000 --- a/lib/hardware/buses/iob_axistream_in/software/src/iob-axistream-in.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob_axistream_in_csrs.h" - -void iob_axis_in_reset(); - -uint32_t iob_axis_read(uint32_t *value); diff --git a/lib/hardware/buses/iob_axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c b/lib/hardware/buses/iob_axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c deleted file mode 100644 index 13f12cd94..000000000 --- a/lib/hardware/buses/iob_axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* PC Emulation of axistream-in peripheral */ - -#include - -#include "iob_axistream_in_csrs.h" - -// Base Address -static int base; -void IOB_AXISTREAM_IN_INIT_BASEADDR(uint32_t addr) { base = addr; } - -// Core Setters and Getters -uint32_t IOB_AXISTREAM_IN_GET_DATA() { return 0x00; } - -uint8_t IOB_AXISTREAM_IN_GET_EMPTY() { return 0x01; } - -uint8_t IOB_AXISTREAM_IN_GET_TLAST_DETECTED() { return 0x00; } - -uint32_t IOB_AXISTREAM_IN_GET_NWORDS() { return 0x00; } - -void IOB_AXISTREAM_IN_SET_SOFT_RESET(uint8_t value) {} - -void IOB_AXISTREAM_IN_SET_ENABLE(uint8_t value) {} - -void IOB_AXISTREAM_IN_SET_FIFO_THRESHOLD(uint32_t value) {} - -void IOB_AXISTREAM_IN_SET_MODE(uint8_t value) {} - -uint32_t IOB_AXISTREAM_IN_GET_FIFO_LEVEL() { return 0x00; } - -uint16_t IOB_AXISTREAM_IN_GET_VERSION() { return 0xaaaa; } diff --git a/lib/hardware/buses/iob_axistream_out/hardware/src/iob_axistream_out.v b/lib/hardware/buses/iob_axistream_out/hardware/src/iob_axistream_out.v deleted file mode 100644 index b12a17d61..000000000 --- a/lib/hardware/buses/iob_axistream_out/hardware/src/iob_axistream_out.v +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`include "iob_axistream_out_conf.vh" -`include "iob_axistream_out_csrs_def.vh" - -module iob_axistream_out #( - `include "iob_axistream_out_params.vs" -) ( - `include "iob_axistream_out_io.vs" -); - - localparam R = DATA_W / TDATA_W; - localparam RAM_ADDR_W = FIFO_ADDR_W - $clog2(R); - - //rst and enble synced to axis_clk - wire axis_sw_rst; - wire axis_sw_enable; - - //fifo write - wire fifo_write; - wire [ DATA_W-1:0] fifo_wdata; - - //fifo read - wire axis_fifo_empty; - reg axis_fifo_read; - wire axis_pc; - reg axis_pc_nxt; - wire [ TDATA_W-1:0] axis_tdata; - reg axis_tvalid; - - //word counter - wire [ DATA_W-1:0] axis_word_count; - wire [ DATA_W-1:0] axis_nwords; - - //fifo ram - wire ext_mem_w_clk; - wire [ R-1:0] ext_mem_w_en; - wire [RAM_ADDR_W-1:0] ext_mem_w_addr; - wire [ DATA_W-1:0] ext_mem_w_data; - wire ext_mem_r_clk; - wire [ R-1:0] ext_mem_r_en; - wire [RAM_ADDR_W-1:0] ext_mem_r_addr; - wire [ DATA_W-1:0] ext_mem_r_data; - - `include "iob_axistream_out_wires.vs" - - // configuration control and status register file. - `include "iob_axistream_out_blocks.vs" - - //AXI Stream interface - assign axis_tvalid_o = axis_tvalid; - assign axis_tdata_o = axis_tdata; - assign axis_tlast_o = (axis_word_count == axis_nwords) & axis_tvalid_o; - - //CPU interface - assign data_wready_wr = ~fifo_full_rd; - assign interrupt_o = fifo_level_rd <= fifo_threshold_wr; - - //DMA data ready - assign sys_tready_o = ~fifo_full_rd & axis_sw_enable & (mode_wr == 1'b1); - - //FIFO write - assign fifo_write = ((data_wen_wr & (mode_wr == 1'b0)) | - (sys_tvalid_i & (mode_wr == 1'b1))) & - axis_sw_enable; - assign fifo_wdata = sys_tvalid_i == 1'b1 ? sys_tdata_i : data_wdata_wr; - - //FIFO read - always @* begin - axis_pc_nxt = axis_pc + 1'b1; - axis_fifo_read = 1'b0; - axis_tvalid = 1'b0; - - case (axis_pc) - 0: begin - if (axis_fifo_empty) begin - axis_pc_nxt = axis_pc; - end else begin - axis_fifo_read = 1'b1; - end - end - default: begin - if (axis_word_count <= axis_nwords) begin // Not in padding - axis_tvalid = axis_sw_enable; - axis_pc_nxt = axis_pc; - if (axis_tready_i && axis_sw_enable) begin - if (axis_fifo_empty) begin - axis_pc_nxt = 1'b0; - end else begin - axis_fifo_read = 1'b1; - end - end - end else begin // In padding bytes (read them whithout tvalid and not waiting for tready) - if (axis_fifo_empty) begin - axis_pc_nxt = 1'b0; - end else begin - axis_pc_nxt = axis_pc; - axis_fifo_read = 1'b1; - end - end - end - endcase - end - - // program counter - iob_reg_re #( - .DATA_W (1), - .RST_VAL(1'd0) - ) tvalid_reg ( - .clk_i (axis_clk_i), - .cke_i (axis_cke_i), - .arst_i(axis_arst_i), - .rst_i (axis_sw_rst), - .en_i (axis_sw_enable), - .data_i(axis_pc_nxt), - .data_o(axis_pc) - ); - - // sent words counter - iob_counter #( - .DATA_W (DATA_W), - .RST_VAL({DATA_W{1'd0}}) - ) word_count_inst ( - .clk_i (axis_clk_i), - .cke_i (axis_cke_i), - .arst_i(axis_arst_i), - .rst_i (axis_sw_rst), - .en_i (axis_fifo_read), - .data_o(axis_word_count) - ); - - - //Synchronizers from sw_regs to axis domain - iob_sync #( - .DATA_W (1), - .RST_VAL(1'd0) - ) sw_rst ( - .clk_i (axis_clk_i), - .arst_i (axis_arst_i), - .signal_i(soft_reset_wr), - .signal_o(axis_sw_rst) - ); - - iob_sync #( - .DATA_W (1), - .RST_VAL(1'd0) - ) sw_enable ( - .clk_i (axis_clk_i), - .arst_i (axis_arst_i), - .signal_i(enable_wr), - .signal_o(axis_sw_enable) - ); - - iob_sync #( - .DATA_W (DATA_W), - .RST_VAL(0) - ) fifo_threshold ( - .clk_i (axis_clk_i), - .arst_i (axis_arst_i), - .signal_i(nwords_wr), - .signal_o(axis_nwords) - ); - - //FIFOs RAMs - genvar p; - generate - for (p = 0; p < R; p = p + 1) begin : gen_fifo_ram - // fifo memories - iob_ram_at2p #( - .DATA_W(TDATA_W), - .ADDR_W(RAM_ADDR_W) - ) iob_ram_at2p ( - .w_clk_i (ext_mem_w_clk), - .w_en_i (ext_mem_w_en[p]), - .w_addr_i(ext_mem_w_addr), - .w_data_i(ext_mem_w_data[p*TDATA_W+:TDATA_W]), - - .r_clk_i (ext_mem_r_clk), - .r_en_i (ext_mem_r_en[p]), - .r_addr_i(ext_mem_r_addr), - .r_data_o(ext_mem_r_data[p*TDATA_W+:TDATA_W]) - ); - end - endgenerate - - //async fifo - iob_fifo_async #( - .W_DATA_W(DATA_W), - .R_DATA_W(TDATA_W), - .ADDR_W (FIFO_ADDR_W) - ) data_fifo ( - //memory write port - .ext_mem_w_clk_o (ext_mem_w_clk), - .ext_mem_w_en_o (ext_mem_w_en), - .ext_mem_w_addr_o(ext_mem_w_addr), - .ext_mem_w_data_o(ext_mem_w_data), - //memory read port - .ext_mem_r_clk_o (ext_mem_r_clk), - .ext_mem_r_en_o (ext_mem_r_en), - .ext_mem_r_addr_o(ext_mem_r_addr), - .ext_mem_r_data_i(ext_mem_r_data), - //read port (axis clk domain) - .r_clk_i (axis_clk_i), - .r_cke_i (axis_cke_i), - .r_arst_i (axis_arst_i), - .r_rst_i (axis_sw_rst), - .r_en_i (axis_fifo_read), - .r_data_o (axis_tdata), - .r_empty_o (axis_fifo_empty), - .r_full_o (), - .r_level_o (), - //write port (sys clk domain) - .w_clk_i (clk_i), - .w_cke_i (cke_i), - .w_arst_i (arst_i), - .w_rst_i (soft_reset_wr), - .w_en_i (fifo_write), - .w_data_i (fifo_wdata), - .w_empty_o (fifo_empty_rd), - .w_full_o (fifo_full_rd), - .w_level_o (fifo_level_rd) - ); - -endmodule - - diff --git a/lib/hardware/buses/iob_axistream_out/iob_axistream_out.py b/lib/hardware/buses/iob_axistream_out/iob_axistream_out.py deleted file mode 100755 index 722235ece..000000000 --- a/lib/hardware/buses/iob_axistream_out/iob_axistream_out.py +++ /dev/null @@ -1,371 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.3", - "board_list": ["cyclonev_gt_dk", "aes_ku040_db_g"], - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "32", - "max": "32", - "descr": "CPU data bus width", - }, - { - "name": "ADDR_W", - "type": "P", - # "val": "`IOB_AXISTREAM_OUT_CSRS_ADDR_W", - "val": "5", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "TDATA_W", - "type": "P", - "val": "8", - "min": "1", - "max": "DATA_W", - "descr": "AXI stream data width", - }, - { - "name": "FIFO_ADDR_W", - "type": "P", - "val": "4", - "min": "NA", - "max": "16", - "descr": "FIFO depth (log2)", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "iob_s", - "interface": { - "type": "iob", - "subtype": "slave", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - "descr": "CPU native interface", - }, - { - "name": "interrupt_o", - "descr": "Interrupt signal", - "signals": [ - { - "name": "interrupt", - "direction": "output", - "width": "1", - "descr": "FIFO threshold interrupt signal", - }, - ], - }, - { - "name": "axistream", - "descr": "AXI Stream interface signals", - "signals": [ - { - "name": "axis_clk", - "direction": "input", - "width": "1", - "descr": "Clock.", - }, - { - "name": "axis_cke", - "direction": "input", - "width": "1", - "descr": "Clock enable.", - }, - { - "name": "axis_arst", - "direction": "input", - "width": "1", - "descr": "Aynchronous and active high reset.", - }, - { - "name": "axis_tdata", - "direction": "output", - "width": "TDATA_W", - "descr": "Data.", - }, - { - "name": "axis_tvalid", - "direction": "output", - "width": "1", - "descr": "Valid.", - }, - { - "name": "axis_tready", - "direction": "input", - "width": "1", - "descr": "Ready.", - }, - { - "name": "axis_tlast", - "direction": "output", - "width": "1", - "descr": "Last word.", - }, - ], - }, - { - "name": "sys_axis", - "descr": "System AXI Stream interface.", - "signals": [ - { - "name": "sys_tdata", - "direction": "input", - "width": "DATA_W", - "descr": "Data.", - }, - { - "name": "sys_tvalid", - "direction": "input", - "width": "1", - "descr": "Valid.", - }, - { - "name": "sys_tready", - "direction": "output", - "width": "1", - "descr": "Ready.", - }, - ], - }, - ], - "wires": [ - { - "name": "csrs_iob", - "descr": "Internal CSRs IOb interface", - "interface": { - "type": "iob", - "wire_prefix": "csrs_", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - { - "name": "soft_reset", - "descr": "", - "signals": [ - {"name": "soft_reset_wr", "width": 1}, - ], - }, - { - "name": "enable", - "descr": "", - "signals": [ - {"name": "enable_wr", "width": 1}, - ], - }, - { - "name": "data", - "descr": "", - "signals": [ - {"name": "data_wdata_wr", "width": 32}, - {"name": "data_wen_wr", "width": 1}, - {"name": "data_wready_wr", "width": 1}, - ], - }, - { - "name": "mode", - "descr": "", - "signals": [ - {"name": "mode_wr", "width": 1}, - ], - }, - { - "name": "nwords", - "descr": "", - "signals": [ - {"name": "nwords_wr", "width": "DATA_W"}, - ], - }, - { - "name": "fifo_full", - "descr": "", - "signals": [ - {"name": "fifo_full_rd", "width": 1}, - ], - }, - { - "name": "fifo_empty", - "descr": "", - "signals": [ - {"name": "fifo_empty_rd", "width": 1}, - ], - }, - { - "name": "fifo_threshold", - "descr": "", - "signals": [ - {"name": "fifo_threshold_wr", "width": "FIFO_ADDR_W+1"}, - ], - }, - { - "name": "fifo_level", - "descr": "", - "signals": [ - {"name": "fifo_level_rd", "width": "FIFO_ADDR_W+1"}, - ], - }, - ], - "blocks": [ - { - "core_name": "csrs", - "instance_name": "csrs_inst", - "instance_description": "Control/Status Registers", - "csrs": [ - { - "name": "axistream", - "descr": "AXI Stream software accessible registers.", - "regs": [ - { - "name": "soft_reset", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Soft reset.", - }, - { - "name": "enable", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Enable peripheral.", - }, - { - "name": "data", - "type": "W", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": False, - "descr": "Data input.", - }, - { - "name": "mode", - "type": "W", - "n_bits": "1", - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Sets the operation mode: (0) data is read using CSR; (1) data is read using system axistream interface.", - }, - { - "name": "nwords", - "type": "W", - "n_bits": "DATA_W", - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Set the number of words (with TDATA_W bits) to be written to the FIFO.", - }, - ], - }, - { - "name": "fifo", - "descr": "FIFO related registers", - "regs": [ - { - "name": "fifo_full", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Full (1), or non-full (0).", - }, - { - "name": "fifo_empty", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Full (1), or non-full (0).", - }, - { - "name": "fifo_threshold", - "type": "W", - "n_bits": "FIFO_ADDR_W+1", - "rst_val": 8, - "log2n_items": 0, - "autoreg": True, - "descr": "FIFO threshold level for interrupt signal", - }, - { - "name": "fifo_level", - "type": "R", - "n_bits": "FIFO_ADDR_W+1", - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Current FIFO level", - }, - ], - }, - ], - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "iob_s", - "csrs_iob_o": "csrs_iob", - # Register interfaces - "soft_reset": "soft_reset", - "enable": "enable", - "data": "data", - "mode": "mode", - "nwords": "nwords", - "fifo_full": "fifo_full", - "fifo_empty": "fifo_empty", - "fifo_threshold": "fifo_threshold", - "fifo_level": "fifo_level", - }, - }, - # TODO: Connect remaining blocks - { - "core_name": "iob_fifo_async", - "instance_name": "iob_fifo_async_inst", - "instantiate": False, - }, - { - "core_name": "iob_sync", - "instance_name": "iob_sync_inst", - "instantiate": False, - }, - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - "instantiate": False, - }, - { - "core_name": "iob_ram_at2p", - "instance_name": "iob_ram_at2p_inst", - "instantiate": False, - }, - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - "instantiate": False, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_axistream_out/software/linux/Readme.md b/lib/hardware/buses/iob_axistream_out/software/linux/Readme.md deleted file mode 100644 index 50392a061..000000000 --- a/lib/hardware/buses/iob_axistream_out/software/linux/Readme.md +++ /dev/null @@ -1,21 +0,0 @@ - - -# IOb AXIStream Out Linux Kernel Drivers -- Structure: - - `drivers/`: directory with linux kernel module drivers for - iob_axistream_out - - `iob_axistream_out_main.c`: driver source - - `[iob_axistream_out.h]` and `[iob_axistream_out_sysfs.h]`: header files - generated by: - ```bash - python3 .path/to/iob-linux/scripts/drivers.py iob_axistream_out -o [output_dir] - ``` - - `driver.mk`: makefile segment with `iob_axistream_out-obj:` target for driver - compilation - - `iob_axistream_out.dts`: device tree template with iob_axistream_out node - - manually add the `axistream_out` node to the system device tree so the - iob_axistream_out is recognized by the linux kernel diff --git a/lib/hardware/buses/iob_axistream_out/software/linux/drivers/driver.mk b/lib/hardware/buses/iob_axistream_out/software/linux/drivers/driver.mk deleted file mode 100644 index 3126ac2f8..000000000 --- a/lib/hardware/buses/iob_axistream_out/software/linux/drivers/driver.mk +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -iob_axistream_out-objs := iob_axistream_out_main.o iob_class/iob_class_utils.o diff --git a/lib/hardware/buses/iob_axistream_out/software/linux/drivers/iob_axistream_out_main.c b/lib/hardware/buses/iob_axistream_out/software/linux/drivers/iob_axistream_out_main.c deleted file mode 100644 index f411827fc..000000000 --- a/lib/hardware/buses/iob_axistream_out/software/linux/drivers/iob_axistream_out_main.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* iob_axistream_out_main.c: driver for iob_axistream_out - * using device platform. No hardcoded hardware address: - * 1. load driver: insmod iob_axistream_out.ko - * 2. run user app: ./user/user - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iob_axistream_out.h" -#include "iob_class/iob_class_utils.h" - -static int iob_axistream_out_probe(struct platform_device *); -static int iob_axistream_out_remove(struct platform_device *); - -static ssize_t iob_axistream_out_read(struct file *, char __user *, size_t, - loff_t *); -static ssize_t iob_axistream_out_write(struct file *, const char __user *, - size_t, loff_t *); -static loff_t iob_axistream_out_llseek(struct file *, loff_t, int); -static int iob_axistream_out_open(struct inode *, struct file *); -static int iob_axistream_out_release(struct inode *, struct file *); - -static struct iob_data iob_axistream_out_data = {0}; -DEFINE_MUTEX(iob_axistream_out_mutex); - -#include "iob_axistream_out_sysfs.h" - -static const struct file_operations iob_axistream_out_fops = { - .owner = THIS_MODULE, - .write = iob_axistream_out_write, - .read = iob_axistream_out_read, - .llseek = iob_axistream_out_llseek, - .open = iob_axistream_out_open, - .release = iob_axistream_out_release, -}; - -static const struct of_device_id of_iob_axistream_out_match[] = { - {.compatible = "iobundle,axistream_out0"}, - {}, -}; - -static struct platform_driver iob_axistream_out_driver = { - .driver = - { - .name = "iob_axistream_out", - .owner = THIS_MODULE, - .of_match_table = of_iob_axistream_out_match, - }, - .probe = iob_axistream_out_probe, - .remove = iob_axistream_out_remove, -}; - -// -// Module init and exit functions -// -static int iob_axistream_out_probe(struct platform_device *pdev) { - struct resource *res; - int result = 0; - - if (iob_axistream_out_data.device != NULL) { - pr_err("[Driver] %s: No more devices allowed!\n", - IOB_AXISTREAM_OUT_DRIVER_NAME); - - return -ENODEV; - } - - pr_info("[Driver] %s: probing.\n", IOB_AXISTREAM_OUT_DRIVER_NAME); - - // Get the I/O region base address - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - pr_err("[Driver]: Failed to get I/O resource!\n"); - result = -ENODEV; - goto r_get_resource; - } - - // Request and map the I/O region - iob_axistream_out_data.regbase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(iob_axistream_out_data.regbase)) { - result = PTR_ERR(iob_axistream_out_data.regbase); - goto r_ioremmap; - } - iob_axistream_out_data.regsize = resource_size(res); - - // Alocate char device - result = alloc_chrdev_region(&iob_axistream_out_data.devnum, 0, 1, - IOB_AXISTREAM_OUT_DRIVER_NAME); - if (result) { - pr_err("%s: Failed to allocate device number!\n", - IOB_AXISTREAM_OUT_DRIVER_NAME); - goto r_alloc_region; - } - - cdev_init(&iob_axistream_out_data.cdev, &iob_axistream_out_fops); - - result = - cdev_add(&iob_axistream_out_data.cdev, iob_axistream_out_data.devnum, 1); - if (result) { - pr_err("%s: Char device registration failed!\n", - IOB_AXISTREAM_OUT_DRIVER_NAME); - goto r_cdev_add; - } - - // Create device class // todo: make a dummy driver just to create and own the - // class: https://stackoverflow.com/a/16365027/8228163 - if ((iob_axistream_out_data.class = - class_create(THIS_MODULE, IOB_AXISTREAM_OUT_DRIVER_CLASS)) == NULL) { - printk("Device class can not be created!\n"); - goto r_class; - } - - // Create device file - iob_axistream_out_data.device = device_create( - iob_axistream_out_data.class, NULL, iob_axistream_out_data.devnum, NULL, - IOB_AXISTREAM_OUT_DRIVER_NAME); - if (iob_axistream_out_data.device == NULL) { - printk("Can not create device file!\n"); - goto r_device; - } - - result = - iob_axistream_out_create_device_attr_files(iob_axistream_out_data.device); - if (result) { - pr_err("Cannot create device attribute file......\n"); - goto r_dev_file; - } - - dev_info(&pdev->dev, "initialized.\n"); - goto r_ok; - -r_dev_file: - iob_axistream_out_remove_device_attr_files(&iob_axistream_out_data); -r_device: - class_destroy(iob_axistream_out_data.class); -r_class: - cdev_del(&iob_axistream_out_data.cdev); -r_cdev_add: - unregister_chrdev_region(iob_axistream_out_data.devnum, 1); -r_alloc_region: - // iounmap is managed by devm -r_ioremmap: -r_get_resource: -r_ok: - - return result; -} - -static int iob_axistream_out_remove(struct platform_device *pdev) { - iob_axistream_out_remove_device_attr_files(&iob_axistream_out_data); - class_destroy(iob_axistream_out_data.class); - cdev_del(&iob_axistream_out_data.cdev); - unregister_chrdev_region(iob_axistream_out_data.devnum, 1); - // Note: no need for iounmap, since we are using devm_ioremap_resource() - - dev_info(&pdev->dev, "exiting.\n"); - - return 0; -} - -static int __init iob_axistream_out_init(void) { - pr_info("[Driver] %s: initializing.\n", IOB_AXISTREAM_OUT_DRIVER_NAME); - - return platform_driver_register(&iob_axistream_out_driver); -} - -static void __exit iob_axistream_out_exit(void) { - pr_info("[Driver] %s: exiting.\n", IOB_AXISTREAM_OUT_DRIVER_NAME); - platform_driver_unregister(&iob_axistream_out_driver); -} - -// -// File operations -// - -static int iob_axistream_out_open(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_axistream_out device opened\n"); - - if (!mutex_trylock(&iob_axistream_out_mutex)) { - pr_info("Another process is accessing the device\n"); - - return -EBUSY; - } - - return 0; -} - -static int iob_axistream_out_release(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_axistream_out device closed\n"); - - mutex_unlock(&iob_axistream_out_mutex); - - return 0; -} - -static ssize_t iob_axistream_out_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) { - int size = 0; - u32 value = 0; - - /* read value from register */ - switch (*ppos) { - case IOB_AXISTREAM_OUT_FIFO_FULL_ADDR: - value = iob_data_read_reg(iob_axistream_out_data.regbase, - IOB_AXISTREAM_OUT_FIFO_FULL_ADDR, - IOB_AXISTREAM_OUT_FIFO_FULL_W); - size = (IOB_AXISTREAM_OUT_FIFO_FULL_W >> 3); // bit to bytes - pr_info("[Driver] Read FIFO_FULL!\n"); - break; - case IOB_AXISTREAM_OUT_FIFO_EMPTY_ADDR: - value = iob_data_read_reg(iob_axistream_out_data.regbase, - IOB_AXISTREAM_OUT_FIFO_EMPTY_ADDR, - IOB_AXISTREAM_OUT_FIFO_EMPTY_W); - size = (IOB_AXISTREAM_OUT_FIFO_EMPTY_W >> 3); // bit to bytes - pr_info("[Driver] Read FIFO_EMPTY!\n"); - break; - case IOB_AXISTREAM_OUT_FIFO_LEVEL_ADDR: - value = iob_data_read_reg(iob_axistream_out_data.regbase, - IOB_AXISTREAM_OUT_FIFO_LEVEL_ADDR, - IOB_AXISTREAM_OUT_FIFO_LEVEL_W); - size = (IOB_AXISTREAM_OUT_FIFO_LEVEL_W >> 3); // bit to bytes - pr_info("[Driver] Read FIFO_LEVEL!\n"); - break; - case IOB_AXISTREAM_OUT_VERSION_ADDR: - value = iob_data_read_reg(iob_axistream_out_data.regbase, - IOB_AXISTREAM_OUT_VERSION_ADDR, - IOB_AXISTREAM_OUT_VERSION_W); - size = (IOB_AXISTREAM_OUT_VERSION_W >> 3); // bit to bytes - pr_info("[Driver] Read version!\n"); - break; - default: - // invalid address - no bytes read - return 0; - } - - // Read min between count and REG_SIZE - if (size > count) - size = count; - - if (copy_to_user(buf, &value, size)) - return -EFAULT; - - return count; -} - -static ssize_t iob_axistream_out_write(struct file *file, - const char __user *buf, size_t count, - loff_t *ppos) { - int size = 0; - u32 value = 0; - - switch (*ppos) { - case IOB_AXISTREAM_OUT_SOFT_RESET_ADDR: - size = (IOB_AXISTREAM_OUT_SOFT_RESET_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_out_data.regbase, value, - IOB_AXISTREAM_OUT_SOFT_RESET_ADDR, - IOB_AXISTREAM_OUT_SOFT_RESET_W); - pr_info("[Driver] SOFT_RESET iob_axistream_out: 0x%x\n", value); - break; - case IOB_AXISTREAM_OUT_ENABLE_ADDR: - size = (IOB_AXISTREAM_OUT_ENABLE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_out_data.regbase, value, - IOB_AXISTREAM_OUT_ENABLE_ADDR, - IOB_AXISTREAM_OUT_ENABLE_W); - pr_info("[Driver] ENABLE iob_axistream_out: 0x%x\n", value); - break; - case IOB_AXISTREAM_OUT_DATA_ADDR: - size = (IOB_AXISTREAM_OUT_DATA_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_out_data.regbase, value, - IOB_AXISTREAM_OUT_DATA_ADDR, IOB_AXISTREAM_OUT_DATA_W); - pr_info("[Driver] DATA iob_axistream_out: 0x%x\n", value); - break; - case IOB_AXISTREAM_OUT_MODE_ADDR: - size = (IOB_AXISTREAM_OUT_MODE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_out_data.regbase, value, - IOB_AXISTREAM_OUT_MODE_ADDR, IOB_AXISTREAM_OUT_MODE_W); - pr_info("[Driver] MODE iob_axistream_out: 0x%x\n", value); - break; - case IOB_AXISTREAM_OUT_NWORDS_ADDR: - size = (IOB_AXISTREAM_OUT_NWORDS_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_out_data.regbase, value, - IOB_AXISTREAM_OUT_NWORDS_ADDR, - IOB_AXISTREAM_OUT_NWORDS_W); - pr_info("[Driver] NWORDS iob_axistream_out: 0x%x\n", value); - break; - case IOB_AXISTREAM_OUT_FIFO_THRESHOLD_ADDR: - size = (IOB_AXISTREAM_OUT_FIFO_THRESHOLD_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_axistream_out_data.regbase, value, - IOB_AXISTREAM_OUT_FIFO_THRESHOLD_ADDR, - IOB_AXISTREAM_OUT_FIFO_THRESHOLD_W); - pr_info("[Driver] FIFO_THRESHOLD iob_axistream_out: 0x%x\n", value); - break; - default: - pr_info("[Driver] Invalid write address 0x%x\n", (unsigned int)*ppos); - // invalid address - no bytes written - return 0; - } - - return count; -} - -/* Custom lseek function - * check: lseek(2) man page for whence modes - */ -static loff_t iob_axistream_out_llseek(struct file *filp, loff_t offset, - int whence) { - loff_t new_pos = -1; - - switch (whence) { - case SEEK_SET: - new_pos = offset; - break; - case SEEK_CUR: - new_pos = filp->f_pos + offset; - break; - case SEEK_END: - new_pos = (1 << IOB_AXISTREAM_OUT_CSRS_ADDR_W) + offset; - break; - default: - return -EINVAL; - } - - // Check for valid bounds - if (new_pos < 0 || new_pos > iob_axistream_out_data.regsize) { - return -EINVAL; - } - - // Update file position - filp->f_pos = new_pos; - - return new_pos; -} - -module_init(iob_axistream_out_init); -module_exit(iob_axistream_out_exit); - -MODULE_LICENSE("Dual MIT/GPL"); -MODULE_AUTHOR("IObundle"); -MODULE_DESCRIPTION("IOb-AXISTREAM-OUT Drivers"); -MODULE_VERSION("0.10"); diff --git a/lib/hardware/buses/iob_axistream_out/software/linux/iob_axistream_out.dts b/lib/hardware/buses/iob_axistream_out/software/linux/iob_axistream_out.dts deleted file mode 100644 index 909349b75..000000000 --- a/lib/hardware/buses/iob_axistream_out/software/linux/iob_axistream_out.dts +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* Copyright (c) 2024 IObundle */ - -/dts-v1/; - -/ { - #address-cells = <1>; - #size-cells = <1>; - model = "IOb-SoC, VexRiscv"; - compatible = "IOb-SoC, VexRiscv"; - // CPU - // Memory - // Choosen - soc { - #address-cells = <1>; - #size-cells = <1>; - compatible = "iobundle,iob-soc", "simple-bus"; - ranges; - - // Other SOC peripherals go here - - // Add this Node to the device tree - AXISTREAMOUT0: axistream_out@/*AXISTREAMOUT0_ADDR_MACRO*/ { - compatible = "iobundle,axistream_out0"; - reg = <0x/*AXISTREAMOUT0_ADDR_MACRO*/ 0x20>; - }; - - }; -}; diff --git a/lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.c b/lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.c deleted file mode 100644 index fd65765fb..000000000 --- a/lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob-axistream-out.h" - -void iob_axis_out_reset() { - IOB_AXISTREAM_OUT_SET_SOFT_RESET(1); - IOB_AXISTREAM_OUT_SET_SOFT_RESET(0); -} - -uint32_t iob_axis_write(uint32_t value) { - if (IOB_AXISTREAM_OUT_GET_FIFO_FULL()) { - return 0; - } else { - IOB_AXISTREAM_OUT_SET_DATA(value); - return 1; - } -} diff --git a/lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.h b/lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.h deleted file mode 100644 index fefa9f876..000000000 --- a/lib/hardware/buses/iob_axistream_out/software/src/iob-axistream-out.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob_axistream_out_csrs.h" - -void iob_axis_out_reset(); - -uint32_t iob_axis_write(uint32_t value); diff --git a/lib/hardware/buses/iob_axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c b/lib/hardware/buses/iob_axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c deleted file mode 100644 index 7fa63a8fa..000000000 --- a/lib/hardware/buses/iob_axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* PC Emulation of axistream-out peripheral */ - -#include - -#include "iob_axistream_out_csrs.h" - -// Base Address -static int base; -void IOB_AXISTREAM_OUT_INIT_BASEADDR(uint32_t addr) { base = addr; } - -// Core Setters and Getters -void IOB_AXISTREAM_OUT_SET_DATA(uint32_t value) {} - -uint8_t IOB_AXISTREAM_OUT_GET_FULL() { return 0x00; } - -void IOB_AXISTREAM_OUT_SET_SOFT_RESET(uint8_t value) {} - -void IOB_AXISTREAM_OUT_SET_ENABLE(uint8_t value) {} - -void IOB_AXISTREAM_OUT_SET_WSTRB(uint8_t value) {} - -void IOB_AXISTREAM_OUT_SET_LAST(uint8_t value) {} - -void IOB_AXISTREAM_OUT_SET_FIFO_THRESHOLD(uint32_t value) {} - -uint32_t IOB_AXISTREAM_OUT_GET_FIFO_LEVEL() { return 0x00; } - -uint16_t IOB_AXISTREAM_OUT_GET_VERSION() { return 0xaaaa; } diff --git a/lib/hardware/buses/iob_bus_demux/hardware/src/iob_bus_demux.v b/lib/hardware/buses/iob_bus_demux/hardware/src/iob_bus_demux.v deleted file mode 100644 index 8f40bd9ac..000000000 --- a/lib/hardware/buses/iob_bus_demux/hardware/src/iob_bus_demux.v +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -// Split the IOb native interface, from a single master to multiple followers -module iob_bus_demux #( - parameter ADDR_W = 32, - parameter DATA_W = 32, - parameter N = 2, // Number of followers, minimum of 2 - parameter NB = $clog2(N) // Number of bits needed to address all followers -) ( - `include "iob_bus_demux_clk_rst_s_port.vs" - - // Master's interface - input m_valid_i, - input [ADDR_W-1:0] m_addr_i, - input [DATA_W-1:0] m_wdata_i, - input [DATA_W/8-1:0] m_wstrb_i, - output [DATA_W-1:0] m_rdata_o, - output m_rvalid_o, - output m_ready_o, - - // Followers' interface - output [N*1-1:0] f_valid_o, - output [N*ADDR_W-1:0] f_addr_o, - output [N*DATA_W-1:0] f_wdata_o, - output [N*(DATA_W/8)-1:0] f_wstrb_o, - input [N*DATA_W-1:0] f_rdata_i, - input [N*1-1:0] f_rvalid_i, - input [N*1-1:0] f_ready_i, - - // Follower selection - input [NB-1:0] f_sel_i -); - - // - // Register the follower selection - // - - wire [NB-1:0] f_sel_r; - iob_reg_e #( - .DATA_W (NB), - .RST_VAL(0) - ) reg_f_sel ( - `include "iob_bus_demux_clk_rst_s_s_portmap.vs" - .cke_i (1'b1), - .en_i (m_valid_i), - .data_i(f_sel_i), - .data_o(f_sel_r) - ); - - // - // Route master request to selected follower - // - - iob_demux #( - .DATA_W (1), - .N (N) - ) demux_valid ( - .sel_i (f_sel_i), - .data_i(m_valid_i), - .data_o(f_valid_o) - ); - - iob_demux #( - .DATA_W (ADDR_W), - .N (N) - ) demux_addr ( - .sel_i (f_sel_i), - .data_i(m_addr_i), - .data_o(f_addr_o) - ); - - iob_demux #( - .DATA_W (DATA_W), - .N (N) - ) demux_wdata ( - .sel_i (f_sel_i), - .data_i(m_wdata_i), - .data_o(f_wdata_o) - ); - - iob_demux #( - .DATA_W (DATA_W/8), - .N (N) - ) demux_wstrb ( - .sel_i (f_sel_i), - .data_i(m_wstrb_i), - .data_o(f_wstrb_o) - ); - - // - // Route selected follower response to master - // - - iob_mux #( - .DATA_W (DATA_W), - .N (N) - ) mux_rdata ( - .sel_i (f_sel_r), - .data_i(f_rdata_i), - .data_o(m_rdata_o) - ); - - iob_mux #( - .DATA_W (1), - .N (N) - ) mux_rvalid ( - .sel_i (f_sel_r), - .data_i(f_rvalid_i), - .data_o(m_rvalid_o) - ); - - iob_mux #( - .DATA_W (1), - .N (N) - ) mux_ready ( - .sel_i (f_sel_i), - .data_i(f_ready_i), - .data_o(m_ready_o) - ); - -endmodule diff --git a/lib/hardware/buses/iob_bus_demux/iob_bus_demux.py b/lib/hardware/buses/iob_bus_demux/iob_bus_demux.py deleted file mode 100644 index df1cf5887..000000000 --- a/lib/hardware/buses/iob_bus_demux/iob_bus_demux.py +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_rst_s", - "interface": { - "type": "clk_rst", - "subtype": "slave", - }, - "descr": "Clock and reset", - }, - ], - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - { - "core_name": "iob_demux", - "instance_name": "iob_demux_inst", - }, - { - "core_name": "iob_mux", - "instance_name": "iob_mux_inst", - }, - ], - } - return attributes_dict diff --git a/lib/hardware/buses/iob_demux/iob_demux.py b/lib/hardware/buses/iob_demux/iob_demux.py deleted file mode 100644 index 6e7b7ebd7..000000000 --- a/lib/hardware/buses/iob_demux/iob_demux.py +++ /dev/null @@ -1,78 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Width of data interface", - }, - { - "name": "N", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Number of outputs", - }, - ], - "ports": [ - { - "name": "sel_i", - "descr": "Selector interface", - "signals": [ - { - "name": "sel", - "width": "($clog2(N)+($clog2(N)==0))", - "direction": "input", - }, - ], - }, - { - "name": "data_i", - "descr": "Input port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - { - "name": "data", - "width": "N*DATA_W", - "direction": "output", - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // //Select the data to output - genvar i; - generate - for (i = 0; i < N; i = i + 1) begin : gen_demux - assign data_o[i*DATA_W+:DATA_W] = (sel_i==i)? data_i : {DATA_W{1'b0}}; - end - endgenerate - - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_iob2wishbone/hardware/src/iob_iob2wishbone.v b/lib/hardware/buses/iob_iob2wishbone/hardware/src/iob_iob2wishbone.v deleted file mode 100644 index cbcc42978..000000000 --- a/lib/hardware/buses/iob_iob2wishbone/hardware/src/iob_iob2wishbone.v +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_iob2wishbone #( - parameter ADDR_W = 32, - parameter DATA_W = 32, - parameter READ_BYTES = 4 -) ( - `include "iob_iob2wishbone_clk_en_rst_s_port.vs" - - // IOb interface - input wire iob_valid_i, - input wire [ ADDR_W-1:0] iob_addr_i, - input wire [ DATA_W-1:0] iob_wdata_i, - input wire [DATA_W/8-1:0] iob_wstrb_i, - output wire iob_rvalid_o, - output wire [ DATA_W-1:0] iob_rdata_o, - output wire iob_ready_o, - - // Wishbone interface - output wire [ ADDR_W-1:0] wb_addr_o, - output wire [DATA_W/8-1:0] wb_select_o, - output wire wb_we_o, - output wire wb_cyc_o, - output wire wb_stb_o, - output wire [ DATA_W-1:0] wb_data_o, - input wire wb_ack_i, - input wire [ DATA_W-1:0] wb_data_i -); - - localparam RB_MASK = {1'b0, {READ_BYTES{1'b1}}}; - - // IOb auxiliar wires - wire iob_valid_r; - wire [ ADDR_W-1:0] iob_address_r; - wire [ DATA_W-1:0] iob_wdata_r; - // Wishbone auxiliar wire - wire [ DATA_W-1:0] wb_data_r; - wire [DATA_W/8-1:0] wb_select; - wire [DATA_W/8-1:0] wb_select_r; - wire wb_we; - wire wb_we_r; - wire wb_ack_r; - - // Logic - assign wb_addr_o = iob_valid_i ? iob_addr_i : iob_address_r; - assign wb_data_o = iob_valid_i ? iob_wdata_i : iob_wdata_r; - assign wb_select_o = iob_valid_i ? wb_select : wb_select_r; - assign wb_we_o = iob_valid_i ? wb_we : wb_we_r; - assign wb_cyc_o = iob_valid_i ? iob_valid_i : iob_valid_r; - assign wb_stb_o = wb_cyc_o; - - assign wb_select = wb_we ? iob_wstrb_i : (RB_MASK) << (iob_addr_i[1:0]); - assign wb_we = |iob_wstrb_i; - - assign iob_rvalid_o = wb_ack_r & (~wb_we_r); - assign iob_rdata_o = wb_ack_i ? wb_data_i : wb_data_r; - assign iob_ready_o = (~iob_valid_r) | wb_ack_r; - - iob_reg_re #( - .DATA_W (1), - .RST_VAL(0) - ) iob_reg_valid ( - `include "iob_iob2wishbone_clk_en_rst_s_s_portmap.vs" - .rst_i (wb_ack_i), - .en_i (iob_valid_i), - .data_i(iob_valid_i), - .data_o(iob_valid_r) - ); - iob_reg_re #( - .DATA_W (ADDR_W), - .RST_VAL(0) - ) iob_reg_addr ( - `include "iob_iob2wishbone_clk_en_rst_s_s_portmap.vs" - .rst_i (1'b0), - .en_i (iob_valid_i), - .data_i(iob_addr_i), - .data_o(iob_address_r) - ); - iob_reg_re #( - .DATA_W (DATA_W), - .RST_VAL(0) - ) iob_reg_iob_data ( - `include "iob_iob2wishbone_clk_en_rst_s_s_portmap.vs" - .rst_i (1'b0), - .en_i (iob_valid_i), - .data_i(iob_wdata_i), - .data_o(iob_wdata_r) - ); - iob_reg_re #( - .DATA_W (1), - .RST_VAL(0) - ) iob_reg_we ( - `include "iob_iob2wishbone_clk_en_rst_s_s_portmap.vs" - .rst_i (1'b0), - .en_i (iob_valid_i), - .data_i(wb_we), - .data_o(wb_we_r) - ); - iob_reg_re #( - .DATA_W (DATA_W / 8), - .RST_VAL(0) - ) iob_reg_strb ( - `include "iob_iob2wishbone_clk_en_rst_s_s_portmap.vs" - .rst_i (1'b0), - .en_i (iob_valid_i), - .data_i(wb_select), - .data_o(wb_select_r) - ); - iob_reg_re #( - .DATA_W (DATA_W), - .RST_VAL(0) - ) iob_reg_wb_data ( - `include "iob_iob2wishbone_clk_en_rst_s_s_portmap.vs" - .rst_i (1'b0), - .en_i (1'b1), - .data_i(wb_data_i), - .data_o(wb_data_r) - ); - iob_reg_re #( - .DATA_W (1), - .RST_VAL(0) - ) iob_reg_wb_ack ( - `include "iob_iob2wishbone_clk_en_rst_s_s_portmap.vs" - .rst_i (1'b0), - .en_i (1'b1), - .data_i(wb_ack_i), - .data_o(wb_ack_r) - ); - - -endmodule diff --git a/lib/hardware/buses/iob_iob2wishbone/iob_iob2wishbone.py b/lib/hardware/buses/iob_iob2wishbone/iob_iob2wishbone.py deleted file mode 100644 index 4903dc2f2..000000000 --- a/lib/hardware/buses/iob_iob2wishbone/iob_iob2wishbone.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_merge/iob_merge.py b/lib/hardware/buses/iob_merge/iob_merge.py deleted file mode 100644 index 69bf2f188..000000000 --- a/lib/hardware/buses/iob_merge/iob_merge.py +++ /dev/null @@ -1,379 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - assert "name" in py_params_dict, print( - "Error: Missing name for generated merge module." - ) - assert "num_inputs" in py_params_dict, print( - "Error: Missing number of inputs for generated merge module." - ) - - NUM_INPUTS = int(py_params_dict["num_inputs"]) - # Number of bits required for input selection - NBITS = (NUM_INPUTS - 1).bit_length() - - ADDR_W = int(py_params_dict["addr_w"]) if "addr_w" in py_params_dict else 32 - DATA_W = int(py_params_dict["data_w"]) if "data_w" in py_params_dict else 32 - - attributes_dict = { - "name": py_params_dict["name"], - "version": "0.1", - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and async reset", - }, - { - "name": "reset_i", - "descr": "Reset signal", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": "1", - }, - ], - }, - { - "name": "output_m", - "interface": { - "type": "iob", - "subtype": "master", - "file_prefix": py_params_dict["name"] + "_output_", - "port_prefix": "output_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W, - }, - "descr": "Merge output", - }, - ], - } - for port_idx in range(NUM_INPUTS): - attributes_dict["ports"].append( - { - "name": f"input_{port_idx}_s", - "interface": { - "type": "iob", - "subtype": "slave", - "file_prefix": f"{py_params_dict['name']}_input{port_idx}_", - "port_prefix": f"input{port_idx}_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W - NBITS, - }, - "descr": "Merge input interfaces", - }, - ) - attributes_dict["wires"] = [ - # Output selection signals - { - "name": "sel_reg_rst", - "descr": "Enable and reset signal for sel_reg", - "signals": [ - {"name": "rst"}, - ], - }, - { - "name": "sel_reg_data_i", - "descr": "Input of sel_reg", - "signals": [ - {"name": "sel", "width": NBITS}, - ], - }, - { - "name": "sel_reg_data_o", - "descr": "Output of sel_reg", - "signals": [ - {"name": "sel_reg", "width": NBITS}, - ], - }, - { - "name": "input_sel", - "descr": "Select output interface", - "signals": [ - {"name": "sel"}, - ], - }, - { - "name": "input_sel_reg", - "descr": "Registered select output interface", - "signals": [ - {"name": "sel_reg"}, - ], - }, - # Mux signals - { - "name": "mux_valid_data_i", - "descr": "Input of valid mux", - "signals": [ - {"name": "mux_valid_input", "width": NUM_INPUTS}, - ], - }, - { - "name": "mux_valid_data_o", - "descr": "Output of valid mux", - "signals": [ - {"name": "output_iob_valid"}, - ], - }, - { - "name": "mux_addr_data_i", - "descr": "Input of address mux", - "signals": [ - {"name": "mux_addr_input", "width": NUM_INPUTS * ADDR_W}, - ], - }, - { - "name": "mux_addr_data_o", - "descr": "Output of address mux", - "signals": [ - {"name": "output_iob_addr"}, - ], - }, - { - "name": "mux_wdata_data_i", - "descr": "Input of wdata mux", - "signals": [ - {"name": "mux_wdata_input", "width": NUM_INPUTS * DATA_W}, - ], - }, - { - "name": "mux_wdata_data_o", - "descr": "Output of wdata mux", - "signals": [ - {"name": "output_iob_wdata"}, - ], - }, - { - "name": "mux_wstrb_data_i", - "descr": "Input of wstrb mux", - "signals": [ - {"name": "mux_wstrb_input", "width": NUM_INPUTS * int(DATA_W / 8)}, - ], - }, - { - "name": "mux_wstrb_data_o", - "descr": "Output of wstrb mux", - "signals": [ - {"name": "output_iob_wstrb"}, - ], - }, - # Demux signals - { - "name": "demux_rdata_data_i", - "descr": "Input of rdata demux", - "signals": [ - {"name": "output_iob_rdata"}, - ], - }, - { - "name": "demux_rdata_data_o", - "descr": "Output of rdata demux", - "signals": [ - {"name": "demux_rdata_output", "width": NUM_INPUTS * DATA_W}, - ], - }, - { - "name": "demux_rvalid_data_i", - "descr": "Input of rvalid demux", - "signals": [ - {"name": "output_iob_rvalid"}, - ], - }, - { - "name": "demux_rvalid_data_o", - "descr": "Output of rvalid demux", - "signals": [ - {"name": "demux_rvalid_output", "width": NUM_INPUTS}, - ], - }, - { - "name": "demux_ready_data_i", - "descr": "Input of ready demux", - "signals": [ - {"name": "output_iob_ready"}, - ], - }, - { - "name": "demux_ready_data_o", - "descr": "Output of ready demux", - "signals": [ - {"name": "demux_ready_output", "width": NUM_INPUTS}, - ], - }, - # Priority encoder signals - { - "name": "prio_enc_i", - "descr": "Input of priority encoder", - "signals": [ - {"name": "mux_valid_input"}, - ], - }, - { - "name": "prio_enc_o", - "descr": "Output of priority encoder", - "signals": [ - {"name": "sel"}, - ], - }, - ] - attributes_dict["blocks"] = [ - { - "core_name": "iob_reg_r", - "instance_name": "sel_reg_r", - "parameters": { - "DATA_W": NBITS, - "RST_VAL": f"{NBITS}'b0", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "rst_i": "sel_reg_rst", - "data_i": "sel_reg_data_i", - "data_o": "sel_reg_data_o", - }, - }, - # muxers - { - "core_name": "iob_mux", - "instance_name": "iob_mux_valid", - "parameters": { - "DATA_W": 1, - "N": NUM_INPUTS, - }, - "connect": { - "sel_i": "input_sel", - "data_i": "mux_valid_data_i", - "data_o": "mux_valid_data_o", - }, - }, - { - "core_name": "iob_mux", - "instance_name": "iob_mux_addr", - "parameters": { - "DATA_W": ADDR_W, - "N": NUM_INPUTS, - }, - "connect": { - "sel_i": "input_sel", - "data_i": "mux_addr_data_i", - "data_o": "mux_addr_data_o", - }, - }, - { - "core_name": "iob_mux", - "instance_name": "iob_mux_wdata", - "parameters": { - "DATA_W": DATA_W, - "N": NUM_INPUTS, - }, - "connect": { - "sel_i": "input_sel", - "data_i": "mux_wdata_data_i", - "data_o": "mux_wdata_data_o", - }, - }, - { - "core_name": "iob_mux", - "instance_name": "iob_mux_wstrb", - "parameters": { - "DATA_W": int(DATA_W / 8), - "N": NUM_INPUTS, - }, - "connect": { - "sel_i": "input_sel", - "data_i": "mux_wstrb_data_i", - "data_o": "mux_wstrb_data_o", - }, - }, - { - "core_name": "iob_demux", - "instance_name": "iob_demux_rdata", - "parameters": { - "DATA_W": DATA_W, - "N": NUM_INPUTS, - }, - "connect": { - "sel_i": "input_sel_reg", - "data_i": "demux_rdata_data_i", - "data_o": "demux_rdata_data_o", - }, - }, - # demuxers - { - "core_name": "iob_demux", - "instance_name": "iob_demux_rvalid", - "parameters": { - "DATA_W": 1, - "N": NUM_INPUTS, - }, - "connect": { - "sel_i": "input_sel_reg", - "data_i": "demux_rvalid_data_i", - "data_o": "demux_rvalid_data_o", - }, - }, - { - "core_name": "iob_demux", - "instance_name": "iob_demux_ready", - "parameters": { - "DATA_W": 1, - "N": NUM_INPUTS, - }, - "connect": { - "sel_i": "input_sel", - "data_i": "demux_ready_data_i", - "data_o": "demux_ready_data_o", - }, - }, - # priority encoder - { - "core_name": "iob_prio_enc", - "instance_name": "sel_enc", - "parameters": { - "W": NUM_INPUTS, - "MODE": '"HIGH"', - }, - "connect": { - "unencoded_i": "prio_enc_i", - "encoded_o": "prio_enc_o", - }, - }, - ] - - # Connect demuxer outputs - verilog_code = "" - verilog_outputs = [] - for port_idx in range(NUM_INPUTS): - verilog_code += f""" - assign input{port_idx}_iob_rdata_o = demux_rdata_output[{port_idx*DATA_W}+:{DATA_W}]; - assign input{port_idx}_iob_rvalid_o = demux_rvalid_output[{port_idx}+:1]; - assign input{port_idx}_iob_ready_o = demux_ready_output[{port_idx}+:1]; -""" - verilog_outputs.append(f"input{port_idx}_iob_rdata") - verilog_outputs.append(f"input{port_idx}_iob_rvalid") - verilog_outputs.append(f"input{port_idx}_iob_ready") - verilog_code += "\n" - # Connect muxer inputs - for signal in ["valid", "addr", "wdata", "wstrb"]: - verilog_code += f" assign mux_{signal}_input = {{" - for port_idx in range(NUM_INPUTS - 1, -1, -1): - # Include padding bits for address - if signal == "addr": - verilog_code += f"{{{NBITS}{{1'b0}}}}, " - verilog_code += f"input{port_idx}_iob_{signal}_i, " - verilog_code = verilog_code[:-2] + "};\n" - verilog_outputs.append(f"mux_{signal}_input") - # Create snippet with demuxer and muxer connections - attributes_dict["snippets"] = [ - { - "verilog_code": verilog_code, - }, - ] - - return attributes_dict diff --git a/lib/hardware/buses/iob_mux/iob_mux.py b/lib/hardware/buses/iob_mux/iob_mux.py deleted file mode 100644 index b626dad0a..000000000 --- a/lib/hardware/buses/iob_mux/iob_mux.py +++ /dev/null @@ -1,80 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Width of data interface", - }, - { - "name": "N", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Number of inputs", - }, - ], - "ports": [ - { - "name": "sel_i", - "descr": "Selector interface", - "signals": [ - { - "name": "sel", - "width": "($clog2(N)+($clog2(N)==0))", - "direction": "input", - }, - ], - }, - { - "name": "data_i", - "descr": "Input port", - "signals": [ - { - "name": "data", - "width": "N*DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - integer input_sel; - always @* begin - data_o = {DATA_W{1'b0}}; - for (input_sel = 0; input_sel < N; input_sel = input_sel + 1) begin : gen_mux - if (input_sel == sel_i) begin - data_o = data_i[input_sel*DATA_W+:DATA_W]; - end - end - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_reverse/iob_reverse.py b/lib/hardware/buses/iob_reverse/iob_reverse.py deleted file mode 100644 index 400dc640d..000000000 --- a/lib/hardware/buses/iob_reverse/iob_reverse.py +++ /dev/null @@ -1,57 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "data_i", - "descr": "Input port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "data_o", - "descr": "Output port", - "signals": [ - { - "name": "data", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": f""" - genvar pos; - generate - for (pos = 0; pos < DATA_W; pos = pos + 1) begin : reverse - assign data_o[pos] = data_i[(DATA_W-1)-pos]; - end - endgenerate - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/buses/iob_split/iob_split.py b/lib/hardware/buses/iob_split/iob_split.py deleted file mode 100644 index 539840986..000000000 --- a/lib/hardware/buses/iob_split/iob_split.py +++ /dev/null @@ -1,351 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - assert "name" in py_params_dict, print( - "Error: Missing name for generated split module." - ) - assert "num_outputs" in py_params_dict, print( - "Error: Missing number of outputs for generated split module." - ) - - NUM_OUTPUTS = int(py_params_dict["num_outputs"]) - # Number of bits required for output selection - NBITS = (NUM_OUTPUTS - 1).bit_length() - - ADDR_W = int(py_params_dict["addr_w"]) if "addr_w" in py_params_dict else 32 - DATA_W = int(py_params_dict["data_w"]) if "data_w" in py_params_dict else 32 - - attributes_dict = { - "name": py_params_dict["name"], - "version": "0.1", - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and async reset", - }, - { - "name": "reset_i", - "descr": "Reset signal", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": "1", - }, - ], - }, - { - "name": "input_s", - "interface": { - "type": "iob", - "subtype": "slave", - "file_prefix": py_params_dict["name"] + "_input_", - "port_prefix": "input_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W, - }, - "descr": "Split input", - }, - ], - } - for port_idx in range(NUM_OUTPUTS): - attributes_dict["ports"].append( - { - "name": f"output_{port_idx}_m", - "interface": { - "type": "iob", - "subtype": "master", - "file_prefix": f"{py_params_dict['name']}_output{port_idx}_", - "port_prefix": f"output{port_idx}_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W - NBITS, - }, - "descr": "Split output interface", - }, - ) - attributes_dict["wires"] = [ - # Output selection signals - { - "name": "sel_reg_en_rst", - "descr": "Enable and reset signal for sel_reg", - "signals": [ - {"name": "input_iob_valid"}, - {"name": "rst"}, - ], - }, - { - "name": "sel_reg_data_i", - "descr": "Input of sel_reg", - "signals": [ - {"name": "sel", "width": NBITS}, - ], - }, - { - "name": "sel_reg_data_o", - "descr": "Output of sel_reg", - "signals": [ - {"name": "sel_reg", "width": NBITS}, - ], - }, - { - "name": "output_sel", - "descr": "Select output interface", - "signals": [ - {"name": "sel"}, - ], - }, - { - "name": "output_sel_reg", - "descr": "Registered select output interface", - "signals": [ - {"name": "sel_reg"}, - ], - }, - # Demux signals - { - "name": "demux_valid_data_i", - "descr": "Input of valid demux", - "signals": [ - {"name": "input_iob_valid"}, - ], - }, - { - "name": "demux_valid_data_o", - "descr": "Output of valid demux", - "signals": [ - {"name": "demux_valid_output", "width": NUM_OUTPUTS}, - ], - }, - { - "name": "demux_addr_data_i", - "descr": "Input of address demux", - "signals": [ - {"name": "input_iob_addr"}, - ], - }, - { - "name": "demux_addr_data_o", - "descr": "Output of address demux", - "signals": [ - {"name": "demux_addr_output", "width": NUM_OUTPUTS * ADDR_W}, - ], - }, - { - "name": "demux_wdata_data_i", - "descr": "Input of wdata demux", - "signals": [ - {"name": "input_iob_wdata"}, - ], - }, - { - "name": "demux_wdata_data_o", - "descr": "Output of wdata demux", - "signals": [ - {"name": "demux_wdata_output", "width": NUM_OUTPUTS * DATA_W}, - ], - }, - { - "name": "demux_wstrb_data_i", - "descr": "Input of wstrb demux", - "signals": [ - {"name": "input_iob_wstrb"}, - ], - }, - { - "name": "demux_wstrb_data_o", - "descr": "Output of wstrb demux", - "signals": [ - {"name": "demux_wstrb_output", "width": NUM_OUTPUTS * int(DATA_W / 8)}, - ], - }, - # Mux signals - { - "name": "mux_rdata_data_i", - "descr": "Input of rdata mux", - "signals": [ - {"name": "mux_rdata_input", "width": NUM_OUTPUTS * DATA_W}, - ], - }, - { - "name": "mux_rdata_data_o", - "descr": "Output of rdata mux", - "signals": [ - {"name": "input_iob_rdata"}, - ], - }, - { - "name": "mux_rvalid_data_i", - "descr": "Input of rvalid mux", - "signals": [ - {"name": "mux_rvalid_input", "width": NUM_OUTPUTS}, - ], - }, - { - "name": "mux_rvalid_data_o", - "descr": "Output of rvalid mux", - "signals": [ - {"name": "input_iob_rvalid"}, - ], - }, - { - "name": "mux_ready_data_i", - "descr": "Input of ready mux", - "signals": [ - {"name": "mux_ready_input", "width": NUM_OUTPUTS}, - ], - }, - { - "name": "mux_ready_data_o", - "descr": "Output of ready mux", - "signals": [ - {"name": "input_iob_ready"}, - ], - }, - ] - attributes_dict["blocks"] = [ - { - "core_name": "iob_reg_re", - "instance_name": "sel_reg_re", - "parameters": { - "DATA_W": NBITS, - "RST_VAL": f"{NBITS}'b0", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "en_rst_i": "sel_reg_en_rst", - "data_i": "sel_reg_data_i", - "data_o": "sel_reg_data_o", - }, - }, - # Demuxers - { - "core_name": "iob_demux", - "instance_name": "iob_demux_valid", - "parameters": { - "DATA_W": 1, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel", - "data_i": "demux_valid_data_i", - "data_o": "demux_valid_data_o", - }, - }, - { - "core_name": "iob_demux", - "instance_name": "iob_demux_addr", - "parameters": { - "DATA_W": ADDR_W, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel", - "data_i": "demux_addr_data_i", - "data_o": "demux_addr_data_o", - }, - }, - { - "core_name": "iob_demux", - "instance_name": "iob_demux_wdata", - "parameters": { - "DATA_W": DATA_W, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel", - "data_i": "demux_wdata_data_i", - "data_o": "demux_wdata_data_o", - }, - }, - { - "core_name": "iob_demux", - "instance_name": "iob_demux_wstrb", - "parameters": { - "DATA_W": int(DATA_W / 8), - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel", - "data_i": "demux_wstrb_data_i", - "data_o": "demux_wstrb_data_o", - }, - }, - { - "core_name": "iob_mux", - "instance_name": "iob_mux_rdata", - "parameters": { - "DATA_W": DATA_W, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel_reg", - "data_i": "mux_rdata_data_i", - "data_o": "mux_rdata_data_o", - }, - }, - # Muxers - { - "core_name": "iob_mux", - "instance_name": "iob_mux_rvalid", - "parameters": { - "DATA_W": 1, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel_reg", - "data_i": "mux_rvalid_data_i", - "data_o": "mux_rvalid_data_o", - }, - }, - { - "core_name": "iob_mux", - "instance_name": "iob_mux_ready", - "parameters": { - "DATA_W": 1, - "N": NUM_OUTPUTS, - }, - "connect": { - "sel_i": "output_sel", - "data_i": "mux_ready_data_i", - "data_o": "mux_ready_data_o", - }, - }, - ] - attributes_dict["snippets"] = [ - { - # Extract output selection bits from address - "verilog_code": f" assign sel = input_iob_addr_i[{ADDR_W-1}-:{NBITS}];", - }, - ] - - # Connect demuxers outputs - verilog_code = "" - for port_idx in range(NUM_OUTPUTS): - verilog_code += f""" - assign output{port_idx}_iob_valid_o = demux_valid_output[{port_idx}+:1]; - assign output{port_idx}_iob_addr_o = demux_addr_output[{port_idx*ADDR_W}+:{ADDR_W-NBITS}]; - assign output{port_idx}_iob_wdata_o = demux_wdata_output[{port_idx*DATA_W}+:{DATA_W}]; - assign output{port_idx}_iob_wstrb_o = demux_wstrb_output[{port_idx*int(DATA_W/8)}+:{int(DATA_W/8)}]; -""" - verilog_code += "\n" - # Connect muxer inputs - for signal in ["rdata", "rvalid", "ready"]: - verilog_code += f" assign mux_{signal}_input = {{" - for port_idx in range(NUM_OUTPUTS - 1, -1, -1): - verilog_code += f"output{port_idx}_iob_{signal}_i, " - verilog_code = verilog_code[:-2] + "};\n" - # Create snippet with muxer and demuxer connections - attributes_dict["snippets"] += [ - { - "verilog_code": verilog_code, - }, - ] - - return attributes_dict diff --git a/lib/hardware/buses/iob_tasks/document/Makefile b/lib/hardware/buses/iob_tasks/document/Makefile deleted file mode 100644 index 70a92c425..000000000 --- a/lib/hardware/buses/iob_tasks/document/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -PNGS=$(patsubst %.drom,%.png,$(wildcard *.drom)) - -all: clean $(PNGS) - -%.png: %.svg - rsvg-convert -b '#ffffff' $< -o $@ - rm -rf *.svg - -%.svg: %.drom - wavedrompy -i $< -s $@ - -clean: - rm -rf *.png - -.PHONY: all clean - diff --git a/lib/hardware/buses/iob_tasks/document/README.md b/lib/hardware/buses/iob_tasks/document/README.md deleted file mode 100644 index c0c72e72e..000000000 --- a/lib/hardware/buses/iob_tasks/document/README.md +++ /dev/null @@ -1,38 +0,0 @@ - - -# IOb-interface - -The IOb Native Interface, or IOb interface shortly, has the following signals -from the slave perspective. (Note that the `_i` and `_o` suffixes denote input -and output, respectively.) -- `clk_i`: interface input clock -- `valid_i`: source signal valid transaction from master input -- `addr_i`: address for read or write access input -- `wdata_i`: data to write -- `wstrb_i`: byte write strobe input -- `rdata_o`: read data -- `rvalid_o`: valid read data -- `ready_o`: slave ready for transaction output - -Transactions occur when both `valid_i` and `ready_o` are HIGH for one `clk_i` -period. - -Use `wstrb_i` to distinguish between READ and WRITE operations. READ operations -occur when all `wstrb_i` bits are LOW. -Byte `i` is written if `wstrb_i[i]` is HIGH and not otherwise. - -## Read Transaction -For READ operations: when `valid_i` and `ready_o` are HIGH, address `addr_i` is -read. The data on `rdata_o` is available when `rvalid_o` is HIGH. - -![Example Read Transaction](iob_if_read.png "Read Transaction") - -## Write Transaction -For WRITE operations: when `valid_i` and `ready_o` are HIGH, the bytes in -`wdata_i` validated by `wstrb_i` are written to address `addr_i.` - -![Example Write Transaction](iob_if_write.png "Write Transaction") diff --git a/lib/hardware/buses/iob_tasks/document/iob_if_read.drom b/lib/hardware/buses/iob_tasks/document/iob_if_read.drom deleted file mode 100644 index 39c3c16ed..000000000 --- a/lib/hardware/buses/iob_tasks/document/iob_if_read.drom +++ /dev/null @@ -1,11 +0,0 @@ -{signal: [ - {name: 'clk_i', wave: 'p...........'}, - {name: 'valid_i', wave: '0..1......0.'}, - - {name: 'addr_i', wave: 'x..=..===.xx', data: ['A0', 'A1', 'A2', 'A3']}, - {name: 'wdata_i', wave: 'x.........x.'}, - {name: 'wstrb_i', wave: 'x..0......x.'}, - {name: 'rdata_o', wave: 'x.....===x=x', data: ['D0', 'D1', 'D2', 'D3']}, - {name: 'rvalid_o', wave: '0.....1..010'}, - {name: 'ready_o', wave: '0....1..01..'}, -]} diff --git a/lib/hardware/buses/iob_tasks/document/iob_if_read.drom.license b/lib/hardware/buses/iob_tasks/document/iob_if_read.drom.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/buses/iob_tasks/document/iob_if_read.drom.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/buses/iob_tasks/document/iob_if_read.png b/lib/hardware/buses/iob_tasks/document/iob_if_read.png deleted file mode 100644 index 24e28d2a418ccec2fa9dbf31a39bafb7246e0bc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15488 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�IeKt-ph*iJYBBys*W?u&a&MuHviAI@ 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 VSFBd~!cFRzkyMoUDrWrl{{XnY&e;F} diff --git a/lib/hardware/buses/iob_tasks/document/iob_if_write.png.license b/lib/hardware/buses/iob_tasks/document/iob_if_write.png.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/buses/iob_tasks/document/iob_if_write.png.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.cpp b/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.cpp deleted file mode 100644 index 9b7085f7c..000000000 --- a/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -#include "iob_tasks.h" - -// Uncomment to enable periodic time printing -// Useful to figure out when we should enable VCD dump -//#define PERIODIC_TIME_PRINT 1000000 - -// Keep track of simulation time -vluint64_t main_time = 0; - -// Delayed start time of VCD trace dump -// Used to avoid large VCD dump files during long simulations -#if (VM_TRACE == 1) -vluint64_t vcd_delayed_start = 0; -#endif - -// Store timer related settings (clk and eval) -timer_settings_t task_timer_settings; - -// Set a signal value with correct data type -static void set_signal(void *signal, signal_datatype_t data_type, - unsigned int data) { - switch (data_type) { - case USINT: - *(unsigned short int *)signal = data; - break; - case UCHAR: - *(unsigned char *)signal = data; - break; - default: - *(unsigned int *)signal = data; - } -} - -// -// Verilator functions to drive the IOb Native protocol -// - -// Write data to IOb Native slave -void iob_write(unsigned int cpu_address, unsigned int cpu_data, - unsigned int nbytes, iob_native_t *native_if) { - char wstrb_int = 0; - switch (nbytes) { - case 1: - wstrb_int = 0b01; - break; - case 2: - wstrb_int = 0b011; - break; - default: - wstrb_int = 0b01111; - break; - } - Timer(1); // In sync with clk posedge + 1ns - set_signal(native_if->iob_addr, native_if->iob_addr_type, cpu_address); - *(native_if->iob_valid) = 1; - *(native_if->iob_wstrb) = wstrb_int << (cpu_address & 0b011); - *(native_if->iob_wdata) = - cpu_data << ((cpu_address & 0b011) * 8); // align data to 32 bits - Timer(CLK_PERIOD - 1); // In sync with clk posedge - while (!*(native_if->iob_ready)) - Timer(CLK_PERIOD); - Timer(1); // In sync with clk posedge + 1ns - *(native_if->iob_wstrb) = 0; - *(native_if->iob_valid) = 0; - Timer(CLK_PERIOD - 1); // In sync with clk posedge -} - -// Read data from IOb Native slave -unsigned int iob_read(unsigned int cpu_address, iob_native_t *native_if) { - unsigned int read_reg = 0; - bool read_complete = 0; - Timer(1); // In sync with clk posedge + 1ns - set_signal(native_if->iob_addr, native_if->iob_addr_type, cpu_address); - *(native_if->iob_valid) = 1; - Timer(CLK_PERIOD - 1); // In sync with clk posedge - while (!*(native_if->iob_ready)) - Timer(CLK_PERIOD); - if (*(native_if->iob_rvalid)) { - read_reg = *(native_if->iob_rdata) >> - ((cpu_address & 0b011) * 8); // align to 32 bits - read_complete = 1; - } - Timer(1); // In sync with clk posedge + 1ns - *(native_if->iob_valid) = 0; - Timer(CLK_PERIOD - 1); // In sync with clk posedge - if (read_complete) - return read_reg; - while (!*(native_if->iob_rvalid)) - Timer(CLK_PERIOD); - read_reg = *(native_if->iob_rdata) >> - ((cpu_address & 0b011) * 8); // align to 32 bits - return read_reg; -} - -// Delay -void Timer(unsigned int ns) { - for (int i = 0; i < ns; i++) { - if (!(main_time % (CLK_PERIOD / 2))) { - *(task_timer_settings.clk) = !*(task_timer_settings.clk); - (*task_timer_settings.eval)(); - // To add a new clk follow the example - // if(!(main_time%(EXAMPLE_CLK_PERIOD/2))){ - // *(task_timer_settings.example_clk) = - // !*(task_timer_settings.example_clk); - - // Also eval 1ns after edge - } else if (!((main_time - 1) % (CLK_PERIOD / 2))) { - (*task_timer_settings.eval)(); - } -#if (VM_TRACE == 1) - if (main_time > vcd_delayed_start) - (*task_timer_settings.dump)(main_time); // Dump values into tracing file -#endif -#ifdef PERIODIC_TIME_PRINT - if (main_time % PERIODIC_TIME_PRINT == 0) - printf("[iob_tasks] Time %u\n", main_time); -#endif - main_time += 1; - } -} diff --git a/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.h b/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.h deleted file mode 100644 index e3f3d9871..000000000 --- a/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#ifndef H_IOB_TASKS_H -#define H_IOB_TASKS_H - -#include "bsp.h" -#include - -#ifndef CLK_PERIOD -#define CLK_PERIOD 1000000000 / FREQ // 1/100MHz*10^9 = 10 ns -#endif - -typedef enum { UINT, USINT, UCHAR } signal_datatype_t; - -// Struct defining iob-native interface -typedef struct { - unsigned char *iob_valid; - void *iob_addr; - signal_datatype_t iob_addr_type; - unsigned int *iob_wdata; - unsigned char *iob_wstrb; - unsigned int *iob_rdata; - unsigned char *iob_rvalid; - unsigned char *iob_ready; -} iob_native_t; - -typedef struct { - unsigned char *clk; - void (*eval)(void); - void (*dump)(vluint64_t); -} timer_settings_t; - -void Timer(unsigned int ns); -void iob_write(unsigned int cpu_address, unsigned int cpu_data, - unsigned int nbytes, iob_native_t *native_if); -unsigned int iob_read(unsigned int cpu_address, iob_native_t *native_if); - -#endif // H_IOB_TASKS_H diff --git a/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.vs b/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.vs deleted file mode 100644 index 74f2a72aa..000000000 --- a/lib/hardware/buses/iob_tasks/hardware/src/iob_tasks.vs +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -// -// Tasks for the IOb Native protocol -// - -`define IOB_NBYTES (DATA_W/8) -`define IOB_GET_NBYTES(WIDTH) (WIDTH/8 + |(WIDTH%8)) -`define IOB_NBYTES_W $clog2(`IOB_NBYTES) -`define IOB_WORD_ADDR(ADDR) ((ADDR>>`IOB_NBYTES_W)<<`IOB_NBYTES_W) - -`define IOB_BYTE_OFFSET(ADDR) (ADDR%(DATA_W/8)) - -`define IOB_GET_WDATA(ADDR, DATA) (DATA<<(8*`IOB_BYTE_OFFSET(ADDR))) -`define IOB_GET_WSTRB(ADDR, WIDTH) (((1<<`IOB_GET_NBYTES(WIDTH))-1)<<`IOB_BYTE_OFFSET(ADDR)) -`define IOB_GET_RDATA(ADDR, DATA, WIDTH) ((DATA>>(8*`IOB_BYTE_OFFSET(ADDR)))&((1<>`IOB_NBYTES_W)<<`IOB_NBYTES_W) - -`define IOB_BYTE_OFFSET(ADDR) (ADDR%(DATA_W/8)) - -`define IOB_GET_WDATA(ADDR, DATA) (DATA<<(8*`IOB_BYTE_OFFSET(ADDR))) -`define IOB_GET_WSTRB(ADDR, WIDTH) (((1<<`IOB_GET_NBYTES(WIDTH))-1)<<`IOB_BYTE_OFFSET(ADDR)) -`define IOB_GET_RDATA(ADDR, DATA, WIDTH) ((DATA>>(8*`IOB_BYTE_OFFSET(ADDR)))&((1< (quant / 2)); - - always @* begin - if (acc_out[FRAC_W-1:0] == {1'b1, {FRAC_W - 1{1'b0}}}) - quant = acc_out[DATA_W-1:FRAC_W] + ^acc_out[DATA_W-1:FRAC_W]; - else if (acc_out[FRAC_W-1]) quant = acc_out[DATA_W-1:FRAC_W] + 1'b1; - else quant = acc_out[DATA_W-1:FRAC_W]; - end -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/clocks_resets/iob_nco/software/example_firmware.c b/lib/hardware/clocks_resets/iob_nco/software/example_firmware.c deleted file mode 100644 index 1a48d5502..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/example_firmware.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "bsp.h" -#include "iob_nco.h" -#include "iob_uart.h" -#include "periphs.h" -#include "printf.h" -#include "system.h" - -int main() { - unsigned long long elapsed; - unsigned int elapsedu; - - // init nco and uart - nco_init(NCO0_BASE); - uart_init(UART_BASE, FREQ / BAUD); - - printf("\nHello world!\n"); - - // read current nco count, compute elapsed time - elapsed = nco_get_count(); - elapsedu = elapsed / (FREQ / 1000000); - - printf("\nExecution time: %d clock cycles\n", (unsigned int)elapsed); - printf("\nExecution time: %dus @%dMHz\n\n", elapsedu, FREQ / 1000000); - - uart_finish(); -} diff --git a/lib/hardware/clocks_resets/iob_nco/software/linux/README.md b/lib/hardware/clocks_resets/iob_nco/software/linux/README.md deleted file mode 100644 index ebb0acced..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/linux/README.md +++ /dev/null @@ -1,24 +0,0 @@ - - -# IOb NCO Linux Kernel Drivers -- Structure: - - `drivers/`: directory with linux kernel module drivers for iob_nco - - `iob_nco_main.c`: driver source - - `[iob_nco.h]` and `[iob_nco_sysfs.h]`: header files generated by: - ```bash - python3 .path/to/iob-linux/scripts/drivers.py iob_nco -o [output_dir] - ``` - - `driver.mk`: makefile segment with `iob_nco-obj:` target for driver - compilation - - `user/`: directory with user application example that uses iob_nco - drivers - - `iob_nco_user.c`: example user application that uses iob_nco - drivers - - `Makefile`: user application compilation targets - - `iob_nco.dts`: device tree template with iob_nco node - - manually add the `nco` node to the system device tree so the - iob_nco is recognized by the linux kernel diff --git a/lib/hardware/clocks_resets/iob_nco/software/linux/drivers/driver.mk b/lib/hardware/clocks_resets/iob_nco/software/linux/drivers/driver.mk deleted file mode 100644 index 339c95d28..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/linux/drivers/driver.mk +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -iob_nco-objs := iob_nco_main.o iob_class/iob_class_utils.o diff --git a/lib/hardware/clocks_resets/iob_nco/software/linux/drivers/iob_nco_main.c b/lib/hardware/clocks_resets/iob_nco/software/linux/drivers/iob_nco_main.c deleted file mode 100644 index aad310bc4..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/linux/drivers/iob_nco_main.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* iob_nco_main.c: driver for iob_nco - * using device platform. No hardcoded hardware address: - * 1. load driver: insmod iob_nco.ko - * 2. run user app: ./user/user - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iob_class/iob_class_utils.h" -#include "iob_nco.h" - -static int iob_nco_probe(struct platform_device *); -static int iob_nco_remove(struct platform_device *); - -static ssize_t iob_nco_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t iob_nco_write(struct file *, const char __user *, size_t, - loff_t *); -static loff_t iob_nco_llseek(struct file *, loff_t, int); -static int iob_nco_open(struct inode *, struct file *); -static int iob_nco_release(struct inode *, struct file *); - -static struct iob_data iob_nco_data = {0}; -DEFINE_MUTEX(iob_nco_mutex); - -#include "iob_nco_sysfs.h" - -static const struct file_operations iob_nco_fops = { - .owner = THIS_MODULE, - .write = iob_nco_write, - .read = iob_nco_read, - .llseek = iob_nco_llseek, - .open = iob_nco_open, - .release = iob_nco_release, -}; - -static const struct of_device_id of_iob_nco_match[] = { - {.compatible = "iobundle,nco0"}, - {}, -}; - -static struct platform_driver iob_nco_driver = { - .driver = - { - .name = "iob_nco", - .owner = THIS_MODULE, - .of_match_table = of_iob_nco_match, - }, - .probe = iob_nco_probe, - .remove = iob_nco_remove, -}; - -// -// Module init and exit functions -// -static int iob_nco_probe(struct platform_device *pdev) { - struct resource *res; - int result = 0; - - if (iob_nco_data.device != NULL) { - pr_err("[Driver] %s: No more devices allowed!\n", IOB_NCO_DRIVER_NAME); - - return -ENODEV; - } - - pr_info("[Driver] %s: probing.\n", IOB_NCO_DRIVER_NAME); - - // Get the I/O region base address - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - pr_err("[Driver]: Failed to get I/O resource!\n"); - result = -ENODEV; - goto r_get_resource; - } - - // Request and map the I/O region - iob_nco_data.regbase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(iob_nco_data.regbase)) { - result = PTR_ERR(iob_nco_data.regbase); - goto r_ioremmap; - } - iob_nco_data.regsize = resource_size(res); - - // Alocate char device - result = alloc_chrdev_region(&iob_nco_data.devnum, 0, 1, IOB_NCO_DRIVER_NAME); - if (result) { - pr_err("%s: Failed to allocate device number!\n", IOB_NCO_DRIVER_NAME); - goto r_alloc_region; - } - - cdev_init(&iob_nco_data.cdev, &iob_nco_fops); - - result = cdev_add(&iob_nco_data.cdev, iob_nco_data.devnum, 1); - if (result) { - pr_err("%s: Char device registration failed!\n", IOB_NCO_DRIVER_NAME); - goto r_cdev_add; - } - - // Create device class // todo: make a dummy driver just to create and own the - // class: https://stackoverflow.com/a/16365027/8228163 - if ((iob_nco_data.class = class_create(THIS_MODULE, IOB_NCO_DRIVER_CLASS)) == - NULL) { - printk("Device class can not be created!\n"); - goto r_class; - } - - // Create device file - iob_nco_data.device = device_create( - iob_nco_data.class, NULL, iob_nco_data.devnum, NULL, IOB_NCO_DRIVER_NAME); - if (iob_nco_data.device == NULL) { - printk("Can not create device file!\n"); - goto r_device; - } - - result = iob_nco_create_device_attr_files(iob_nco_data.device); - if (result) { - pr_err("Cannot create device attribute file......\n"); - goto r_dev_file; - } - - dev_info(&pdev->dev, "initialized.\n"); - goto r_ok; - -r_dev_file: - iob_nco_remove_device_attr_files(&iob_nco_data); -r_device: - class_destroy(iob_nco_data.class); -r_class: - cdev_del(&iob_nco_data.cdev); -r_cdev_add: - unregister_chrdev_region(iob_nco_data.devnum, 1); -r_alloc_region: - // iounmap is managed by devm -r_ioremmap: -r_get_resource: -r_ok: - - return result; -} - -static int iob_nco_remove(struct platform_device *pdev) { - iob_nco_remove_device_attr_files(&iob_nco_data); - class_destroy(iob_nco_data.class); - cdev_del(&iob_nco_data.cdev); - unregister_chrdev_region(iob_nco_data.devnum, 1); - // Note: no need for iounmap, since we are using devm_ioremap_resource() - - dev_info(&pdev->dev, "exiting.\n"); - - return 0; -} - -static int __init test_counter_init(void) { - pr_info("[Driver] %s: initializing.\n", IOB_NCO_DRIVER_NAME); - - return platform_driver_register(&iob_NCO_driver); -} - -static void __exit test_counter_exit(void) { - pr_info("[Driver] %s: exiting.\n", IOB_NCO_DRIVER_NAME); - platform_driver_unregister(&iob_NCO_driver); -} - -// -// File operations -// - -static int iob_nco_open(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_nco device opened\n"); - - if (!mutex_trylock(&iob_nco_mutex)) { - pr_info("Another process is accessing the device\n"); - - return -EBUSY; - } - - return 0; -} - -static int iob_nco_release(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_nco device closed\n"); - - mutex_unlock(&iob_nco_mutex); - - return 0; -} - -static ssize_t iob_nco_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) { - int size = 0; - u32 value = 0; - - /* read value from register */ - switch (*ppos) { - case IOB_NCO_DATA_LOW_ADDR: - value = iob_data_read_reg(iob_nco_data.regbase, IOB_NCO_DATA_LOW_ADDR, - IOB_NCO_DATA_LOW_W); - size = (IOB_NCO_DATA_LOW_W >> 3); // bit to bytes - pr_info("[Driver] Read data low!\n"); - break; - case IOB_NCO_DATA_HIGH_ADDR: - value = iob_data_read_reg(iob_nco_data.regbase, IOB_NCO_DATA_HIGH_ADDR, - IOB_NCO_DATA_HIGH_W); - size = (IOB_NCO_DATA_HIGH_W >> 3); // bit to bytes - pr_info("[Driver] Read data high!\n"); - break; - case IOB_NCO_VERSION_ADDR: - value = iob_data_read_reg(iob_nco_data.regbase, IOB_NCO_VERSION_ADDR, - IOB_NCO_VERSION_W); - size = (IOB_NCO_VERSION_W >> 3); // bit to bytes - pr_info("[Driver] Read version!\n"); - break; - default: - // invalid address - no bytes read - return 0; - } - - // Read min between count and REG_SIZE - if (size > count) - size = count; - - if (copy_to_user(buf, &value, size)) - return -EFAULT; - - return count; -} - -static ssize_t iob_nco_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) { - int size = 0; - u32 value = 0; - - switch (*ppos) { - case IOB_NCO_SOFTRESET_ADDR: - size = (IOB_NCO_SOFTRESET_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_nco_data.regbase, value, IOB_NCO_SOFTRESET_ADDR, - IOB_NCO_SOFTRESET_W); - pr_info("[Driver] Reset iob_nco: 0x%x\n", value); - break; - case IOB_NCO_ENABLE_ADDR: - size = (IOB_NCO_ENABLE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_nco_data.regbase, value, IOB_NCO_ENABLE_ADDR, - IOB_NCO_ENABLE_W); - pr_info("[Driver] Enable iob_nco: 0x%x\n", value); - break; - case IOB_NCO_SAMPLE_ADDR: // sample counter - size = (IOB_NCO_SAMPLE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_nco_data.regbase, value, IOB_NCO_SAMPLE_ADDR, - IOB_NCO_SAMPLE_W); - pr_info("[Driver] Sample iob_nco: 0x%x\n", value); - break; - default: - pr_info("[Driver] Invalid write address 0x%x\n", (unsigned int)*ppos); - // invalid address - no bytes written - return 0; - } - - return count; -} - -/* Custom lseek function - * check: lseek(2) man page for whence modes - */ -static loff_t iob_nco_llseek(struct file *filp, loff_t offset, int whence) { - loff_t new_pos = -1; - - switch (whence) { - case SEEK_SET: - new_pos = offset; - break; - case SEEK_CUR: - new_pos = filp->f_pos + offset; - break; - case SEEK_END: - new_pos = (1 << IOB_NCO_CSRS_ADDR_W) + offset; - break; - default: - return -EINVAL; - } - - // Check for valid bounds - if (new_pos < 0 || new_pos > iob_nco_data.regsize) { - return -EINVAL; - } - - // Update file position - filp->f_pos = new_pos; - - return new_pos; -} - -module_init(test_counter_init); -module_exit(test_counter_exit); - -MODULE_LICENSE("Dual MIT/GPL"); -MODULE_AUTHOR("IObundle"); -MODULE_DESCRIPTION("IOb-nco Drivers"); -MODULE_VERSION("0.10"); diff --git a/lib/hardware/clocks_resets/iob_nco/software/linux/iob_nco.dts b/lib/hardware/clocks_resets/iob_nco/software/linux/iob_nco.dts deleted file mode 100644 index c43cd9988..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/linux/iob_nco.dts +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* Copyright (c) 2024 IObundle */ - -/dts-v1/; - -/ { - #address-cells = <1>; - #size-cells = <1>; - model = "IOb-SoC, VexRiscv"; - compatible = "IOb-SoC, VexRiscv"; - // CPU - // Memory - // Choosen - soc { - #address-cells = <1>; - #size-cells = <1>; - compatible = "iobundle,iob-soc", "simple-bus"; - ranges; - - // Other SOC peripherals go here - - // Add this Node to device tree - NCO0: nco@/*NCO0_ADDR_MACRO*/ { - compatible = "iobundle,nco0"; - reg = <0x/*NCO0_ADDR_MACRO*/ 0x100>; - }; - - }; -}; diff --git a/lib/hardware/clocks_resets/iob_nco/software/linux/user/Makefile b/lib/hardware/clocks_resets/iob_nco/software/linux/user/Makefile deleted file mode 100644 index fb7bd3def..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/linux/user/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -SRC = $(wildcard *.c) -HDR += iob_nco.h -FLAGS = -Wall -Werror -O2 -FLAGS += -static -FLAGS += -march=rv32imac -FLAGS += -mabi=ilp32 -BIN = iob_nco_user -CC = riscv64-unknown-linux-gnu-gcc - -all: $(BIN) - -$(BIN): $(SRC) $(HDR) - $(CC) $(FLAGS) $(INCLUDE) -o $(BIN) $(SRC) - -LIB_DIR = ../../../../../../ -IOB_LINUX_DIR ?= ../../../$(LIB_DIR)/iob-linux -$(HDR): - cd $(LIB_DIR) && \ - .$(IOB_LINUX_DIR)/scripts/drivers.py iob_nco -o `realpath $(CURDIR)` - -clean: - rm -rf $(BIN) $(HDR) diff --git a/lib/hardware/clocks_resets/iob_nco/software/linux/user/iob_nco_user.c b/lib/hardware/clocks_resets/iob_nco/software/linux/user/iob_nco_user.c deleted file mode 100644 index 5b8dcf762..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/linux/user/iob_nco_user.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include -#include -#include - -#include "iob_nco.h" - -int sysfs_read_file(const char *filename, uint32_t *read_value) { - // Open file for read - FILE *file = fopen(filename, "r"); - if (file == NULL) { - perror("[User] Failed to open the file"); - return -1; - } - - // Read uint32_t value from file in ASCII - ssize_t ret = fscanf(file, "%u", read_value); - if (ret == -1) { - perror("[User] Failed to read from file"); - fclose(file); - return -1; - } - - fclose(file); - - return ret; -} - -int sysfs_write_file(const char *filename, uint32_t write_value) { - // Open file for write - FILE *file = fopen(filename, "w"); - if (file == NULL) { - perror("[User] Failed to open the file"); - return -1; - } - - // Write uint32_t value to file in ASCII - ssize_t ret = fprintf(file, "%u", write_value); - if (ret == -1) { - perror("[User] Failed to write to file"); - fclose(file); - return -1; - } - - fclose(file); - - return ret; -} - -int nco_reset() { - if (sysfs_write_file(IOB_NCO_SYSFILE_RESET, 1) == -1) { - return -1; - } - if (sysfs_write_file(IOB_NCO_SYSFILE_RESET, 0) == -1) { - return -1; - } - - return 0; -} - -int nco_init() { - if (nco_reset()) { - return -1; - } - - if (sysfs_write_file(IOB_NCO_SYSFILE_ENABLE, 1) == -1) { - return -1; - } - - return 0; -} - -int nco_print_version() { - uint32_t ret = -1; - uint32_t version = 0; - - ret = sysfs_read_file(IOB_NCO_SYSFILE_VERSION, &version); - if (ret == -1) { - return ret; - } - - printf("[User] Version: 0x%x\n", version); - return 0; -} - -int nco_get_count(uint64_t *count) { - uint32_t ret = -1; - uint32_t data = 0; - - // Sample nco counter - if (sysfs_write_file(IOB_NCO_SYSFILE_SAMPLE, 1) == -1) { - return -1; - } - if (sysfs_write_file(IOB_NCO_SYSFILE_SAMPLE, 0) == -1) { - return -1; - } - - // Read sampled nco counter - ret = sysfs_read_file(IOB_NCO_SYSFILE_DATA_HIGH, &data); - if (ret == -1) { - return -1; - } - *count = ((uint64_t)data) << IOB_NCO_DATA_LOW_W; - ret = sysfs_read_file(IOB_NCO_SYSFILE_DATA_LOW, &data); - if (ret == -1) { - return -1; - } - (*count) = (*count) | (uint64_t)data; - - return 0; -} - -int main(int argc, char *argv[]) { - printf("[User] IOb-nco application\n"); - - if (nco_init()) { - perror("[User] Failed to initialize nco"); - - return EXIT_FAILURE; - } - - if (nco_print_version()) { - perror("[User] Failed to print version"); - - return EXIT_FAILURE; - } - - // read current nco count - uint64_t elapsed = 0; - if (nco_get_count(&elapsed)) { - perror("[User] Failed to get count"); - } - printf("\nExecution time: %lu clock cycles\n", elapsed); - - return EXIT_SUCCESS; -} diff --git a/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.c b/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.c deleted file mode 100644 index d3008cfe1..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob_nco.h" - -// Base Address -static uint32_t base; - -void nco_reset() { - IOB_NCO_SET_SOFTRESET(1); - IOB_NCO_SET_SOFTRESET(0); -} - -void nco_init(uint32_t base_address) { - base = base_address; - IOB_NCO_INIT_BASEADDR(base_address); - nco_reset(); -} - -void nco_enable(bool enable) { IOB_NCO_SET_ENABLE(enable); } - -// Configure NCO output signal period to be 'period'+1 times the system clock -// period. Iob_NCO always assumes +1 clock period implicitly. Lowest 8 bits of -// value are the fractional part of the period by default -void nco_set_period(uint32_t period) { IOB_NCO_SET_PERIOD(period); } diff --git a/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.h b/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.h deleted file mode 100644 index 40e14e59a..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#pragma once -#include "iob_nco_csrs.h" -#include - -// Functions -void nco_reset(); -void nco_init(uint32_t base_address); -void nco_enable(bool enable); -void nco_set_period(uint32_t period); diff --git a/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco_csrs_pc_emul.c b/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco_csrs_pc_emul.c deleted file mode 100644 index 0b597d786..000000000 --- a/lib/hardware/clocks_resets/iob_nco/software/src/iob_nco_csrs_pc_emul.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* PC Emulation of nco peripheral */ - -#include -#include - -#include "bsp.h" -#include "iob_nco_csrs.h" - -/* convert clock values from PC CLOCK FREQ to EMBEDDED FREQ */ -#define PC_TO_FREQ_FACTOR ((1.0 * FREQ) / CLOCKS_PER_SEC) - -static clock_t start, end, time_counter, counter_reg; -static int nco_enable; - -static int base; -void IOB_NCO_INIT_BASEADDR(uint32_t addr) { - base = addr; - return; -} - -void IOB_NCO_SET_SOFTRESET(uint8_t value) { - // use only reg width - int rst_int = (value & 0x01); - if (rst_int) { - start = end = 0; - time_counter = 0; - nco_enable = 0; - } - return; -} - -void IOB_NCO_SET_ENABLE(uint8_t value) { - // use only reg width - int en_int = (value & 0x01); - // manage transitions - // 0 -> 1 - if (nco_enable == 0 && en_int == 1) { - // start counting time - start = clock(); - } else if (nco_enable == 1 && en_int == 0) { - // accumulate enable interval - end = clock(); - nco_enable += (end - start); - start = end = 0; // reset aux clock values - } - // store enable en_int - nco_enable = en_int; - return; -} diff --git a/lib/hardware/clocks_resets/iob_pulse_gen/hardware/simulation/src/iob_pulse_gen_tb.v b/lib/hardware/clocks_resets/iob_pulse_gen/hardware/simulation/src/iob_pulse_gen_tb.v deleted file mode 100644 index 5e04fd8cb..000000000 --- a/lib/hardware/clocks_resets/iob_pulse_gen/hardware/simulation/src/iob_pulse_gen_tb.v +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_pulse_gen_tb; - - localparam START = 2; - localparam DURATION = 10; - - parameter clk_per = 10; // clk period = 10 timeticks - - reg clk; - reg rst; - reg start_i; - wire pulse_o; - - iob_pulse_gen #( - .START (START), - .DURATION(DURATION) - ) - pulse_gen - ( - .clk_i (clk), - .arst_i (rst), - .cke_i (1'b1), - .start_i (start_i), - .pulse_o (pulse_o) - ); - - integer i; - integer fd; - integer duration; - integer start; - initial begin -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - clk = 0; - rst = 1; - start_i = 0; - - duration= 0; - start = 0; - - #clk_per rst = 0; - - #clk_per start_i = 1; - #clk_per start_i = 0; - - @(posedge clk); - // wait for pulse to enable - while(pulse_o == 0) begin - @(posedge clk); - start = start + 1; - end - - while(pulse_o == 1) begin - duration = duration + 1; - @(posedge clk); - end - - if((duration == DURATION) & (start == START)) begin - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - end else begin - $display("Test failed: duration %d\texpected %d", duration, DURATION); - $display("Test failed: start %d\texpected %d", start, START); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test failed: duration %d\texpected %d", duration, DURATION); - $fdisplay(fd, "Test failed: start %d\texpected %d", start, START); - $fclose(fd); - - - end - #(10*clk_per) $finish; - end // initial begin - - always #(clk_per / 2) clk = ~clk; - -endmodule - - diff --git a/lib/hardware/clocks_resets/iob_pulse_gen/iob_pulse_gen.py b/lib/hardware/clocks_resets/iob_pulse_gen/iob_pulse_gen.py deleted file mode 100644 index 76537bbb0..000000000 --- a/lib/hardware/clocks_resets/iob_pulse_gen/iob_pulse_gen.py +++ /dev/null @@ -1,165 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "START", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "", - }, - { - "name": "DURATION", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "", - }, - { - "name": "WIDTH", - "type": "F", - "val": "$clog2(START + DURATION + 2)", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "START_INT", - "type": "F", - "val": "(START <= 0) ? 0 : START - 1", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "FINISH", - "type": "F", - "val": "START_INT + DURATION", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "start_i", - "descr": "Input port", - "signals": [ - {"name": "start", "width": 1, "direction": "input"}, - ], - }, - { - "name": "pulse_o", - "descr": "Output port", - "signals": [ - {"name": "pulse", "width": 1, "direction": "output"}, - ], - }, - ], - "wires": [ - { - "name": "start_detected", - "descr": "Start detect wire", - "signals": [ - {"name": "start_detected", "width": 1}, - ], - }, - { - "name": "start_detected_nxt", - "descr": "Start detect next wire", - "signals": [ - {"name": "start_detected_nxt", "width": 1}, - ], - }, - { - "name": "iob_pulse_gen_int", - "descr": "iob_pulse_gen_int wire", - "signals": [ - {"name": "cnt_en", "width": 1}, - {"name": "start"}, - ], - }, - { - "name": "cnt", - "descr": "", - "signals": [ - {"name": "cnt", "width": "WIDTH"}, - ], - }, - { - "name": "pulse_nxt", - "descr": "", - "signals": [ - {"name": "pulse_nxt", "width": 1}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg", - "instance_name": "start_detected_inst", - "parameters": { - "DATA_W": 1, - "RST_VAL": 0, - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "start_detected_nxt", - "data_o": "start_detected", - }, - }, - { - "core_name": "iob_counter", - "instance_name": "cnt0", - "parameters": { - "DATA_W": "WIDTH", - "RST_VAL": "{WIDTH{1'b0}}", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "en_rst_i": "iob_pulse_gen_int", - "data_o": "cnt", - }, - }, - { - "core_name": "iob_reg", - "instance_name": "pulse_reg", - "parameters": { - "DATA_W": 1, - "RST_VAL": 0, - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "pulse_nxt", - "data_o": "pulse_o", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign start_detected_nxt = start_detected | start_i; - assign cnt_en = start_detected & (cnt <= FINISH); - assign pulse_nxt = cnt_en & (cnt < FINISH) & (cnt >= START_INT); - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/fifo/iob_bfifo/hardware/src/iob_bfifo.v b/lib/hardware/fifo/iob_bfifo/hardware/src/iob_bfifo.v deleted file mode 100644 index d532ba734..000000000 --- a/lib/hardware/fifo/iob_bfifo/hardware/src/iob_bfifo.v +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_bfifo #( - parameter DATA_W = 21 -) ( - `include "iob_bfifo_clk_en_rst_s_port.vs" - - input rst_i, - - input write_i, - input [ $clog2(DATA_W):0] wwidth_i, - input [ DATA_W-1:0] wdata_i, - output [$clog2(2*DATA_W):0] wlevel_o, - - input read_i, - input [ $clog2(DATA_W):0] rwidth_i, - output [ DATA_W-1:0] rdata_o, - output [$clog2(2*DATA_W):0] rlevel_o -); - - `include "iob_functions.vs" - - localparam BUFFER_SIZE = 2 * DATA_W; - //data register - wire [ (2*DATA_W)-1:0] data; - reg [ (2*DATA_W)-1:0] data_nxt; - - //read and write pointers - wire [$clog2(2*DATA_W)-1:0] rptr; //init to 2*DATA_W-1 - wire [$clog2(2*DATA_W)-1:0] wptr; //init to 0 - reg [$clog2(2*DATA_W)-1:0] rptr_nxt; - reg [$clog2(2*DATA_W)-1:0] wptr_nxt; - - //fifo level - wire [ $clog2(2*DATA_W):0] level; - reg [ $clog2(2*DATA_W):0] level_nxt; - - //write data - reg [ DATA_W-1:0] wdata_int; - reg [ (2*DATA_W)-1:0] wdata; - wire [ (2*DATA_W)-1:0] wmask; - wire [ (2*DATA_W)-1:0] rdata; - - //assign outputs - assign wlevel_o = (1'b1 << $clog2(BUFFER_SIZE)) - level; - assign rlevel_o = level; - - //widths' complement - wire [$clog2(DATA_W)-1:0] crwidth; - wire [$clog2(DATA_W)-1:0] cwwidth; - assign crwidth = (~rwidth_i[$clog2(DATA_W)-1:0]) + {{$clog2(DATA_W) - 1{1'd0}}, 1'd1}; - assign cwwidth = (~wwidth_i[$clog2(DATA_W)-1:0]) + {{$clog2(DATA_W) - 1{1'd0}}, 1'd1}; - - //zero trailing bits - assign rdata_o = (rdata[(2*DATA_W)-1-:DATA_W] >> crwidth) << crwidth; - - //write mask shifted - assign wmask = iob_cshift_right(BUFFER_SIZE, ({BUFFER_SIZE{1'b1}} >> wwidth_i), wptr); - //read data shifted - assign rdata = iob_cshift_left(BUFFER_SIZE, data, rptr); - - always @* begin - //write data shifted - wdata_int = (wdata_i >> cwwidth) << cwwidth; - wdata = iob_cshift_right(BUFFER_SIZE, {wdata_int, {DATA_W{1'b0}}}, wptr); - data_nxt = data; - rptr_nxt = rptr; - wptr_nxt = wptr; - level_nxt = level; - if (read_i) begin //read - rptr_nxt = rptr + rwidth_i; - level_nxt = level - rwidth_i; - end else if (write_i) begin //write - data_nxt = (data & wmask) | wdata; - wptr_nxt = wptr + wwidth_i; - level_nxt = level + wwidth_i; - end - end - - //data register - iob_reg_r #( - .DATA_W (BUFFER_SIZE), - .RST_VAL({BUFFER_SIZE{1'b0}}) - ) data_reg_inst ( - `include "iob_bfifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(data_nxt), - .data_o(data) - ); - - //read pointer - iob_reg_r #( - .DATA_W ($clog2(BUFFER_SIZE)), - .RST_VAL({$clog2(BUFFER_SIZE) {1'b0}}) - ) rptr_reg ( - `include "iob_bfifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(rptr_nxt), - .data_o(rptr) - ); - - //write pointer - iob_reg_r #( - .DATA_W ($clog2(BUFFER_SIZE)), - .RST_VAL({$clog2(BUFFER_SIZE) {1'b0}}) - ) wptr_reg ( - `include "iob_bfifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(wptr_nxt), - .data_o(wptr) - ); - - //fifo level - iob_reg_r #( - .DATA_W ($clog2(BUFFER_SIZE) + 1), - .RST_VAL({$clog2(BUFFER_SIZE) + 1{1'b0}}) - ) level_reg ( - `include "iob_bfifo_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(level_nxt), - .data_o(level) - ); - -endmodule - - diff --git a/lib/hardware/fifo/iob_bfifo/iob_bfifo.py b/lib/hardware/fifo/iob_bfifo/iob_bfifo.py deleted file mode 100644 index cdde59020..000000000 --- a/lib/hardware/fifo/iob_bfifo/iob_bfifo.py +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_reg_r", - "instance_name": "iob_reg_r_inst", - }, - # For simulation - { - "core_name": "iob_functions", - "instance_name": "iob_functions_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/fifo/iob_fifo_async/hardware/simulation/src/iob_fifo_async_tb.v b/lib/hardware/fifo/iob_fifo_async/hardware/simulation/src/iob_fifo_async_tb.v deleted file mode 100644 index 6bdbb7adc..000000000 --- a/lib/hardware/fifo/iob_fifo_async/hardware/simulation/src/iob_fifo_async_tb.v +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define IOB_PULSE(VAR, PRE, DURATION, POST) VAR=0; #PRE VAR=1; #DURATION VAR=0; #POST; - -// TODO: re-implement these tests -// $(VLOG) -DW_DATA_W=8 -DR_DATA_W=8 $(wildcard $(BUILD_VSRC_DIR)/*.v) &&\ -// $(VLOG) -DW_DATA_W=32 -DR_DATA_W=8 $(wildcard $(BUILD_VSRC_DIR)/*.v) &&\ -// $(VLOG) -DW_DATA_W=8 -DR_DATA_W=32 $(wildcard $(BUILD_VSRC_DIR)/*.v) &&\ -// $(VLOG) -DW_DATA_W=8 -DR_DATA_W=8 $(wildcard $(BUILD_VSRC_DIR)/*.v) &&\ -// - - -//test defines -`define ADDR_W 4 -`define TESTSIZE 256 //bytes - - -module iob_fifo_async_tb; - - `include "iob_functions.vs" - - localparam TESTSIZE = `TESTSIZE; //bytes - localparam W_DATA_W = 8; - localparam R_DATA_W = 8; - localparam MAXDATA_W = iob_max(W_DATA_W, R_DATA_W); - localparam MINDATA_W = iob_min(W_DATA_W, R_DATA_W); - localparam ADDR_W = `ADDR_W; - localparam R = MAXDATA_W / MINDATA_W; - localparam MINADDR_W = ADDR_W - $clog2(R); //lower ADDR_W (higher DATA_W) - localparam W_ADDR_W = W_DATA_W == MAXDATA_W ? MINADDR_W : ADDR_W; - localparam R_ADDR_W = R_DATA_W == MAXDATA_W ? MINADDR_W : ADDR_W; - - - //global reset - reg arst = 0; - - //write reset - reg w_arst = 0; - always @(posedge w_clk, posedge arst) - if (arst) w_arst = 1; - else w_arst = #1 arst; - - //read reset - reg r_arst = 0; - always @(posedge r_clk, posedge arst) - if (arst) r_arst = 1; - else r_arst = #1 arst; - - //write clock - reg w_clk; - iob_clock #(.CLK_PERIOD(10)) iob_clock_1 (.clk_o(w_clk)); - - //read clock - reg r_clk; - iob_clock #(.CLK_PERIOD(13)) iob_clock_2 (.clk_o(r_clk)); - - reg r_cke = 1; - reg w_cke = 1; - - - //write port - reg w_en = 0; - reg [W_DATA_W-1:0] w_data; - wire w_empty; - wire w_full; - wire [ ADDR_W:0] w_level; - - //read port - reg r_en = 0; - wire [R_DATA_W-1:0] r_data; - wire r_empty; - wire r_full; - wire [ ADDR_W:0] r_level; - - integer i, j; //iterators - integer fd; - - reg [TESTSIZE*8-1:0] test_data; - reg [TESTSIZE*8-1:0] read; - - // - // WRITE PROCESS - // - - initial begin - - if (W_DATA_W > R_DATA_W) $display("W_DATA_W > R_DATA_W"); - else if (W_DATA_W < R_DATA_W) $display("W_DATA_W < R_DATA_W"); - else $display("W_DATA_W = R_DATA_W"); - - $display("W_DATA_W=%d", W_DATA_W); - $display("W_ADDR_W=%d", W_ADDR_W); - $display("R_DATA_W=%d", R_DATA_W); - $display("R_ADDR_W=%d", R_ADDR_W); - - //create the test data bytes - for (i = 0; i < TESTSIZE; i = i + 1) test_data[i*8+:8] = i; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - #10 `IOB_PULSE(arst, 50, 50, 50) - - //fill up the FIFO - @(posedge w_clk) #1; - for (i = 0; i < 2 ** W_ADDR_W; i = i + 1) begin - w_en = 1; - w_data = test_data[i*W_DATA_W+:W_DATA_W]; - @(posedge w_clk) #1; - end - w_en = 0; - - if (w_full != 1) $fatal(1, "ERROR: write proc: w_full=1 expected"); - - if (w_level != 2 ** ADDR_W) - $fatal(1, "ERROR: write proc: expecting w_level = %.0f, got %d", 2 ** ADDR_W, w_level); - - while (!w_empty) @(posedge w_clk) #1; - $display("INFO: write proc: w_empty=1 as expected"); - - //write test data continuously to the FIFO - @(posedge w_clk) #1; - for (i = 0; i < ((TESTSIZE * 8) / W_DATA_W); i = i + 1) begin - while (w_full) @(posedge w_clk) #1; - w_en = 1; - w_data = test_data[i*W_DATA_W+:W_DATA_W]; - @(posedge w_clk) #1; - w_en = 0; - end - end - - // - // READ PROCESS - // - - initial begin - - //wait until fifo is full - while (r_full !== 1'b1) @(posedge r_clk) #1; - $display("INFO: read proc: r_full = 1 as expected"); - - //read all data from full FIFO - @(posedge r_clk) #1; - for (j = 0; j < 2 ** R_ADDR_W; j = j + 1) begin - r_en = 1; - @(posedge r_clk) #1; - read[j*R_DATA_W+:R_DATA_W] = r_data; - r_en = 0; - end - - if (!r_empty) $fatal(1, "ERROR: read proc: r_empty=1 expected"); - - if (r_level != 0) $fatal(1, "ERROR: read proc: expect r_level=0, got r_level=%d", r_level); - - //read data continuously from the FIFO - for (j = 0; j < ((TESTSIZE * 8) / R_DATA_W); j = j + 1) begin - while (r_empty) @(posedge r_clk) #1; - r_en = 1; - @(posedge r_clk) #1; - read[j*R_DATA_W+:R_DATA_W] = r_data; - r_en = 0; - if (r_data != test_data[j*R_DATA_W+:R_DATA_W]) - $fatal( - 1, - "ERROR: read proc: expected r_data=%d, got r_data=%d", - test_data[j*R_DATA_W+:R_DATA_W], - r_data - ); - end - - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #100 $finish(); - end - - wire [ 1-1:0] ext_mem_w_clk; - wire [ R-1:0] ext_mem_w_en; - wire [MINADDR_W-1:0] ext_mem_w_addr; - wire [MAXDATA_W-1:0] ext_mem_w_data; - wire [ 1-1:0] ext_mem_r_clk; - wire [ R-1:0] ext_mem_r_en; - wire [MINADDR_W-1:0] ext_mem_r_addr; - wire [MAXDATA_W-1:0] ext_mem_r_data; - - // FIFO memory - genvar p; - generate - for (p = 0; p < R; p = p + 1) begin - iob_ram_at2p #( - .DATA_W(MINDATA_W), - .ADDR_W(MINADDR_W) - ) iob_ram_at2p ( - .w_clk_i (ext_mem_w_clk), - .w_en_i (ext_mem_w_en[p]), - .w_addr_i(ext_mem_w_addr), - .w_data_i(ext_mem_w_data[p*MINDATA_W+:MINDATA_W]), - - .r_clk_i (ext_mem_r_clk), - .r_en_i (ext_mem_r_en[p]), - .r_addr_i(ext_mem_r_addr), - .r_data_o(ext_mem_r_data[p*MINDATA_W+:MINDATA_W]) - ); - end - endgenerate - - // Instantiate the Unit Under Test (UUT) - iob_fifo_async #( - .W_DATA_W(W_DATA_W), - .R_DATA_W(R_DATA_W), - .ADDR_W (ADDR_W) - ) uut ( - //memory write port - .ext_mem_w_clk_o (ext_mem_w_clk), - .ext_mem_w_en_o (ext_mem_w_en), - .ext_mem_w_addr_o(ext_mem_w_addr), - .ext_mem_w_data_o(ext_mem_w_data), - //memory read port - .ext_mem_r_clk_o (ext_mem_r_clk), - .ext_mem_r_en_o (ext_mem_r_en), - .ext_mem_r_addr_o(ext_mem_r_addr), - .ext_mem_r_data_i(ext_mem_r_data), - //read port - .r_clk_i (r_clk), - .r_arst_i (r_arst), - .r_rst_i (1'd0), - .r_cke_i (r_cke), - .r_en_i (r_en), - .r_data_o (r_data), - .r_empty_o (r_empty), - .r_full_o (r_full), - .r_level_o (r_level), - //write port - .w_clk_i (w_clk), - .w_arst_i (w_arst), - .w_rst_i (1'd0), - .w_cke_i (w_cke), - .w_en_i (w_en), - .w_data_i (w_data), - .w_empty_o (w_empty), - .w_full_o (w_full), - .w_level_o (w_level) - ); - -endmodule diff --git a/lib/hardware/fifo/iob_fifo_async/hardware/src/iob_fifo_async.v b/lib/hardware/fifo/iob_fifo_async/hardware/src/iob_fifo_async.v deleted file mode 100644 index 248596388..000000000 --- a/lib/hardware/fifo/iob_fifo_async/hardware/src/iob_fifo_async.v +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_fifo_async #( - parameter W_DATA_W = 21, - parameter R_DATA_W = 21, - parameter ADDR_W = 3, //higher ADDR_W lower DATA_W - //determine W_ADDR_W and R_ADDR_W - parameter MAXDATA_W = iob_max(W_DATA_W, R_DATA_W), - parameter MINDATA_W = iob_min(W_DATA_W, R_DATA_W), - parameter R = MAXDATA_W / MINDATA_W, - parameter ADDR_W_DIFF = $clog2(R), - parameter MINADDR_W = ADDR_W - $clog2(R), //lower ADDR_W (higher DATA_W) - parameter W_ADDR_W = (W_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W, - parameter R_ADDR_W = (R_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W -) ( - `include "iob_fifo_async_io.vs" -); - - `include "iob_functions.vs" - - localparam [ADDR_W:0] FIFO_SIZE = {1'b1, {ADDR_W{1'b0}}}; //in bytes - - //binary read addresses on both domains - wire [R_ADDR_W:0] r_raddr_bin; - wire [R_ADDR_W:0] w_raddr_bin; - wire [W_ADDR_W:0] r_waddr_bin; - wire [W_ADDR_W:0] w_waddr_bin; - - //normalized binary addresses (for narrower data side) - wire [ ADDR_W:0] r_raddr_bin_n; - wire [ ADDR_W:0] r_waddr_bin_n; - wire [ ADDR_W:0] w_waddr_bin_n; - wire [ ADDR_W:0] w_raddr_bin_n; - - //assign according to assymetry type - localparam [ADDR_W-1:0] W_INCR = (W_DATA_W > R_DATA_W) ? 1'b1 << ADDR_W_DIFF : 1'b1; - localparam [ADDR_W-1:0] R_INCR = (R_DATA_W > W_DATA_W) ? 1'b1 << ADDR_W_DIFF : 1'b1; - - generate - if (W_DATA_W > R_DATA_W) begin : g_write_wider_bin - assign w_waddr_bin_n = w_waddr_bin << ADDR_W_DIFF; - assign w_raddr_bin_n = w_raddr_bin; - assign r_raddr_bin_n = r_raddr_bin; - assign r_waddr_bin_n = r_waddr_bin << ADDR_W_DIFF; - end else if (R_DATA_W > W_DATA_W) begin : g_read_wider_bin - assign w_waddr_bin_n = w_waddr_bin; - assign w_raddr_bin_n = w_raddr_bin << ADDR_W_DIFF; - assign r_raddr_bin_n = r_raddr_bin << ADDR_W_DIFF; - assign r_waddr_bin_n = r_waddr_bin; - end else begin : g_write_equals_read_bin - assign w_raddr_bin_n = w_raddr_bin; - assign w_waddr_bin_n = w_waddr_bin; - assign r_waddr_bin_n = r_waddr_bin; - assign r_raddr_bin_n = r_raddr_bin; - end - endgenerate - - - //sync write gray address to read domain - wire [W_ADDR_W:0] w_waddr_gray; - wire [W_ADDR_W:0] r_waddr_gray; - iob_sync #( - .DATA_W (W_ADDR_W + 1), - .RST_VAL({(W_ADDR_W + 1) {1'd0}}) - ) w_waddr_gray_sync0 ( - .clk_i (r_clk_i), - .arst_i (r_arst_i), - .signal_i(w_waddr_gray), - .signal_o(r_waddr_gray) - ); - - //sync read gray address to write domain - wire [R_ADDR_W:0] r_raddr_gray; - wire [R_ADDR_W:0] w_raddr_gray; - iob_sync #( - .DATA_W (R_ADDR_W + 1), - .RST_VAL({(R_ADDR_W + 1) {1'd0}}) - ) r_raddr_gray_sync0 ( - .clk_i (w_clk_i), - .arst_i (w_arst_i), - .signal_i(r_raddr_gray), - .signal_o(w_raddr_gray) - ); - - - //READ DOMAIN FIFO LEVEL - wire [(ADDR_W+1)-1:0] r_level_int; - assign r_level_int = r_waddr_bin_n - r_raddr_bin_n; - assign r_level_o = r_level_int[0+:(ADDR_W+1)]; - - //READ DOMAIN EMPTY AND FULL FLAGS - assign r_empty_o = (r_level_int < {2'd0, R_INCR}); - assign r_full_o = (r_level_int > (FIFO_SIZE - {2'd0, R_INCR})); - - //WRITE DOMAIN FIFO LEVEL - wire [(ADDR_W+1)-1:0] w_level_int; - assign w_level_int = w_waddr_bin_n - w_raddr_bin_n; - assign w_level_o = w_level_int[0+:(ADDR_W+1)]; - - //WRITE DOMAIN EMPTY AND FULL FLAGS - assign w_empty_o = (w_level_int < {2'd0, W_INCR}); - assign w_full_o = (w_level_int > (FIFO_SIZE - {2'd0, W_INCR})); - - - //read address gray code counter - wire r_en_int = (r_en_i & (~r_empty_o)); - iob_gray_counter #( - .W(R_ADDR_W + 1) - ) r_raddr_gray_counter ( - .clk_i (r_clk_i), - .cke_i (r_cke_i), - .arst_i(r_arst_i), - .rst_i (r_rst_i), - .en_i (r_en_int), - .data_o(r_raddr_gray) - ); - - //write address gray code counter - wire w_en_int = (w_en_i & (~w_full_o)); - iob_gray_counter #( - .W(W_ADDR_W + 1) - ) w_waddr_gray_counter ( - .clk_i (w_clk_i), - .cke_i (w_cke_i), - .arst_i(w_arst_i), - .rst_i (w_rst_i), - .en_i (w_en_int), - .data_o(w_waddr_gray) - ); - - //convert gray read address to binary - iob_gray2bin #( - .DATA_W(R_ADDR_W + 1) - ) gray2bin_r_raddr ( - .gr_i (r_raddr_gray), - .bin_o(r_raddr_bin) - ); - - //convert synced gray write address to binary - iob_gray2bin #( - .DATA_W(W_ADDR_W + 1) - ) gray2bin_r_raddr_sync ( - .gr_i (r_waddr_gray), - .bin_o(r_waddr_bin) - ); - - //convert gray write address to binary - iob_gray2bin #( - .DATA_W(W_ADDR_W + 1) - ) gray2bin_w_waddr ( - .gr_i (w_waddr_gray), - .bin_o(w_waddr_bin) - ); - - //convert synced gray read address to binary - iob_gray2bin #( - .DATA_W(R_ADDR_W + 1) - ) gray2bin_w_raddr_sync ( - .gr_i (w_raddr_gray), - .bin_o(w_raddr_bin) - ); - - wire [W_ADDR_W-1:0] w_addr = w_waddr_bin[W_ADDR_W-1:0]; - wire [R_ADDR_W-1:0] r_addr = r_raddr_bin[R_ADDR_W-1:0]; - - assign ext_mem_w_clk_o = w_clk_i; - assign ext_mem_r_clk_o = r_clk_i; - - iob_asym_converter #( - .W_DATA_W(W_DATA_W), - .R_DATA_W(R_DATA_W), - .ADDR_W (ADDR_W) - ) asym_converter ( - .ext_mem_w_en_o (ext_mem_w_en_o), - .ext_mem_w_addr_o(ext_mem_w_addr_o), - .ext_mem_w_data_o(ext_mem_w_data_o), - .ext_mem_r_en_o (ext_mem_r_en_o), - .ext_mem_r_addr_o(ext_mem_r_addr_o), - .ext_mem_r_data_i(ext_mem_r_data_i), - .clk_i (r_clk_i), - .cke_i (r_cke_i), - .arst_i (r_arst_i), - .rst_i (r_rst_i), - .w_addr_i (w_addr), - .w_en_i (w_en_int), - .w_data_i (w_data_i), - .r_addr_i (r_addr), - .r_en_i (r_en_int), - .r_data_o (r_data_o) - ); - -endmodule diff --git a/lib/hardware/fifo/iob_fifo_async/iob_fifo_async.py b/lib/hardware/fifo/iob_fifo_async/iob_fifo_async.py deleted file mode 100644 index 77c83911c..000000000 --- a/lib/hardware/fifo/iob_fifo_async/iob_fifo_async.py +++ /dev/null @@ -1,295 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "confs": [ - { - "name": "W_DATA_W", - "descr": "", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - }, - { - "name": "R_DATA_W", - "descr": "", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - }, - { - "name": "ADDR_W", - "descr": "Higher ADDR_W lower DATA_W", - "type": "P", - "val": "3", - "min": "NA", - "max": "NA", - }, - { - "name": "MAXDATA_W", - "descr": "", - "type": "F", - "val": "iob_max(W_DATA_W, R_DATA_W)", - "min": "NA", - "max": "NA", - }, - { - "name": "MINDATA_W", - "descr": "", - "type": "F", - "val": "iob_min(W_DATA_W, R_DATA_W)", - "min": "NA", - "max": "NA", - }, - { - "name": "R", - "descr": "", - "type": "F", - "val": "MAXDATA_W / MINDATA_W", - "min": "NA", - "max": "NA", - }, - { - "name": "MINADDR_W", - "descr": "Lower ADDR_W (higher DATA_W)", - "type": "F", - "val": "ADDR_W - $clog2(R)", - "min": "NA", - "max": "NA", - }, - { - "name": "W_ADDR_W", - "descr": "", - "type": "F", - "val": "(W_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W", - "min": "NA", - "max": "NA", - }, - { - "name": "R_ADDR_W", - "descr": "", - "type": "F", - "val": "(R_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W", - "min": "NA", - "max": "NA", - }, - ], - "ports": [ - { - "name": "write", - "descr": "Write interface", - "signals": [ - { - "name": "w_clk", - "direction": "input", - "width": 1, - "descr": "Write clock", - }, - { - "name": "w_cke", - "direction": "input", - "width": 1, - "descr": "Write clock enable", - }, - { - "name": "w_arst", - "direction": "input", - "width": 1, - "descr": "Write async reset", - }, - { - "name": "w_rst", - "direction": "input", - "width": 1, - "descr": "Write sync reset", - }, - { - "name": "w_en", - "direction": "input", - "width": 1, - "descr": "Write enable", - }, - { - "name": "w_data", - "direction": "input", - "width": "W_DATA_W", - "descr": "Write data", - }, - { - "name": "w_full", - "direction": "output", - "width": 1, - "descr": "Write full signal", - }, - { - "name": "w_empty", - "direction": "output", - "width": 1, - "descr": "Write empty signal", - }, - { - "name": "w_level", - "direction": "output", - "width": "ADDR_W+1", - "descr": "Write fifo level", - }, - ], - }, - { - "name": "read", - "descr": "Read interface", - "signals": [ - { - "name": "r_clk", - "direction": "input", - "width": 1, - "descr": "Read clock", - }, - { - "name": "r_cke", - "direction": "input", - "width": 1, - "descr": "Read clock enable", - }, - { - "name": "r_arst", - "direction": "input", - "width": 1, - "descr": "Read async reset", - }, - { - "name": "r_rst", - "direction": "input", - "width": 1, - "descr": "Read sync reset", - }, - { - "name": "r_en", - "direction": "input", - "width": 1, - "descr": "Read enable", - }, - { - "name": "r_data", - "direction": "output", - "width": "R_DATA_W", - "descr": "Read data", - }, - { - "name": "r_full", - "direction": "output", - "width": 1, - "descr": "Read full signal", - }, - { - "name": "r_empty", - "direction": "output", - "width": 1, - "descr": "Read empty signal", - }, - { - "name": "r_level", - "direction": "output", - "width": "ADDR_W+1", - "descr": "Read fifo level", - }, - ], - }, - { - "name": "extmem", - "descr": "External memory interface", - "signals": [ - # Write port - { - "name": "ext_mem_w_clk", - "direction": "output", - "width": 1, - "descr": "Memory clock", - }, - { - "name": "ext_mem_w_en", - "direction": "output", - "width": "R", - "descr": "Memory write enable", - }, - { - "name": "ext_mem_w_addr", - "direction": "output", - "width": "MINADDR_W", - "descr": "Memory write address", - }, - { - "name": "ext_mem_w_data", - "direction": "output", - "width": "MAXDATA_W", - "descr": "Memory write data", - }, - # Read port - { - "name": "ext_mem_r_clk", - "direction": "output", - "width": 1, - "descr": "Memory clock", - }, - { - "name": "ext_mem_r_en", - "direction": "output", - "width": "R", - "descr": "Memory read enable", - }, - { - "name": "ext_mem_r_addr", - "direction": "output", - "width": "MINADDR_W", - "descr": "Memory read address", - }, - { - "name": "ext_mem_r_data", - "direction": "input", - "width": "MAXDATA_W", - "descr": "Memory read data", - }, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_gray_counter", - "instance_name": "iob_gray_counter_inst", - }, - { - "core_name": "iob_gray2bin", - "instance_name": "iob_gray2bin_inst", - }, - { - "core_name": "iob_sync", - "instance_name": "iob_sync_inst", - }, - { - "core_name": "iob_asym_converter", - "instance_name": "iob_asym_converter_inst", - }, - { - "core_name": "iob_functions", - "instance_name": "iob_functions_inst", - }, - # For simulation - { - "core_name": "iob_ram_at2p", - "instance_name": "iob_ram_at2p_inst", - }, - { - "core_name": "iob_clock", - "instance_name": "iob_clock_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/fifo/iob_fifo_sync/hardware/simulation/src/iob_fifo_sync_tb.v b/lib/hardware/fifo/iob_fifo_sync/hardware/simulation/src/iob_fifo_sync_tb.v deleted file mode 100644 index ce421e190..000000000 --- a/lib/hardware/fifo/iob_fifo_sync/hardware/simulation/src/iob_fifo_sync_tb.v +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -/* TODO: re-implement these tests -- $(VLOG) -DW_DATA_W=8 -DR_DATA_W=8 $(wildcard $(BUILD_VSRC_DIR)*.v) &&\ -- $(VLOG) -DW_DATA_W=32 -DR_DATA_W=8 $(wildcard $(BUILD_VSRC_DIR)*.v) &&\ -- $(VLOG) -DW_DATA_W=8 -DR_DATA_W=32 $(wildcard $(BUILD_VSRC_DIR)*.v) &&\ -- $(VLOG) -DW_DATA_W=8 -DR_DATA_W=8 $(wildcard $(BUILD_VSRC_DIR)*.v) &&\ -*/ - -module iob_fifo_sync_tb; - - `include "iob_functions.vs" - - localparam W_DATA_W = 8; - localparam R_DATA_W = 8; - localparam MAXDATA_W = iob_max(W_DATA_W, R_DATA_W); - localparam MINDATA_W = iob_min(W_DATA_W, R_DATA_W); - localparam ADDR_W = 10; - localparam R = MAXDATA_W / MINDATA_W; - localparam MINADDR_W = ADDR_W - $clog2(R); //lower ADDR_W (higher DATA_W) - localparam W_ADDR_W = W_DATA_W == MAXDATA_W ? MINADDR_W : ADDR_W; - localparam R_ADDR_W = R_DATA_W == MAXDATA_W ? MINADDR_W : ADDR_W; - localparam TESTSIZE = (2 ** W_ADDR_W) * R; //MINDATA_W - - reg reset = 0; - reg arst = 0; - reg clk = 0; - reg cke = 1; - - //write port - reg w_en = 0; - reg [W_DATA_W-1:0] w_data; - wire w_full; - - //read port - reg r_en = 0; - wire [R_DATA_W-1:0] r_data; - wire r_empty; - - //FIFO level - wire [ ADDR_W:0] level; - - parameter clk_per = 10; // clk period = 10 timeticks - always #(clk_per / 2) clk = ~clk; - - integer i, j; //iterators - integer fd; - - reg [TESTSIZE*MINDATA_W-1:0] test_data; - reg [TESTSIZE*MINDATA_W-1:0] read; - - //FIFO memory - wire ext_mem_clk; - wire [ R-1:0] ext_mem_w_en; - wire [ MAXDATA_W-1:0] ext_mem_w_data; - wire [ MINADDR_W-1:0] ext_mem_w_addr; - wire [ R-1:0] ext_mem_r_en; - wire [ MINADDR_W-1:0] ext_mem_r_addr; - wire [ MAXDATA_W-1:0] ext_mem_r_data; - - // - //WRITE PROCESS - // - reg w_r_en = 0; //disable reads initially - - initial begin - - if (W_DATA_W > R_DATA_W) $display("W_DATA_W > R_DATA_W"); - else if (W_DATA_W < R_DATA_W) $display("W_DATA_W < R_DATA_W"); - else $display("W_DATA_W = R_DATA_W"); - - $display("W_DATA_W=%d", W_DATA_W); - $display("W_ADDR_W=%d", W_ADDR_W); - $display("R_DATA_W=%d", R_DATA_W); - $display("R_ADDR_W=%d", R_ADDR_W); - - //create the test data - for (i = 0; i < TESTSIZE; i = i + 1) test_data[i*MINDATA_W+:MINDATA_W] = i[0+:MINDATA_W]; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - repeat (4) @(posedge clk) #1; - - - //reset FIFO - #clk_per; - @(posedge clk) #1; - arst = 1; - repeat (4) @(posedge clk) #1; - arst = 0; - - //reset FIFO - #clk_per; - @(posedge clk) #1; - reset = 1; - repeat (4) @(posedge clk) #1; - reset = 0; - - //fill up the FIFO - for (i = 0; i < 2 ** W_ADDR_W; i = i + 1) begin - w_en = 1; - w_data = test_data[i*W_DATA_W+:W_DATA_W]; - @(posedge clk) #1; - end - w_en = 0; - - if (w_full != 1) $fatal(1, "ERROR: write proc: expecting w_full=1"); - $display("INFO: write proc: w_full=1 as expected"); - - if (level != 2 ** ADDR_W) - $fatal( - 1, "ERROR: write proc: expecting level = %.0f, but got level=%d", 2 ** ADDR_W, level - ); - $display("INFO: write proc: level = %.0f as expected", 2 ** ADDR_W); - - //enable reads and wait for empty - w_r_en = 1; - while (!r_empty) @(posedge clk) #1; - $display("INFO: write proc: r_empty=1 as expected"); - - //write test data continuously to the FIFO - for (i = 0; i < ((TESTSIZE * 8) / W_DATA_W); i = i + 1) begin - while (w_full) @(posedge clk) #1; - w_en = 1; - w_data = test_data[i*W_DATA_W+:W_DATA_W]; - @(posedge clk) #1; - w_en = 0; - end - - $display("INFO: write proc: test data written"); - end - - // - // READ PROCESS - // - - initial begin - - //wait for reset to be de-asserted - @(negedge reset) repeat (4) @(posedge clk) #1; - while (!w_r_en) @(posedge clk) #1; - - //wait for FIFO full - while (!w_full) @(posedge clk) #1; - $display("INFO: read proc: w_full=1 as expected"); - - //read data from the entire FIFO - for (j = 0; j < 2 ** R_ADDR_W; j = j + 1) begin - while (r_empty) @(posedge clk) #1; - r_en = 1; - @(posedge clk) #1; - read[j*R_DATA_W+:R_DATA_W] = r_data; - r_en = 0; - end - - while (!r_empty) @(posedge clk) #1; - $display("INFO: read proc: r_empty = 1 as expected"); - - if (level != 0) $fatal(1, "ERROR: read proc: expecting level = 0, but got level=%d", level); - $display("INFO: read proc: level = 0 as expected"); - - //read data continuously from the FIFO - for (j = 0; j < ((TESTSIZE * 8) / R_DATA_W); j = j + 1) begin - while (r_empty) @(posedge clk) #1; - r_en = 1; - @(posedge clk) #1; - read[j*R_DATA_W+:R_DATA_W] = r_data; - r_en = 0; - end - - if (read !== test_data) begin - $display("ERROR: read proc: data read does not match the test data."); - $fatal(1, "read proc: data read XOR test data: %x", read ^ test_data); - end - $display("INFO: read proc: data read matches test data as expected"); - - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - end - - // Instantiate the Unit Under Test (UUT) - iob_fifo_sync #( - .W_DATA_W(W_DATA_W), - .R_DATA_W(R_DATA_W), - .ADDR_W (ADDR_W) - ) uut ( - .clk_i (clk), - .arst_i(arst), - .cke_i (cke), - .rst_i (reset), - - .ext_mem_clk_o (ext_mem_clk), - .ext_mem_w_en_o (ext_mem_w_en), - .ext_mem_w_addr_o(ext_mem_w_addr), - .ext_mem_w_data_o(ext_mem_w_data), - .ext_mem_r_en_o (ext_mem_r_en), - .ext_mem_r_addr_o(ext_mem_r_addr), - .ext_mem_r_data_i(ext_mem_r_data), - - .r_en_i (r_en), - .r_data_o (r_data), - .r_empty_o(r_empty), - - .w_en_i (w_en), - .w_data_i(w_data), - .w_full_o(w_full), - .level_o (level) - ); - - genvar p; - generate - for (p = 0; p < R; p = p + 1) begin - iob_ram_t2p #( - .DATA_W(MINDATA_W), - .ADDR_W(MINADDR_W) - ) iob_ram_t2p_inst ( - .clk_i (ext_mem_clk), - .w_en_i (ext_mem_w_en[p]), - .w_addr_i(ext_mem_w_addr), - .w_data_i(ext_mem_w_data[p*MINDATA_W+:MINDATA_W]), - .r_en_i (ext_mem_r_en[p]), - .r_addr_i(ext_mem_r_addr), - .r_data_o(ext_mem_r_data[p*MINDATA_W+:MINDATA_W]) - ); - end - endgenerate - -endmodule diff --git a/lib/hardware/fifo/iob_fifo_sync/hardware/src/iob_fifo_sync.v b/lib/hardware/fifo/iob_fifo_sync/hardware/src/iob_fifo_sync.v deleted file mode 100644 index c2b858777..000000000 --- a/lib/hardware/fifo/iob_fifo_sync/hardware/src/iob_fifo_sync.v +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_fifo_sync #( - parameter W_DATA_W = 21, - R_DATA_W = 21, - ADDR_W = 21, //higher ADDR_W lower DATA_W - //determine W_ADDR_W and R_ADDR_W - MAXDATA_W = iob_max(W_DATA_W, R_DATA_W), - MINDATA_W = iob_min(W_DATA_W, R_DATA_W), - R = MAXDATA_W / MINDATA_W, - MINADDR_W = ADDR_W - $clog2(R), //lower ADDR_W (higher DATA_W) - W_ADDR_W = (W_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W, - R_ADDR_W = (R_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W -) ( - `include "iob_fifo_sync_io.vs" -); - - `include "iob_functions.vs" - - localparam ADDR_W_DIFF = $clog2(R); - localparam [ADDR_W:0] FIFO_SIZE = {1'b1, {ADDR_W{1'b0}}}; //in bytes - - //effective write enable - wire w_en_int = (w_en_i & (~w_full_o)); - - //write address - wire [W_ADDR_W-1:0] w_addr; - iob_counter #( - .DATA_W (W_ADDR_W), - .RST_VAL({W_ADDR_W{1'd0}}) - ) w_addr_cnt0 ( - `include "iob_fifo_sync_clk_en_rst_s_s_portmap.vs" - - .rst_i (rst_i), - .en_i (w_en_int), - .data_o(w_addr) - ); - - //effective read enable - wire r_en_int = (r_en_i & (~r_empty_o)); - - //read address - wire [R_ADDR_W-1:0] r_addr; - iob_counter #( - .DATA_W (R_ADDR_W), - .RST_VAL({R_ADDR_W{1'd0}}) - ) r_addr_cnt0 ( - `include "iob_fifo_sync_clk_en_rst_s_s_portmap.vs" - - .rst_i (rst_i), - .en_i (r_en_int), - .data_o(r_addr) - ); - - //assign according to assymetry type - localparam [ADDR_W-1:0] W_INCR = (W_DATA_W > R_DATA_W) ? - {{ADDR_W-1{1'd0}},{1'd1}} << ADDR_W_DIFF : {{ADDR_W-1{1'd0}},{1'd1}}; - localparam [ADDR_W-1:0] R_INCR = (R_DATA_W > W_DATA_W) ? - {{ADDR_W-1{1'd0}},{1'd1}} << ADDR_W_DIFF : {{ADDR_W-1{1'd0}},{1'd1}}; - - //FIFO level - reg [ADDR_W:0] level_nxt; - wire [ADDR_W:0] level_int; - iob_reg_r #( - .DATA_W (ADDR_W + 1), - .RST_VAL({(ADDR_W + 1) {1'd0}}) - ) level_reg0 ( - `include "iob_fifo_sync_clk_en_rst_s_s_portmap.vs" - - .rst_i(rst_i), - - .data_i(level_nxt), - .data_o(level_int) - ); - - reg [(ADDR_W+1)-1:0] level_incr; - always @* begin - level_incr = level_int + W_INCR; - level_nxt = level_int; - if (w_en_int && (!r_en_int)) //write only - level_nxt = level_incr; - else if (w_en_int && r_en_int) //write and read - level_nxt = level_incr - R_INCR; - else if (r_en_int) //read only - level_nxt = level_int - R_INCR; - end - - assign level_o = level_int; - - //FIFO empty - wire r_empty_nxt; - assign r_empty_nxt = level_nxt < {1'b0, R_INCR}; - iob_reg_r #( - .DATA_W (1), - .RST_VAL(1'd1) - ) r_empty_reg0 ( - `include "iob_fifo_sync_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(r_empty_nxt), - .data_o(r_empty_o) - ); - - //FIFO full - wire w_full_nxt; - assign w_full_nxt = level_nxt > (FIFO_SIZE - W_INCR); - iob_reg_r #( - .DATA_W (1), - .RST_VAL(1'd0) - ) w_full_reg0 ( - `include "iob_fifo_sync_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(w_full_nxt), - .data_o(w_full_o) - ); - - assign ext_mem_clk_o = clk_i; - - iob_asym_converter #( - .W_DATA_W(W_DATA_W), - .R_DATA_W(R_DATA_W), - .ADDR_W (ADDR_W) - ) asym_converter ( - .ext_mem_w_en_o (ext_mem_w_en_o), - .ext_mem_w_addr_o(ext_mem_w_addr_o), - .ext_mem_w_data_o(ext_mem_w_data_o), - .ext_mem_r_en_o (ext_mem_r_en_o), - .ext_mem_r_addr_o(ext_mem_r_addr_o), - .ext_mem_r_data_i(ext_mem_r_data_i), - `include "iob_fifo_sync_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .w_addr_i (w_addr), - .w_en_i (w_en_int), - .w_data_i (w_data_i), - .r_addr_i (r_addr), - .r_en_i (r_en_int), - .r_data_o (r_data_o) - ); - -endmodule diff --git a/lib/hardware/fifo/iob_fifo_sync/iob_fifo_sync.py b/lib/hardware/fifo/iob_fifo_sync/iob_fifo_sync.py deleted file mode 100644 index 5008739a9..000000000 --- a/lib/hardware/fifo/iob_fifo_sync/iob_fifo_sync.py +++ /dev/null @@ -1,238 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "confs": [ - { - "name": "W_DATA_W", - "descr": "", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - }, - { - "name": "R_DATA_W", - "descr": "", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - }, - { - "name": "ADDR_W", - "descr": "Higher ADDR_W lower DATA_W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - }, - { - "name": "MAXDATA_W", - "descr": "", - "type": "F", - "val": "iob_max(W_DATA_W, R_DATA_W)", - "min": "NA", - "max": "NA", - }, - { - "name": "MINDATA_W", - "descr": "", - "type": "F", - "val": "iob_min(W_DATA_W, R_DATA_W)", - "min": "NA", - "max": "NA", - }, - { - "name": "R", - "descr": "", - "type": "F", - "val": "MAXDATA_W / MINDATA_W", - "min": "NA", - "max": "NA", - }, - { - "name": "MINADDR_W", - "descr": "Lower ADDR_W (higher DATA_W)", - "type": "F", - "val": "ADDR_W - $clog2(R)", - "min": "NA", - "max": "NA", - }, - { - "name": "W_ADDR_W", - "descr": "", - "type": "F", - "val": "(W_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W", - "min": "NA", - "max": "NA", - }, - { - "name": "R_ADDR_W", - "descr": "", - "type": "F", - "val": "(R_DATA_W == MAXDATA_W) ? MINADDR_W : ADDR_W", - "min": "NA", - "max": "NA", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "rst_i", - "descr": "Synchronous reset interface", - "signals": [ - { - "name": "rst", - "direction": "input", - "width": 1, - "descr": "Synchronous reset input", - }, - ], - }, - { - "name": "write", - "descr": "Write interface", - "signals": [ - { - "name": "w_en", - "direction": "input", - "width": 1, - "descr": "Write enable", - }, - { - "name": "w_data", - "direction": "input", - "width": "W_DATA_W", - "descr": "Write data", - }, - { - "name": "w_full", - "direction": "output", - "width": 1, - "descr": "Write full signal", - }, - ], - }, - { - "name": "read", - "descr": "Read interface", - "signals": [ - { - "name": "r_en", - "direction": "input", - "width": 1, - "descr": "Read enable", - }, - { - "name": "r_data", - "direction": "output", - "width": "R_DATA_W", - "descr": "Read data", - }, - { - "name": "r_empty", - "direction": "output", - "width": 1, - "descr": "Read empty signal", - }, - ], - }, - { - "name": "extmem", - "descr": "External memory interface", - "signals": [ - {"name": "ext_mem_clk", "direction": "output", "width": 1}, - { - "name": "ext_mem_w_en", - "direction": "output", - "width": "R", - "descr": "Memory write enable", - }, - { - "name": "ext_mem_w_addr", - "direction": "output", - "width": "MINADDR_W", - "descr": "Memory write address", - }, - { - "name": "ext_mem_w_data", - "direction": "output", - "width": "MAXDATA_W", - "descr": "Memory write data", - }, - # Read port - { - "name": "ext_mem_r_en", - "direction": "output", - "width": "R", - "descr": "Memory read enable", - }, - { - "name": "ext_mem_r_addr", - "direction": "output", - "width": "MINADDR_W", - "descr": "Memory read address", - }, - { - "name": "ext_mem_r_data", - "direction": "input", - "width": "MAXDATA_W", - "descr": "Memory read data", - }, - ], - }, - { - "name": "fifo_o", - "descr": "FIFO interface", - "signals": [ - { - "name": "level", - "direction": "output", - "width": "ADDR_W+1", - "descr": "FIFO level", - }, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg_r", - "instance_name": "iob_reg_r_inst", - }, - { - "core_name": "iob_reg", - "instance_name": "iob_reg_inst", - }, - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - }, - { - "core_name": "iob_asym_converter", - "instance_name": "iob_asym_converter_inst", - }, - { - "core_name": "iob_ram_t2p", - "instance_name": "iob_ram_t2p_inst", - }, - { - "core_name": "iob_functions", - "instance_name": "iob_functions_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/fifo/iob_gray2bin/hardware/src/iob_gray2bin.v b/lib/hardware/fifo/iob_gray2bin/hardware/src/iob_gray2bin.v deleted file mode 100644 index 786cfe303..000000000 --- a/lib/hardware/fifo/iob_gray2bin/hardware/src/iob_gray2bin.v +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -// Convert gray encoding to binary -module iob_gray2bin #( - parameter DATA_W = 4 -) ( - input [DATA_W-1:0] gr_i, - output [DATA_W-1:0] bin_o -); - - genvar pos; - - generate - for (pos = 0; pos < DATA_W; pos = pos + 1) begin : gen_bin - assign bin_o[pos] = ^gr_i[DATA_W-1:pos]; - end - endgenerate - -endmodule diff --git a/lib/hardware/fifo/iob_gray2bin/iob_gray2bin.py b/lib/hardware/fifo/iob_gray2bin/iob_gray2bin.py deleted file mode 100644 index 39fbb46d3..000000000 --- a/lib/hardware/fifo/iob_gray2bin/iob_gray2bin.py +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - } - - return attributes_dict diff --git a/lib/hardware/fifo/iob_gray_counter/hardware/src/iob_gray_counter.v b/lib/hardware/fifo/iob_gray_counter/hardware/src/iob_gray_counter.v deleted file mode 100644 index 85884738a..000000000 --- a/lib/hardware/fifo/iob_gray_counter/hardware/src/iob_gray_counter.v +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_gray_counter #( - parameter W = 1 -) ( - `include "iob_gray_counter_clk_en_rst_s_port.vs" - - input rst_i, - input en_i, - - output [W-1:0] data_o -); - - wire [W-1:0] bin_counter; - wire [W-1:0] bin_counter_nxt; - wire [W-1:0] gray_counter; - wire [W-1:0] gray_counter_nxt; - - assign bin_counter_nxt = bin_counter + 1'b1; - - generate - if (W > 1) begin : g_width_gt1 - assign gray_counter_nxt = {bin_counter[W-1], bin_counter[W-2:0] ^ bin_counter[W-1:1]}; - end else begin : g_width_eq1 - assign gray_counter_nxt = bin_counter; - end - endgenerate - - iob_reg_re #( - .DATA_W (W), - .RST_VAL({{(W - 1) {1'd0}}, 1'd1}) - ) bin_counter_reg ( - `include "iob_gray_counter_clk_en_rst_s_s_portmap.vs" - - .rst_i(rst_i), - .en_i (en_i), - - .data_i(bin_counter_nxt), - .data_o(bin_counter) - ); - - iob_reg_re #( - .DATA_W (W), - .RST_VAL({W{1'd0}}) - ) gray_counter_reg ( - `include "iob_gray_counter_clk_en_rst_s_s_portmap.vs" - - .rst_i(rst_i), - .en_i (en_i), - - .data_i(gray_counter_nxt), - .data_o(gray_counter) - ); - - assign data_o = gray_counter; - -endmodule diff --git a/lib/hardware/fifo/iob_gray_counter/iob_gray_counter.py b/lib/hardware/fifo/iob_gray_counter/iob_gray_counter.py deleted file mode 100644 index 5c26cd2b0..000000000 --- a/lib/hardware/fifo/iob_gray_counter/iob_gray_counter.py +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - # TODO: Remaining ports - ], - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/iob_gpio/Makefile b/lib/hardware/iob_gpio/Makefile deleted file mode 100644 index 708ea0c2f..000000000 --- a/lib/hardware/iob_gpio/Makefile +++ /dev/null @@ -1,64 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# -# TOP MAKEFILE -# - -GPIO_DIR:=. -include core.mk - -# -# SIMULATE -# - -sim: -ifeq ($(SIM_SERVER),) - make -C $(SIM_DIR) run SIMULATOR=$(SIMULATOR) -else - ssh $(SIM_USER)@$(SIM_SERVER) "if [ ! -d $(REMOTE_ROOT_DIR) ]; then mkdir -p $(REMOTE_ROOT_DIR); fi" - make -C $(SIM_DIR) clean - rsync -avz --delete --exclude .git $(GPIO_DIR) $(SIM_USER)@$(SIM_SERVER):$(USER)/$(REMOTE_ROOT_DIR) - ssh $(SIM_USER)@$(SIM_SERVER) 'cd $(REMOTE_ROOT_DIR); make -C $(SIM_DIR) run SIMULATOR=$(SIMULATOR) SIM_SERVER=localhost' -endif - -sim-waves: - gtkwave $(SIM_DIR)/*.vcd & - -sim-clean: - make -C $(SIM_DIR) clean -ifneq ($(SIM_SERVER),) - rsync -avz --delete --exclude .git $(GPIO_DIR) $(SIM_USER)@$(SIM_SERVER):$(USER)/$(REMOTE_ROOT_DIR) - ssh $(SIM_USER)@$(SIM_SERVER) "if [ -d $(REMOTE_ROOT_DIR) ]; then cd $(REMOTE_ROOT_DIR); make -C $(SIM_DIR) clean; fi" -endif - -# -# FPGA COMPILE -# - -fpga: -ifeq ($(FPGA_SERVER),) - make -C $(FPGA_DIR) run DATA_W=$(DATA_W) -else - ssh $(FPGA_USER)@$(FPGA_SERVER) "if [ ! -d $(REMOTE_ROOT_DIR) ]; then mkdir -p $(REMOTE_ROOT_DIR); fi" - rsync -avz --delete --exclude .git $(GPIO_DIR) $(FPGA_USER)@$(FPGA_SERVER):$(REMOTE_ROOT_DIR) - ssh $(FPGA_USER)@$(FPGA_SERVER) 'cd $(REMOTE_ROOT_DIR); make -C $(FPGA_DIR) run FPGA_FAMILY=$(FPGA_FAMILY) FPGA_SERVER=localhost' - mkdir -p $(FPGA_DIR)/$(FPGA_FAMILY) - scp $(FPGA_USER)@$(FPGA_SERVER):$(REMOTE_ROOT_DIR)/$(FPGA_DIR)/$(FPGA_FAMILY)/$(FPGA_LOG) $(FPGA_DIR)/$(FPGA_FAMILY) -endif - -fpga-clean: - make -C $(FPGA_DIR) clean -ifneq ($(FPGA_SERVER),) - rsync -avz --delete --exclude .git $(GPIO_DIR) $(FPGA_USER)@$(FPGA_SERVER):$(REMOTE_ROOT_DIR) - ssh $(FPGA_USER)@$(FPGA_SERVER) "if [ -d $(REMOTE_ROOT_DIR) ]; then cd $(REMOTE_ROOT_DIR); make -C $(FPGA_DIR) clean; fi" -endif - - -# CLEAN ALL -clean-all: sim-clean fpga-clean - -.PHONY: sim sim-waves sim-clean \ - fpga fpga-clean \ - clean-all diff --git a/lib/hardware/iob_gpio/README.md b/lib/hardware/iob_gpio/README.md deleted file mode 100644 index c491925d6..000000000 --- a/lib/hardware/iob_gpio/README.md +++ /dev/null @@ -1,63 +0,0 @@ - - -# README # - -# iob-gpio - -## What is this repository for? ## - -The IObundle GPIO is a RISC-V-based Peripheral written in Verilog, which users can download, modify, simulate and implement in FPGA or ASIC. -This peripheral provides a General Purpose Input/Output interface with up to 32 inputs and 32 outputs. -The tri-state output logic is supported via external tri-state buffers using the output enable interface of this peripheral. - -This peripheral can be used as a verification tool of the [OpenCryptoTester](https://nlnet.nl/project/OpenCryptoTester#ack) project. - -## Integrate in SoC ## - -* Check out [IOb-SoC-SUT](https://github.com/IObundle/iob-soc-sut) - -## Usage - -This peripheral has three 32-bit registers, one for each interface it provides. -It has three interfaces with up to 32 ports: -- The `input_ports` input contains a set of ports, whose value can be read via `gpio_get()` function. This function returns a 32-bit value, where each bit corresponds to the value of the corresponding input port. -- The `output_ports` output contains a set of ports, whose value can be set via `gpio_set(value)` function. This function sets a 32-bit value, where each bit corresponds to the value of the corresponding output port. -- The `output_enable` output contains a set of ports, whose value can be set via `gpio_set_output_enable(value)` function. This function sets a 32-bit value, where each bit corresponds to the value of the corresponding output port. - -The `output_enable` interface is used to trigger external tri-state buffers for the `output_ports`. - -The number of ports is configurable via the `GPIO_W` Verilog parameter. If the `GPIO_W` parameter is less than 32, the most significant bits of the functions above will be ignored, as they will not match an existing port. - - -To instantiate the peripheral, add a dictionary describing the peripheral in the `peripherals` list of the `blocks` dictionary in the setup Python module of the system. - -The `iob_soc_sut_setup.py` script of the [IOb-SoC-SUT](https://github.com/IObundle/iob-soc-sut) system, uses the following dictionary to instantiate a GPIO peripheral with the instance name `GPIO0`: -```Python -blocks = \ -[ - # Other blocks here... - - {'name':'peripherals', 'descr':'peripheral modules', 'blocks': [ - {'name':'GPIO0', 'type':'GPIO', 'descr':'GPIO interface', 'params':{}}, - - # Other peripheral instances here... - ]}, -] -``` - -# Acknowledgement -The [OpenCryptoTester](https://nlnet.nl/project/OpenCryptoTester#ack) project is funded through the NGI Assure Fund, a fund established by NLnet -with financial support from the European Commission's Next Generation Internet -programme, under the aegis of DG Communications Networks, Content and Technology -under grant agreement No 957073. - - - - - - -
NLnet foundation logoNGI Assure logo
diff --git a/lib/hardware/iob_gpio/hardware/fpga/fpga.mk b/lib/hardware/iob_gpio/hardware/fpga/fpga.mk deleted file mode 100644 index 9768a1168..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/fpga.mk +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -GPIO_DIR:=../../.. -include $(GPIO_DIR)/hardware/hardware.mk - -FPGA_VSRC=$(addprefix ../, $(VSRC) ) -FPGA_VHDR=$(addprefix ../, $(VHDR) ) -FPGA_INCLUDE=$(addprefix ../, $(INCLUDE) ) - -$(FPGA_OBJ): $(CONSTRAINTS) $(VSRC) $(VHDR) - mkdir -p $(FPGA_FAMILY) - cd $(FPGA_FAMILY); ../build.sh "$(TOP_MODULE)" "$(FPGA_PART)" "$(FPGA_VSRC)" "$(FPGA_INCLUDE)" "$(DEFINE)" diff --git a/lib/hardware/iob_gpio/hardware/fpga/quartus/Makefile b/lib/hardware/iob_gpio/hardware/fpga/quartus/Makefile deleted file mode 100644 index c4b29a48d..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/quartus/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -FPGA_OBJ?=$(FPGA_FAMILY)/$(TOP_MODULE)_0.qxp -CONSTRAINTS:=$(wildcard *.sdc) - -include ../fpga.mk - -run: $(FPGA_OBJ) - if [ -f $(FPGA_FAMILY)/output_files/*.fit.summary ]; \ - then mv $(FPGA_FAMILY)/output_files/*.fit.summary $(FPGA_FAMILY)/quartus.log; fi - -clean: gpio_clean_hw - rm -rf $(FPGA_FAMILY) *.v *.vh - -.PHONY: clean diff --git a/lib/hardware/iob_gpio/hardware/fpga/quartus/build.sh b/lib/hardware/iob_gpio/hardware/fpga/quartus/build.sh deleted file mode 100755 index a3c73b1e5..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/quartus/build.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/bash - -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -export ALTERAPATH=/home/iobundle/Intel/Altera_full/18.0 -export LM_LICENSE_FILE=1801@localhost:$ALTERAPATH/../1-MVXX5H_License.dat -nios=/home/iobundle/Intel/Altera_full/18.0/nios2eds/nios2_command_shell.sh - -$nios quartus_sh -t ../$1.tcl "$1" "$2" "$3" "$4" "$5" -$nios quartus_map --read_settings_files=on --write_settings_files=off $1 -c $1 -$nios quartus_fit --read_settings_files=off --write_settings_files=off $1 -c $1 -$nios quartus_cdb --read_settings_files=off --write_settings_files=off $1 -c $1 --merge=on -$nios quartus_cdb $1 -c $1 --incremental_compilation_export=iob_timer_0.qxp --incremental_compilation_export_partition_name=Top --incremental_compilation_export_post_synth=on --incremental_compilation_export_post_fit=off --incremental_compilation_export_routing=on --incremental_compilation_export_flatten=on - diff --git a/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.sdc b/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.sdc deleted file mode 100644 index f2112dde0..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.sdc +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -create_clock -name "clk" -add -period 10.0 [get_ports clk] -derive_clock_uncertainty diff --git a/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.tcl b/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.tcl deleted file mode 100644 index 194f72e80..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.tcl +++ /dev/null @@ -1,73 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# -# Synthesis and implementation script -# - -set QUARTUS_VERSION "18.0.0 Standard Edition" -set FAMILY "Cyclone V" - -set TOP [lindex $argv 0] -set DEVICE [lindex $argv 1] -set VSRC [lindex $argv 2] -set HW_INCLUDE [lindex $argv 3] -set HW_DEFINE [lindex $argv 4] - -project_new $TOP -overwrite - -set_global_assignment -name FAMILY $FAMILY -set_global_assignment -name DEVICE $DEVICE -set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files -set_global_assignment -name TOP_LEVEL_ENTITY $TOP -set_global_assignment -name VERILOG_INPUT_VERSION SYSTEMVERILOG_2005 - -#set_global_assignment -name ORIGINAL_QUARTUS_VERSION 18.0.0 -#set_global_assignment -name PROJECT_CREATION_TIME_DATE "15:59:11 JANUARY 21, 2019" - -#set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 256 - -#file search paths -foreach path [split $HW_INCLUDE \ ] { - if {$path != ""} { - set_global_assignment -name SEARCH_PATH $path - } -} - -#verilog macros -foreach macro [split $HW_DEFINE \ ] { - if {$macro != ""} { - set_global_assignment -name VERILOG_MACRO $macro - } -} - -#verilog sources -foreach file [split $VSRC \ ] { - if {$file != ""} { - set_global_assignment -name VERILOG_FILE $file - } -} - - -set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top -set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top -set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top - - -set_global_assignment -name PARTITION_NETLIST_TYPE POST_SYNTH -section_id $TOP":"$TOP"_0" - -set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id $TOP":"$TOP"_0" - -set_global_assignment -name PARTITION_COLOR 39423 -section_id $TOP":"$TOP"_0" - -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top - -set_global_assignment -name LAST_QUARTUS_VERSION $QUARTUS_VERSION -set_global_assignment -name SDC_FILE ../$TOP.sdc -set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 -set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 -set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" -set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" - -project_close diff --git a/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.xdc b/lib/hardware/iob_gpio/hardware/fpga/quartus/gpio.xdc deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/hardware/iob_gpio/hardware/fpga/vivado/Makefile b/lib/hardware/iob_gpio/hardware/fpga/vivado/Makefile deleted file mode 100644 index 9bbc17de0..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/vivado/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -FPGA_OBJ?=$(FPGA_FAMILY)/$(TOP_MODULE).edif -CONSTRAINTS:=$(wildcard *.xdc) - -include ../fpga.mk - -run: $(FPGA_OBJ) - -clean: gpio_clean_hw - rm -rf $(FPGA_FAMILY) *.v *.vh - -.PHONY: run clean diff --git a/lib/hardware/iob_gpio/hardware/fpga/vivado/build.sh b/lib/hardware/iob_gpio/hardware/fpga/vivado/build.sh deleted file mode 100755 index b9464febe..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/vivado/build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/bash - -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -export XILINXPATH=/opt/Xilinx -export LM_LICENSE_FILE=$LM_LICENSE_FILE:$XILINXPATH/Xilinx.lic -source /opt/Xilinx/Vivado/settings64.sh -vivado -nojournal -log vivado.log -mode batch -source ../$1.tcl -tclargs "$1" "$2" "$3" "$4" "$5" diff --git a/lib/hardware/iob_gpio/hardware/fpga/vivado/gpio.tcl b/lib/hardware/iob_gpio/hardware/fpga/vivado/gpio.tcl deleted file mode 100644 index cff21b313..000000000 --- a/lib/hardware/iob_gpio/hardware/fpga/vivado/gpio.tcl +++ /dev/null @@ -1,38 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -set TOP [lindex $argv 0] -set PART [lindex $argv 1] -set VSRC [lindex $argv 2] -set HW_INCLUDE [lindex $argv 3] -set HW_DEFINE [lindex $argv 4] - -puts $VSRC - -#verilog sources -foreach file [split $VSRC \ ] { - puts $file - if {$file != "" && $file != " " && $file != "\n"} { - read_verilog -sv $file - } -} - -set_property part $PART [current_project] - -synth_design -include_dirs $HW_INCLUDE -verilog_define $HW_DEFINE -part $PART -top $TOP -mode out_of_context -flatten_hierarchy none -verbose - -read_xdc ../$TOP.xdc - -opt_design -place_design -route_design - -report_utilization -report_timing -report_clocks - -write_edif -force $TOP.edif -set TOP_STUB $TOP -append TOP_STUB "_stub" -write_verilog -force -mode synth_stub $TOP_STUB.v diff --git a/lib/hardware/iob_gpio/hardware/fpga/vivado/gpio.xdc b/lib/hardware/iob_gpio/hardware/fpga/vivado/gpio.xdc deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/hardware/iob_gpio/hardware/include/inst.vh b/lib/hardware/iob_gpio/hardware/include/inst.vh deleted file mode 100644 index 1425b7b91..000000000 --- a/lib/hardware/iob_gpio/hardware/include/inst.vh +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - - // - // GPIO - // - - iob_gpio gpio0 - ( - .clk (clk), - .rst (rst), - - // Registers interface - .gpio_input (gpio_input), - .gpio_output (gpio_output), - .gpio_output_enable (gpio_output_enable), - - // CPU interface - .valid (slaves_req[`valid(`GPIO)]), - .address (slaves_req[`address(`GPIO,`iob_gpio_csrs_ADDR_W+2)-2]), - .wdata (slaves_req[`wdata(`GPIO)]), - .wstrb (slaves_req[`wstrb(`GPIO)]), - .rdata (slaves_resp[`rdata(`GPIO)]), - .ready (slaves_resp[`ready(`GPIO)]) - ); diff --git a/lib/hardware/iob_gpio/hardware/include/pio.vh b/lib/hardware/iob_gpio/hardware/include/pio.vh deleted file mode 100644 index cfc479086..000000000 --- a/lib/hardware/iob_gpio/hardware/include/pio.vh +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - - // GPIO - input [`GPIO_INPUT_W-1:0] gpio_input, - output [`GPIO_OUTPUT_W-1:0] gpio_output, - output [`GPIO_OUTPUT_W-1:0] gpio_output_enable, diff --git a/lib/hardware/iob_gpio/hardware/simulation/icarus/Makefile b/lib/hardware/iob_gpio/hardware/simulation/icarus/Makefile deleted file mode 100644 index f3ae59504..000000000 --- a/lib/hardware/iob_gpio/hardware/simulation/icarus/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -GPIO_DIR:=../../.. - -incdir:=-I -defmacro:=-D - -include ../simulation.mk - -#icarus verilog simulator -VLOG:=iverilog -W all -g2005-sv - -run: a.out - ./$< - -a.out: $(VSRC) $(VHDR) - $(VLOG) $(INCLUDE) $(DEFINE) $(VSRC) - -clean: - @rm -f a.out *~ *.vcd - -.PHONY: run clean diff --git a/lib/hardware/iob_gpio/hardware/simulation/simulation.mk b/lib/hardware/iob_gpio/hardware/simulation/simulation.mk deleted file mode 100644 index fa7ba28e0..000000000 --- a/lib/hardware/iob_gpio/hardware/simulation/simulation.mk +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -include $(GPIO_DIR)/hardware/hardware.mk - -DEFINE+=$(defmacro)VCD - -VSRC+=$(wildcard $(GPIO_HW_DIR)/testbench/*.v) diff --git a/lib/hardware/iob_gpio/iob_gpio.py b/lib/hardware/iob_gpio/iob_gpio.py deleted file mode 100755 index aac72bd3f..000000000 --- a/lib/hardware/iob_gpio/iob_gpio.py +++ /dev/null @@ -1,213 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - NAME = py_params_dict["name"] if "name" in py_params_dict else "iob_gpio" - N_INPUTS = int(py_params_dict["n_inputs"]) if "n_inputs" in py_params_dict else 1 - N_OUTPUTS = int(py_params_dict["n_outputs"]) if "n_outputs" in py_params_dict else 1 - # Create a dedicated output for tristate (output enable) of each output port - TRISTATE = ( - bool(py_params_dict["tristate"]) if "tristate" in py_params_dict else False - ) - - attributes_dict = { - "name": NAME, - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "32", - "descr": "Data bus width", - }, - { - "name": "ADDR_W", - "type": "P", - # "val": "`IOB_GPIO_CSRS_ADDR_W", - "val": "4", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "INPUT_GPIO_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "DATA_W", - "descr": "Width of GPIO input ports", - }, - { - "name": "OUTPUT_GPIO_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "DATA_W", - "descr": "Width of GPIO output ports", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "iob_s", - "interface": { - "type": "iob", - "subtype": "slave", - }, - "descr": "CPU native interface", - }, - ], - } - for idx in range(N_INPUTS): - attributes_dict["ports"].append( - { - "name": "input_" + str(idx) + "_i", - "descr": "", - "signals": [ - { - "name": "input_port_" + str(idx), - "direction": "input", - "width": "INPUT_GPIO_W", - "descr": "Input interface " + str(idx), - }, - ], - } - ) - for idx in range(N_OUTPUTS): - attributes_dict["ports"].append( - { - "name": "output_" + str(idx) + "_o", - "descr": "", - "signals": [ - { - "name": "output_port_" + str(idx), - "direction": "output", - "width": "OUTPUT_GPIO_W", - "descr": "Output interface " + str(idx), - }, - ], - } - ) - if TRISTATE: - attributes_dict["ports"][-1]["signals"].append( - { - "name": "output_enable_" + str(idx) + "_o", - "direction": "output", - "width": "OUTPUT_GPIO_W", - "descr": f"Output Enable interface bits can be used to tristate output {idx} on external module", - }, - ) - attributes_dict["wires"] = [ - { - "name": "csrs_iob", - "descr": "Internal iob interface", - "interface": { - "type": "iob", - "wire_prefix": "csrs_", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - ] - regs = [] - reg_connections = {} - # Create regs and reg connections for each input - for idx in range(N_INPUTS): - # Create Reg - regs.append( - { - "name": f"input_{idx}", - "type": "R", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Value of GPIO input port " + str(idx), - } - ) - # Connect reg to port - reg_connections[f"input_{idx}"] = f"input_{idx}" - # Create wires, regs, and reg connections for each output - for idx in range(N_OUTPUTS): - # Wires for reg connections (references to port signals) - attributes_dict["wires"].append( - { - "name": "output_port_" + str(idx), - "descr": "", - "signals": [ - {"name": "output_port_" + str(idx)}, - ], - } - ) - if TRISTATE: - attributes_dict["wires"].append( - { - "name": "output_enable_" + str(idx), - "descr": "", - "signals": [ - {"name": "output_enable_" + str(idx)}, - ], - } - ) - # Create Regs - regs.append( - { - "name": f"output_{idx}", - "type": "W", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Value of GPIO output port " + str(idx), - } - ) - if TRISTATE: - regs.append( - { - "name": f"output_enable_{idx}", - "type": "W", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": f'32 bits: 1 bit for each bit in GPIO output {idx}. Bits with "1" are driven with output value, bits with "0" are in tristate.', - } - ) - # Connect regs to wires - reg_connections[f"output_{idx}"] = f"output_port_{idx}" - if TRISTATE: - reg_connections[f"output_enable_{idx}"] = f"output_enable_{idx}" - - attributes_dict["blocks"] = [ - { - "core_name": "csrs", - "instance_name": "csrs_inst", - "instance_description": "Control/Status Registers", - "csrs": [ - { - "name": "gpio", - "descr": "GPIO software accessible registers.", - "regs": regs, - } - ], - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "iob_s", - "csrs_iob_o": "csrs_iob", - **reg_connections, - }, - }, - ] - - return attributes_dict diff --git a/lib/hardware/iob_gpio/software/linux/README.md b/lib/hardware/iob_gpio/software/linux/README.md deleted file mode 100644 index e0b978e2c..000000000 --- a/lib/hardware/iob_gpio/software/linux/README.md +++ /dev/null @@ -1,24 +0,0 @@ - - -# IOb GPIO Linux Kernel Drivers -- Structure: - - `drivers/`: directory with linux kernel module drivers for iob_gpio - - `iob_gpio_main.c`: driver source - - `[iob_gpio.h]` and `[iob_gpio_sysfs.h]`: header files generated by: - ```bash - python3 .path/to/iob-linux/scripts/drivers.py iob_gpio -o [output_dir] - ``` - - `driver.mk`: makefile segment with `iob_gpio-obj:` target for driver - compilation - - `user/`: directory with user application example that uses iob_gpio - drivers - - `iob_gpio_user.c`: example user application that uses iob_gpio - drivers - - `Makefile`: user application compilation targets - - `iob_gpio.dts`: device tree template with iob_gpio node - - manually add the `gpio` node to the system device tree so the - iob_gpio is recognized by the linux kernel diff --git a/lib/hardware/iob_gpio/software/linux/drivers/driver.mk b/lib/hardware/iob_gpio/software/linux/drivers/driver.mk deleted file mode 100644 index 15f32bea1..000000000 --- a/lib/hardware/iob_gpio/software/linux/drivers/driver.mk +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -iob_gpio-objs := iob_gpio_main.o iob_class/iob_class_utils.o diff --git a/lib/hardware/iob_gpio/software/linux/drivers/iob_gpio_main.c b/lib/hardware/iob_gpio/software/linux/drivers/iob_gpio_main.c deleted file mode 100644 index aeb5e780c..000000000 --- a/lib/hardware/iob_gpio/software/linux/drivers/iob_gpio_main.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* iob_gpio_main.c: driver for iob_gpio - * using device platform. No hardcoded hardware address: - * 1. load driver: insmod iob_gpio.ko - * 2. run user app: ./user/user - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iob_class/iob_class_utils.h" -#include "iob_gpio.h" - -static int iob_gpio_probe(struct platform_device *); -static int iob_gpio_remove(struct platform_device *); - -static ssize_t iob_gpio_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t iob_gpio_write(struct file *, const char __user *, size_t, - loff_t *); -static loff_t iob_gpio_llseek(struct file *, loff_t, int); -static int iob_gpio_open(struct inode *, struct file *); -static int iob_gpio_release(struct inode *, struct file *); - -struct iob_data iob_gpio_data = {0}; -DEFINE_MUTEX(iob_gpio_mutex); - -#include "iob_gpio_sysfs.h" - -static const struct file_operations iob_gpio_fops = { - .owner = THIS_MODULE, - .write = iob_gpio_write, - .read = iob_gpio_read, - .llseek = iob_gpio_llseek, - .open = iob_gpio_open, - .release = iob_gpio_release, -}; - -static const struct of_device_id of_iob_gpio_match[] = { - {.compatible = "iobundle,gpio0"}, - {}, -}; - -static struct platform_driver iob_gpio_driver = { - .driver = - { - .name = "iob_gpio", - .owner = THIS_MODULE, - .of_match_table = of_iob_gpio_match, - }, - .probe = iob_gpio_probe, - .remove = iob_gpio_remove, -}; - -// -// Module init and exit functions -// -static int iob_gpio_probe(struct platform_device *pdev) { - struct resource *res; - int result = 0; - - if (iob_gpio_data.device != NULL) { - pr_err("[Driver] %s: No more devices allowed!\n", IOB_GPIO_DRIVER_NAME); - - return -ENODEV; - } - - pr_info("[Driver] %s: probing.\n", IOB_GPIO_DRIVER_NAME); - - // Get the I/O region base address - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - pr_err("[Driver]: Failed to get I/O resource!\n"); - result = -ENODEV; - goto r_get_resource; - } - - // Request and map the I/O region - iob_gpio_data.regbase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(iob_gpio_data.regbase)) { - result = PTR_ERR(iob_gpio_data.regbase); - goto r_ioremmap; - } - iob_gpio_data.regsize = resource_size(res); - - // Alocate char device - result = - alloc_chrdev_region(&iob_gpio_data.devnum, 0, 1, IOB_GPIO_DRIVER_NAME); - if (result) { - pr_err("%s: Failed to allocate device number!\n", IOB_GPIO_DRIVER_NAME); - goto r_alloc_region; - } - - cdev_init(&iob_gpio_data.cdev, &iob_gpio_fops); - - result = cdev_add(&iob_gpio_data.cdev, iob_gpio_data.devnum, 1); - if (result) { - pr_err("%s: Char device registration failed!\n", IOB_GPIO_DRIVER_NAME); - goto r_cdev_add; - } - - if ((iob_gpio_data.class = - class_create(THIS_MODULE, IOB_GPIO_DRIVER_CLASS)) == NULL) { - printk("Device class can not be created!\n"); - goto r_class; - } - - // Create device file - iob_gpio_data.device = - device_create(iob_gpio_data.class, NULL, iob_gpio_data.devnum, NULL, - IOB_GPIO_DRIVER_NAME); - if (iob_gpio_data.device == NULL) { - printk("Can not create device file!\n"); - goto r_device; - } - - result = iob_gpio_create_device_attr_files(iob_gpio_data.device); - if (result) { - pr_err("Cannot create device attribute file......\n"); - goto r_dev_file; - } - - dev_info(&pdev->dev, "initialized.\n"); - goto r_ok; - -r_dev_file: - iob_gpio_remove_device_attr_files(&iob_gpio_data); -r_device: - class_destroy(iob_gpio_data.class); -r_class: - cdev_del(&iob_gpio_data.cdev); -r_cdev_add: - unregister_chrdev_region(iob_gpio_data.devnum, 1); -r_alloc_region: - // iounmap is managed by devm -r_ioremmap: -r_get_resource: -r_ok: - - return result; -} - -static int iob_gpio_remove(struct platform_device *pdev) { - iob_gpio_remove_device_attr_files(&iob_gpio_data); - class_destroy(iob_gpio_data.class); - cdev_del(&iob_gpio_data.cdev); - unregister_chrdev_region(iob_gpio_data.devnum, 1); - // Note: no need for iounmap, since we are using devm_ioremap_resource() - - dev_info(&pdev->dev, "exiting.\n"); - - return 0; -} - -static int __init iob_gpio_init(void) { - pr_info("[Driver] %s: initializing.\n", IOB_GPIO_DRIVER_NAME); - - return platform_driver_register(&iob_gpio_driver); -} - -static void __exit iob_gpio_exit(void) { - pr_info("[Driver] %s: exiting.\n", IOB_GPIO_DRIVER_NAME); - platform_driver_unregister(&iob_gpio_driver); -} - -// -// File operations -// - -static int iob_gpio_open(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_gpio device opened\n"); - - if (!mutex_trylock(&iob_gpio_mutex)) { - pr_info("Another process is accessing the device\n"); - - return -EBUSY; - } - - return 0; -} - -static int iob_gpio_release(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_gpio device closed\n"); - - mutex_unlock(&iob_gpio_mutex); - - return 0; -} - -static ssize_t iob_gpio_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) { - int size = 0; - u32 value = 0; - - /* read value from register */ - switch (*ppos) { - case IOB_GPIO_GPIO_INPUT_ADDR: - value = iob_data_read_reg(iob_gpio_data.regbase, IOB_GPIO_GPIO_INPUT_ADDR, - IOB_GPIO_GPIO_INPUT_W); - size = (IOB_GPIO_GPIO_INPUT_W >> 3); // bit to bytes - pr_info("[Driver] Read data input!\n"); - break; - case IOB_GPIO_VERSION_ADDR: - value = iob_data_read_reg(iob_gpio_data.regbase, IOB_GPIO_VERSION_ADDR, - IOB_GPIO_VERSION_W); - size = (IOB_GPIO_VERSION_W >> 3); // bit to bytes - pr_info("[Driver] Read version!\n"); - break; - default: - // invalid address - no bytes read - return 0; - } - - // Read min between count and REG_SIZE - if (size > count) - size = count; - - if (copy_to_user(buf, &value, size)) - return -EFAULT; - - return count; -} - -static ssize_t iob_gpio_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) { - int size = 0; - u32 value = 0; - - switch (*ppos) { - case IOB_GPIO_GPIO_OUTPUT_ADDR: - size = (IOB_GPIO_GPIO_OUTPUT_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_gpio_data.regbase, value, IOB_GPIO_GPIO_OUTPUT_ADDR, - IOB_GPIO_GPIO_OUTPUT_W); - pr_info("[Driver] Reset iob_gpio: 0x%x\n", value); - break; - case IOB_GPIO_GPIO_OUTPUT_ENABLE_ADDR: - size = (IOB_GPIO_GPIO_OUTPUT_ENABLE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_gpio_data.regbase, value, - IOB_GPIO_GPIO_OUTPUT_ENABLE_ADDR, - IOB_GPIO_GPIO_OUTPUT_ENABLE_W); - pr_info("[Driver] Enable iob_gpio: 0x%x\n", value); - break; - default: - pr_info("[Driver] Invalid write address 0x%x\n", (unsigned int)*ppos); - // invalid address - no bytes written - return 0; - } - - return count; -} - -/* Custom lseek function - * check: lseek(2) man page for whence modes - */ -static loff_t iob_gpio_llseek(struct file *filp, loff_t offset, int whence) { - loff_t new_pos = -1; - - switch (whence) { - case SEEK_SET: - new_pos = offset; - break; - case SEEK_CUR: - new_pos = filp->f_pos + offset; - break; - case SEEK_END: - new_pos = (1 << IOB_GPIO_CSRS_ADDR_W) + offset; - break; - default: - return -EINVAL; - } - - // Check for valid bounds - if (new_pos < 0 || new_pos > iob_gpio_data.regsize) { - return -EINVAL; - } - - // Update file position - filp->f_pos = new_pos; - - return new_pos; -} - -module_init(iob_gpio_init); -module_exit(iob_gpio_exit); - -MODULE_LICENSE("Dual MIT/GPL"); -MODULE_AUTHOR("IObundle"); -MODULE_DESCRIPTION("IOb-GPIO Drivers"); -MODULE_VERSION("0.10"); diff --git a/lib/hardware/iob_gpio/software/linux/iob_gpio.dts b/lib/hardware/iob_gpio/software/linux/iob_gpio.dts deleted file mode 100644 index 367ff6217..000000000 --- a/lib/hardware/iob_gpio/software/linux/iob_gpio.dts +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* Copyright (c) 2024 IObundle */ - -/dts-v1/; - -/ { - #address-cells = <1>; - #size-cells = <1>; - model = "IOb-SoC, VexRiscv"; - compatible = "IOb-SoC, VexRiscv"; - // CPU - // Memory - // Choosen - soc { - #address-cells = <1>; - #size-cells = <1>; - compatible = "iobundle,iob-soc", "simple-bus"; - ranges; - - // Other SOC peripherals go here - - // Add this Node to device tree - GPIO0: gpio@/*GPIO0_ADDR_MACRO*/ { - compatible = "iobundle,gpio0"; - reg = <0x/*GPIO0_ADDR_MACRO*/ 0x100>; - }; - - }; -}; diff --git a/lib/hardware/iob_gpio/software/linux/user/Makefile b/lib/hardware/iob_gpio/software/linux/user/Makefile deleted file mode 100644 index ab9c37399..000000000 --- a/lib/hardware/iob_gpio/software/linux/user/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -SRC = $(wildcard *.c) -HDR += iob_gpio.h -FLAGS = -Wall -Werror -O2 -FLAGS += -static -FLAGS += -march=rv32imac -FLAGS += -mabi=ilp32 -BIN = iob_gpio_user -CC = riscv64-unknown-linux-gnu-gcc - -all: $(BIN) - -$(BIN): $(SRC) $(HDR) - $(CC) $(FLAGS) $(INCLUDE) -o $(BIN) $(SRC) - -LIB_DIR = ../../../../../../ -IOB_LINUX_DIR ?= ../../../$(LIB_DIR)/iob-linux -$(HDR): - cd $(LIB_DIR) && \ - .$(IOB_LINUX_DIR)/scripts/drivers.py iob_gpio -o `realpath $(CURDIR)` - -clean: - rm -rf $(BIN) $(HDR) diff --git a/lib/hardware/iob_gpio/software/linux/user/iob_gpio_user.c b/lib/hardware/iob_gpio/software/linux/user/iob_gpio_user.c deleted file mode 100644 index cd73c968b..000000000 --- a/lib/hardware/iob_gpio/software/linux/user/iob_gpio_user.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include -#include -#include - -#include "iob_gpio.h" - -int sysfs_read_file(const char *filename, uint32_t *read_value) { - // Open file for read - FILE *file = fopen(filename, "r"); - if (file == NULL) { - perror("[User] Failed to open the file"); - return -1; - } - - // Read uint32_t value from file in ASCII - ssize_t ret = fscanf(file, "%u", read_value); - if (ret == -1) { - perror("[User] Failed to read from file"); - fclose(file); - return -1; - } - - fclose(file); - - return ret; -} - -int sysfs_write_file(const char *filename, uint32_t write_value) { - // Open file for write - FILE *file = fopen(filename, "w"); - if (file == NULL) { - perror("[User] Failed to open the file"); - return -1; - } - - // Write uint32_t value to file in ASCII - ssize_t ret = fprintf(file, "%u", write_value); - if (ret == -1) { - perror("[User] Failed to write to file"); - fclose(file); - return -1; - } - - fclose(file); - - return ret; -} - -uint32_t gpio_get() { - uint32_t value = 0; - sysfs_read_file(IOB_GPIO_SYSFILE_GPIO_INPUT, &value); - - return value; -} - -int gpio_set(uint32_t value) { - return sysfs_write_file(IOB_GPIO_SYSFILE_GPIO_OUTPUT, value); -} - -int gpio_set_output_enable(uint32_t value) { - return sysfs_write_file(IOB_GPIO_SYSFILE_GPIO_OUTPUT_ENABLE, value); -} - -int gpio_print_version() { - uint32_t ret = -1; - uint32_t version = 0; - - ret = sysfs_read_file(IOB_GPIO_SYSFILE_VERSION, &version); - if (ret == -1) { - return ret; - } - - printf("[User] Version: 0x%x\n", version); - return 0; -} - -int main() { - printf("[User] IOb-GPIO test\n"); - - if (gpio_print_version() == -1) { - perror("[User] Failed to print version"); - - return -1; - } - - // Attempt to set GPIO registers - if (gpio_set_output_enable(0x0) == -1 || gpio_set(0x0) == -1) { - perror("[User] Failed to set outputs"); - - return -1; - } - - return 0; -} diff --git a/lib/hardware/iob_gpio/software/src/iob-gpio.c b/lib/hardware/iob_gpio/software/src/iob-gpio.c deleted file mode 100644 index a4f2f7173..000000000 --- a/lib/hardware/iob_gpio/software/src/iob-gpio.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob-gpio.h" - -// GPIO functions - -// Set GPIO base address -void gpio_init(int base_address) { IOB_GPIO_INIT_BASEADDR(base_address); } - -// Get values from inputs -uint32_t gpio_get() { return IOB_GPIO_GET_GPIO_INPUT(); } - -// Set values on outputs -void gpio_set(uint32_t value) { IOB_GPIO_SET_GPIO_OUTPUT(value); } - -// Set mask for outputs (bits 1 are driven outputs, bits 0 are tristate) -void gpio_set_output_enable(uint32_t value) { - IOB_GPIO_SET_GPIO_OUTPUT_ENABLE(value); -} diff --git a/lib/hardware/iob_gpio/software/src/iob-gpio.h b/lib/hardware/iob_gpio/software/src/iob-gpio.h deleted file mode 100644 index 153632919..000000000 --- a/lib/hardware/iob_gpio/software/src/iob-gpio.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include - -#include "iob_gpio_csrs.h" - -// GPIO functions - -// Set GPIO base address -void gpio_init(int base_address); - -// Get values from inputs -uint32_t gpio_get(); - -// Set values on outputs -void gpio_set(uint32_t outputs); - -// Set mask for outputs (bits 1 are driven outputs, bits 0 are tristate) -void gpio_set_output_enable(uint32_t value); diff --git a/lib/hardware/iob_gpio/software/src/iob_gpio_swreg_pc_emul.c b/lib/hardware/iob_gpio/software/src/iob_gpio_swreg_pc_emul.c deleted file mode 100644 index 641641b89..000000000 --- a/lib/hardware/iob_gpio/software/src/iob_gpio_swreg_pc_emul.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* PC Emulation of GPIO peripheral */ - -#include -#include - -#include "iob_gpio_csrs.h" - -static uint32_t base; -void IOB_GPIO_INIT_BASEADDR(uint32_t addr) { base = addr; } - -// Core Setters and Getters -uint32_t IOB_GPIO_GET_GPIO_INPUT() { return 0xaaaaaaaa; } - -void IOB_GPIO_SET_GPIO_OUTPUT(uint32_t value) {} - -void IOB_GPIO_SET_GPIO_OUTPUT_ENABLE(uint32_t value) {} - -uint16_t IOB_GPIO_GET_VERSION() { return 0xaaaa; } diff --git a/lib/hardware/iob_iobuf/iob_iobuf.py b/lib/hardware/iob_iobuf/iob_iobuf.py deleted file mode 100644 index 85904c9ab..000000000 --- a/lib/hardware/iob_iobuf/iob_iobuf.py +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "ports": [ - { - "name": "i_i", - "descr": "Input port", - "signals": [ - { - "name": "i", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "t_i", - "descr": "Input port", - "signals": [ - { - "name": "t", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "n_i", - "descr": "Input port", - "signals": [ - { - "name": "n", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "o_o", - "descr": "Output port", - "signals": [ - { - "name": "o", - "width": 1, - "direction": "output", - }, - ], - }, - { - "name": "io_io", - "descr": "In/Output port", - "signals": [ - { - "name": "io", - "width": 1, - "direction": "inout", - }, - ], - }, - ], - "wires": [ - { - "name": "o_int", - "descr": "o_int wire", - "signals": [ - {"name": "o_int", "width": 1}, - ], - }, - ], - "snippets": [ - { - "verilog_code": f""" - `ifdef XILINX - IOBUF IOBUF_inst ( - .I (i_i), - .T (t_i), - .O (o_int), - .IO(io) - ); -`else - reg o_var; - assign io = t_i ? 1'bz : i_i; - always @* o_var = #1 io; - assign o_int = o_var; -`endif - - assign o_o = (n_i ^ o_int); - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/iob_picorv32/hardware/src/iob_picorv32.v b/lib/hardware/iob_picorv32/hardware/src/iob_picorv32.v deleted file mode 100644 index 4dcb19de8..000000000 --- a/lib/hardware/iob_picorv32/hardware/src/iob_picorv32.v +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1 ns / 1 ps -`include "iob_picorv32_conf.vh" - -module iob_picorv32 #( - `include "iob_picorv32_params.vs" -) ( - `include "iob_picorv32_io.vs" -); - - //picorv32 native interface wires - wire cpu_instr; - wire cpu_valid; - wire [ ADDR_W-1:0] cpu_addr; - wire [DATA_W/8-1:0] cpu_wstrb; - wire [ DATA_W-1:0] cpu_wdata; - wire [ DATA_W-1:0] cpu_rdata; - wire cpu_ready; - - //split cpu bus into ibus and dbus - wire iob_i_valid; - wire iob_d_valid; - - //iob interface wires - wire iob_i_rvalid; - wire iob_d_rvalid; - wire iob_d_ready; - - //compute the instruction bus request - assign ibus_iob_valid_o = iob_i_valid; - assign ibus_iob_addr_o = cpu_addr; - assign ibus_iob_wdata_o = {DATA_W{1'b0}}; - assign ibus_iob_wstrb_o = {(DATA_W/8){1'b0}}; - - //compute the data bus request - assign dbus_iob_valid_o = iob_d_valid; - assign dbus_iob_addr_o = cpu_addr; - assign dbus_iob_wdata_o = cpu_wdata; - assign dbus_iob_wstrb_o = cpu_wstrb; - - //split cpu bus into instruction and data buses - assign iob_i_valid = cpu_instr & cpu_valid; - - assign iob_d_valid = (~cpu_instr) & cpu_valid & (~iob_d_rvalid); - - //extract iob interface wires from concatenated buses - assign iob_d_rvalid = dbus_iob_rvalid_i; - assign iob_i_rvalid = ibus_iob_rvalid_i; - assign iob_d_ready = dbus_iob_ready_i; - - //cpu rdata and ready - assign cpu_rdata = cpu_instr ? ibus_iob_rdata_i : dbus_iob_rdata_i; - assign cpu_ready = cpu_instr ? iob_i_rvalid : |cpu_wstrb? iob_d_ready : iob_d_rvalid; - - //intantiate the PicoRV32 CPU - picorv32 #( - .COMPRESSED_ISA (USE_COMPRESSED), - .ENABLE_FAST_MUL(USE_MUL_DIV), - .ENABLE_DIV (USE_MUL_DIV), - .BARREL_SHIFTER (1) - ) picorv32_core ( - .clk (clk_i), - .resetn (~rst_i), - .trap (trap_o), - .mem_instr (cpu_instr), - //memory interface - .mem_valid (cpu_valid), - .mem_addr (cpu_addr), - .mem_wdata (cpu_wdata), - .mem_wstrb (cpu_wstrb), - .mem_rdata (cpu_rdata), - .mem_ready (cpu_ready), - //lookahead interface - .mem_la_read (), - .mem_la_write(), - .mem_la_addr (), - .mem_la_wdata(), - .mem_la_wstrb(), - //co-processor interface (PCPI) - .pcpi_valid (), - .pcpi_insn (), - .pcpi_rs1 (), - .pcpi_rs2 (), - .pcpi_wr (1'b0), - .pcpi_rd (32'd0), - .pcpi_wait (1'b0), - .pcpi_ready (1'b0), - // IRQ - .irq (32'd0), - .eoi (), - .trace_valid (), - .trace_data () - ); - -endmodule diff --git a/lib/hardware/iob_picorv32/hardware/src/picorv32.v b/lib/hardware/iob_picorv32/hardware/src/picorv32.v deleted file mode 100644 index b13d9a1ad..000000000 --- a/lib/hardware/iob_picorv32/hardware/src/picorv32.v +++ /dev/null @@ -1,2930 +0,0 @@ -// SPDX-FileCopyrightText: 2015 Clifford Wolf -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* - * PicoRV32 -- A Small RISC-V (RV32I) Processor Core - */ - -`timescale 1 ns / 1 ps -// `default_nettype none -// `define DEBUGNETS -// `define DEBUGREGS -// `define DEBUGASM -// `define DEBUG - -`ifdef DEBUG -`define DEBUG_(debug_command) debug_command -`else -`define DEBUG_(debug_command) empty_statement(0) -`endif - -`ifdef FORMAL -`define FORMAL_KEEP (* keep *) -`define ASSERT_(assert_expr) ASSERT_(assert_expr) -`else -`ifdef DEBUGNETS -`define FORMAL_KEEP (* keep *) -`else -`define FORMAL_KEEP -`endif -`define ASSERT_(assert_expr) empty_statement(0) -`endif - -// this macro can be used to check if the verilog files in your -// design are read in the correct order. -`define PICORV32_V - - -/*************************************************************** - * picorv32 - ***************************************************************/ - -module picorv32 #( - parameter [ 0:0] ENABLE_COUNTERS = 1, - parameter [ 0:0] ENABLE_COUNTERS64 = 1, - parameter [ 0:0] ENABLE_REGS_16_31 = 1, - parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, - parameter [ 0:0] LATCHED_MEM_RDATA = 0, - parameter [ 0:0] TWO_STAGE_SHIFT = 1, - parameter [ 0:0] BARREL_SHIFTER = 0, - parameter [ 0:0] TWO_CYCLE_COMPARE = 0, - parameter [ 0:0] TWO_CYCLE_ALU = 0, - parameter [ 0:0] COMPRESSED_ISA = 0, - parameter [ 0:0] CATCH_MISALIGN = 1, - parameter [ 0:0] CATCH_ILLINSN = 1, - parameter [ 0:0] ENABLE_PCPI = 0, - parameter [ 0:0] ENABLE_MUL = 0, - parameter [ 0:0] ENABLE_FAST_MUL = 0, - parameter [ 0:0] ENABLE_DIV = 0, - parameter [ 0:0] ENABLE_IRQ = 0, - parameter [ 0:0] ENABLE_IRQ_QREGS = 1, - parameter [ 0:0] ENABLE_IRQ_TIMER = 1, - parameter [ 0:0] ENABLE_TRACE = 0, - parameter [ 0:0] REGS_INIT_ZERO = 0, - parameter [31:0] MASKED_IRQ = 32'h0000_0000, - parameter [31:0] LATCHED_IRQ = 32'hffff_ffff, - parameter [31:0] PROGADDR_RESET = 32'h8000_0000, - parameter [31:0] PROGADDR_IRQ = 32'h0000_0010, - parameter [31:0] STACKADDR = 32'hffff_ffff -) ( - input clk, - resetn, - output reg trap, - - output reg mem_valid, - output reg mem_instr, - input mem_ready, - - output reg [31:0] mem_addr, - output reg [31:0] mem_wdata, - output reg [ 3:0] mem_wstrb, - input [31:0] mem_rdata, - - // Look-Ahead Interface - output mem_la_read, - output mem_la_write, - output [31:0] mem_la_addr, - output reg [31:0] mem_la_wdata, - output reg [ 3:0] mem_la_wstrb, - - // Pico Co-Processor Interface (PCPI) - output reg pcpi_valid, - output reg [31:0] pcpi_insn, - output [31:0] pcpi_rs1, - output [31:0] pcpi_rs2, - input pcpi_wr, - input [31:0] pcpi_rd, - input pcpi_wait, - input pcpi_ready, - - // IRQ Interface - input [31:0] irq, - output reg [31:0] eoi, - -`ifdef RISCV_FORMAL - output reg rvfi_valid, - output reg [63:0] rvfi_order, - output reg [31:0] rvfi_insn, - output reg rvfi_trap, - output reg rvfi_halt, - output reg rvfi_intr, - output reg [ 1:0] rvfi_mode, - output reg [ 4:0] rvfi_rs1_addr, - output reg [ 4:0] rvfi_rs2_addr, - output reg [31:0] rvfi_rs1_rdata, - output reg [31:0] rvfi_rs2_rdata, - output reg [ 4:0] rvfi_rd_addr, - output reg [31:0] rvfi_rd_wdata, - output reg [31:0] rvfi_pc_rdata, - output reg [31:0] rvfi_pc_wdata, - output reg [31:0] rvfi_mem_addr, - output reg [ 3:0] rvfi_mem_rmask, - output reg [ 3:0] rvfi_mem_wmask, - output reg [31:0] rvfi_mem_rdata, - output reg [31:0] rvfi_mem_wdata, -`endif - - // Trace Interface - output reg trace_valid, - output reg [35:0] trace_data -); - - - localparam integer irq_timer = 0; - localparam integer irq_ebreak = 1; - localparam integer irq_buserror = 2; - - localparam integer irqregs_offset = ENABLE_REGS_16_31 ? 32 : 16; - localparam integer regfile_size = (ENABLE_REGS_16_31 ? 32 : 16) + 4*ENABLE_IRQ*ENABLE_IRQ_QREGS; - localparam integer regindex_bits = (ENABLE_REGS_16_31 ? 5 : 4) + ENABLE_IRQ * ENABLE_IRQ_QREGS; - - localparam WITH_PCPI = ENABLE_PCPI || ENABLE_MUL || ENABLE_FAST_MUL || ENABLE_DIV; - - localparam [35:0] TRACE_BRANCH = {4'b0001, 32'b0}; - localparam [35:0] TRACE_ADDR = {4'b0010, 32'b0}; - localparam [35:0] TRACE_IRQ = {4'b1000, 32'b0}; - - reg [63:0] count_cycle, count_instr; - reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out; - reg [ 4:0] reg_sh; - - reg [31:0] next_insn_opcode; - reg [31:0] dbg_insn_opcode; - reg [31:0] dbg_insn_addr; - - wire dbg_mem_valid = mem_valid; - wire dbg_mem_instr = mem_instr; - wire dbg_mem_ready = mem_ready; - wire [31:0] dbg_mem_addr = mem_addr; - wire [31:0] dbg_mem_wdata = mem_wdata; - wire [ 3:0] dbg_mem_wstrb = mem_wstrb; - wire [31:0] dbg_mem_rdata = mem_rdata; - - assign pcpi_rs1 = reg_op1; - assign pcpi_rs2 = reg_op2; - - wire [31:0] next_pc; - - reg irq_delay; - reg irq_active; - reg [31:0] irq_mask; - reg [31:0] irq_pending; - reg [31:0] timer; - - reg [31:0] cpuregs [regfile_size]; - - integer i; - initial begin - if (REGS_INIT_ZERO) begin - for (i = 0; i < regfile_size; i = i + 1) cpuregs[i] = 0; - end - end - - task empty_statement; - input x; - - // This task is used by the `ASSERT_ directive in non-formal mode to - // avoid empty statement (which are unsupported by plain Verilog syntax). - begin - end - endtask - -`ifdef DEBUGREGS - wire [31:0] dbg_reg_x0 = 0; - wire [31:0] dbg_reg_x1 = cpuregs[1]; - wire [31:0] dbg_reg_x2 = cpuregs[2]; - wire [31:0] dbg_reg_x3 = cpuregs[3]; - wire [31:0] dbg_reg_x4 = cpuregs[4]; - wire [31:0] dbg_reg_x5 = cpuregs[5]; - wire [31:0] dbg_reg_x6 = cpuregs[6]; - wire [31:0] dbg_reg_x7 = cpuregs[7]; - wire [31:0] dbg_reg_x8 = cpuregs[8]; - wire [31:0] dbg_reg_x9 = cpuregs[9]; - wire [31:0] dbg_reg_x10 = cpuregs[10]; - wire [31:0] dbg_reg_x11 = cpuregs[11]; - wire [31:0] dbg_reg_x12 = cpuregs[12]; - wire [31:0] dbg_reg_x13 = cpuregs[13]; - wire [31:0] dbg_reg_x14 = cpuregs[14]; - wire [31:0] dbg_reg_x15 = cpuregs[15]; - wire [31:0] dbg_reg_x16 = cpuregs[16]; - wire [31:0] dbg_reg_x17 = cpuregs[17]; - wire [31:0] dbg_reg_x18 = cpuregs[18]; - wire [31:0] dbg_reg_x19 = cpuregs[19]; - wire [31:0] dbg_reg_x20 = cpuregs[20]; - wire [31:0] dbg_reg_x21 = cpuregs[21]; - wire [31:0] dbg_reg_x22 = cpuregs[22]; - wire [31:0] dbg_reg_x23 = cpuregs[23]; - wire [31:0] dbg_reg_x24 = cpuregs[24]; - wire [31:0] dbg_reg_x25 = cpuregs[25]; - wire [31:0] dbg_reg_x26 = cpuregs[26]; - wire [31:0] dbg_reg_x27 = cpuregs[27]; - wire [31:0] dbg_reg_x28 = cpuregs[28]; - wire [31:0] dbg_reg_x29 = cpuregs[29]; - wire [31:0] dbg_reg_x30 = cpuregs[30]; - wire [31:0] dbg_reg_x31 = cpuregs[31]; -`endif - - // Internal PCPI Cores - - wire pcpi_mul_wr; - wire [31:0] pcpi_mul_rd; - wire pcpi_mul_wait; - wire pcpi_mul_ready; - - wire pcpi_div_wr; - wire [31:0] pcpi_div_rd; - wire pcpi_div_wait; - wire pcpi_div_ready; - - reg pcpi_int_wr; - reg [31:0] pcpi_int_rd; - reg pcpi_int_wait; - reg pcpi_int_ready; - - generate - if (ENABLE_FAST_MUL) begin : g_fast_mul - picorv32_pcpi_fast_mul pcpi_mul ( - .clk (clk), - .resetn (resetn), - .pcpi_valid(pcpi_valid), - .pcpi_insn (pcpi_insn), - .pcpi_rs1 (pcpi_rs1), - .pcpi_rs2 (pcpi_rs2), - .pcpi_wr (pcpi_mul_wr), - .pcpi_rd (pcpi_mul_rd), - .pcpi_wait (pcpi_mul_wait), - .pcpi_ready(pcpi_mul_ready) - ); - end else if (ENABLE_MUL) begin : g_mul - picorv32_pcpi_mul pcpi_mul ( - .clk (clk), - .resetn (resetn), - .pcpi_valid(pcpi_valid), - .pcpi_insn (pcpi_insn), - .pcpi_rs1 (pcpi_rs1), - .pcpi_rs2 (pcpi_rs2), - .pcpi_wr (pcpi_mul_wr), - .pcpi_rd (pcpi_mul_rd), - .pcpi_wait (pcpi_mul_wait), - .pcpi_ready(pcpi_mul_ready) - ); - end else begin : g_no_mul - assign pcpi_mul_wr = 0; - assign pcpi_mul_rd = 32'hxx; - assign pcpi_mul_wait = 0; - assign pcpi_mul_ready = 0; - end - endgenerate - - generate - if (ENABLE_DIV) begin : g_div - picorv32_pcpi_div pcpi_div ( - .clk (clk), - .resetn (resetn), - .pcpi_valid(pcpi_valid), - .pcpi_insn (pcpi_insn), - .pcpi_rs1 (pcpi_rs1), - .pcpi_rs2 (pcpi_rs2), - .pcpi_wr (pcpi_div_wr), - .pcpi_rd (pcpi_div_rd), - .pcpi_wait (pcpi_div_wait), - .pcpi_ready(pcpi_div_ready) - ); - end else begin : g_no_div - assign pcpi_div_wr = 0; - assign pcpi_div_rd = 32'hx; - assign pcpi_div_wait = 0; - assign pcpi_div_ready = 0; - end - endgenerate - - always @* begin - pcpi_int_wr = 0; - pcpi_int_rd = 32'hx; - pcpi_int_wait = |{ENABLE_PCPI && pcpi_wait, (ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_wait, ENABLE_DIV && pcpi_div_wait}; - pcpi_int_ready = |{ENABLE_PCPI && pcpi_ready, (ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_ready, ENABLE_DIV && pcpi_div_ready}; - - if (ENABLE_PCPI && pcpi_ready) begin - pcpi_int_wr = ENABLE_PCPI ? pcpi_wr : 0; - pcpi_int_rd = ENABLE_PCPI ? pcpi_rd : 0; - end else if ((ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_ready) begin - pcpi_int_wr = pcpi_mul_wr; - pcpi_int_rd = pcpi_mul_rd; - end else if (ENABLE_DIV && pcpi_div_ready) begin - pcpi_int_wr = pcpi_div_wr; - pcpi_int_rd = pcpi_div_rd; - end - end - - - // Memory Interface - - reg [ 1:0] mem_state; - reg [ 1:0] mem_wordsize; - reg [31:0] mem_rdata_word; - reg [31:0] mem_rdata_q; - reg mem_do_prefetch; - reg mem_do_rinst; - reg mem_do_rdata; - reg mem_do_wdata; - - wire mem_xfer; - reg mem_la_secondword, mem_la_firstword_reg, last_mem_valid; - wire mem_la_firstword = COMPRESSED_ISA && (mem_do_prefetch || mem_do_rinst) && next_pc[1] && !mem_la_secondword; - wire mem_la_firstword_xfer = COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg); - - reg prefetched_high_word; - reg clear_prefetched_high_word; - reg [15:0] mem_16bit_buffer; - - wire [31:0] mem_rdata_latched_noshuffle; - wire [31:0] mem_rdata_latched; - - wire mem_la_use_prefetched_high_word = COMPRESSED_ISA && mem_la_firstword && prefetched_high_word && !clear_prefetched_high_word; - assign mem_xfer = (mem_valid && mem_ready) || (mem_la_use_prefetched_high_word && mem_do_rinst); - - wire mem_busy = |{mem_do_prefetch, mem_do_rinst, mem_do_rdata, mem_do_wdata}; - wire mem_done = resetn && ((mem_xfer && |mem_state && (mem_do_rinst || mem_do_rdata || mem_do_wdata)) || (&mem_state && mem_do_rinst)) && - (!mem_la_firstword || (~&mem_rdata_latched[1:0] && mem_xfer)); - - assign mem_la_write = resetn && !mem_state && mem_do_wdata; - assign mem_la_read = resetn && ((!mem_la_use_prefetched_high_word && !mem_state && (mem_do_rinst || mem_do_prefetch || mem_do_rdata)) || - (COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg) && !mem_la_secondword && &mem_rdata_latched[1:0])); - assign mem_la_addr = (mem_do_prefetch || mem_do_rinst) ? {next_pc[31:2] + mem_la_firstword_xfer, 2'b00} : {reg_op1[31:2], 2'b00}; - - assign mem_rdata_latched_noshuffle = (mem_xfer || LATCHED_MEM_RDATA) ? mem_rdata : mem_rdata_q; - - assign mem_rdata_latched = COMPRESSED_ISA && mem_la_use_prefetched_high_word ? {16'hx, mem_16bit_buffer} : - COMPRESSED_ISA && mem_la_secondword ? {mem_rdata_latched_noshuffle[15:0], mem_16bit_buffer} : - COMPRESSED_ISA && mem_la_firstword ? {16'hx, mem_rdata_latched_noshuffle[31:16]} : mem_rdata_latched_noshuffle; - - always @(posedge clk) begin - if (!resetn) begin - mem_la_firstword_reg <= 0; - last_mem_valid <= 0; - end else begin - if (!last_mem_valid) mem_la_firstword_reg <= mem_la_firstword; - last_mem_valid <= mem_valid && !mem_ready; - end - end - - always @* begin - //(* full_case *) - case (mem_wordsize) - 0: begin - mem_la_wdata = reg_op2; - mem_la_wstrb = 4'b1111; - mem_rdata_word = mem_rdata; - end - 1: begin - mem_la_wdata = {2{reg_op2[15:0]}}; - mem_la_wstrb = reg_op1[1] ? 4'b1100 : 4'b0011; - case (reg_op1[1]) - 1'b0: mem_rdata_word = {16'b0, mem_rdata[15:0]}; - default: mem_rdata_word = {16'b0, mem_rdata[31:16]}; - endcase - end - 2: begin - mem_la_wdata = {4{reg_op2[7:0]}}; - mem_la_wstrb = 4'b0001 << reg_op1[1:0]; - case (reg_op1[1:0]) - 2'b00: mem_rdata_word = {24'b0, mem_rdata[7:0]}; - 2'b01: mem_rdata_word = {24'b0, mem_rdata[15:8]}; - 2'b10: mem_rdata_word = {24'b0, mem_rdata[23:16]}; - default: mem_rdata_word = {24'b0, mem_rdata[31:24]}; - endcase - end - default: ; - endcase - end - - always @(posedge clk) begin - if (mem_xfer) begin - mem_rdata_q <= COMPRESSED_ISA ? mem_rdata_latched : mem_rdata; - next_insn_opcode <= COMPRESSED_ISA ? mem_rdata_latched : mem_rdata; - end - - if (COMPRESSED_ISA && mem_done && (mem_do_prefetch || mem_do_rinst)) begin - case (mem_rdata_latched[1:0]) - 2'b00: begin // Quadrant 0 - case (mem_rdata_latched[15:13]) - 3'b000: begin // C.ADDI4SPN - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:20] <= { - 2'b0, - mem_rdata_latched[10:7], - mem_rdata_latched[12:11], - mem_rdata_latched[5], - mem_rdata_latched[6], - 2'b00 - }; - end - 3'b010: begin // C.LW - mem_rdata_q[31:20] <= { - 5'b0, - mem_rdata_latched[5], - mem_rdata_latched[12:10], - mem_rdata_latched[6], - 2'b00 - }; - mem_rdata_q[14:12] <= 3'b010; - end - 3'b110: begin // C.SW - {mem_rdata_q[31:25], mem_rdata_q[11:7]} <= { - 5'b0, - mem_rdata_latched[5], - mem_rdata_latched[12:10], - mem_rdata_latched[6], - 2'b00 - }; - mem_rdata_q[14:12] <= 3'b010; - end - default: ; - endcase - end - 2'b01: begin // Quadrant 1 - case (mem_rdata_latched[15:13]) - 3'b000: begin // C.ADDI - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]}); - end - 3'b010: begin // C.LI - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]}); - end - 3'b011: begin - if (mem_rdata_latched[11:7] == 2) begin // C.ADDI16SP - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:20] <= $signed( - { - mem_rdata_latched[12], - mem_rdata_latched[4:3], - mem_rdata_latched[5], - mem_rdata_latched[2], - mem_rdata_latched[6], - 4'b0000 - } - ); - end else begin // C.LUI - mem_rdata_q[31:12] <= - $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]}); - end - end - 3'b100: begin - if (mem_rdata_latched[11:10] == 2'b00) begin // C.SRLI - mem_rdata_q[31:25] <= 7'b0000000; - mem_rdata_q[14:12] <= 3'b101; - end - if (mem_rdata_latched[11:10] == 2'b01) begin // C.SRAI - mem_rdata_q[31:25] <= 7'b0100000; - mem_rdata_q[14:12] <= 3'b101; - end - if (mem_rdata_latched[11:10] == 2'b10) begin // C.ANDI - mem_rdata_q[14:12] <= 3'b111; - mem_rdata_q[31:20] <= $signed( - {mem_rdata_latched[12], mem_rdata_latched[6:2]} - ); - end - if (mem_rdata_latched[12:10] == 3'b011) begin // C.SUB, C.XOR, C.OR, C.AND - if (mem_rdata_latched[6:5] == 2'b00) mem_rdata_q[14:12] <= 3'b000; - if (mem_rdata_latched[6:5] == 2'b01) mem_rdata_q[14:12] <= 3'b100; - if (mem_rdata_latched[6:5] == 2'b10) mem_rdata_q[14:12] <= 3'b110; - if (mem_rdata_latched[6:5] == 2'b11) mem_rdata_q[14:12] <= 3'b111; - mem_rdata_q[31:25] <= mem_rdata_latched[6:5] == 2'b00 ? 7'b0100000 : 7'b0000000; - end - end - 3'b110: begin // C.BEQZ - mem_rdata_q[14:12] <= 3'b000; - { mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8] } <= - $signed( - { - mem_rdata_latched[12], - mem_rdata_latched[6:5], - mem_rdata_latched[2], - mem_rdata_latched[11:10], - mem_rdata_latched[4:3] - } - ); - end - 3'b111: begin // C.BNEZ - mem_rdata_q[14:12] <= 3'b001; - { mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8] } <= - $signed( - { - mem_rdata_latched[12], - mem_rdata_latched[6:5], - mem_rdata_latched[2], - mem_rdata_latched[11:10], - mem_rdata_latched[4:3] - } - ); - end - default: ; - endcase - end - 2'b10: begin // Quadrant 2 - case (mem_rdata_latched[15:13]) - 3'b000: begin // C.SLLI - mem_rdata_q[31:25] <= 7'b0000000; - mem_rdata_q[14:12] <= 3'b001; - end - 3'b010: begin // C.LWSP - mem_rdata_q[31:20] <= { - 4'b0, - mem_rdata_latched[3:2], - mem_rdata_latched[12], - mem_rdata_latched[6:4], - 2'b00 - }; - mem_rdata_q[14:12] <= 3'b010; - end - 3'b100: begin - if (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] == 0) begin // C.JR - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:20] <= 12'b0; - end - if (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] != 0) begin // C.MV - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:25] <= 7'b0000000; - end - if (mem_rdata_latched[12] != 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JALR - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:20] <= 12'b0; - end - if (mem_rdata_latched[12] != 0 && mem_rdata_latched[6:2] != 0) begin // C.ADD - mem_rdata_q[14:12] <= 3'b000; - mem_rdata_q[31:25] <= 7'b0000000; - end - end - 3'b110: begin // C.SWSP - {mem_rdata_q[31:25], mem_rdata_q[11:7]} <= { - 4'b0, mem_rdata_latched[8:7], mem_rdata_latched[12:9], 2'b00 - }; - mem_rdata_q[14:12] <= 3'b010; - end - default: ; - endcase - end - default: ; - endcase - end - end - - always @(posedge clk) begin - if (resetn && !trap) begin - if (mem_do_prefetch || mem_do_rinst || mem_do_rdata) `ASSERT_(!mem_do_wdata); - - if (mem_do_prefetch || mem_do_rinst) `ASSERT_(!mem_do_rdata); - - if (mem_do_rdata) `ASSERT_(!mem_do_prefetch && !mem_do_rinst); - - if (mem_do_wdata) `ASSERT_(!(mem_do_prefetch || mem_do_rinst || mem_do_rdata)); - - if (mem_state == 2 || mem_state == 3) `ASSERT_(mem_valid || mem_do_prefetch); - end - end - - always @(posedge clk) begin - if (!resetn || trap) begin - if (!resetn) mem_state <= 0; - if (!resetn || mem_ready) mem_valid <= 0; - mem_la_secondword <= 0; - prefetched_high_word <= 0; - end else begin - if (mem_la_read || mem_la_write) begin - mem_addr <= mem_la_addr; - mem_wstrb <= mem_la_wstrb & {4{mem_la_write}}; - end - if (mem_la_write) begin - mem_wdata <= mem_la_wdata; - end - case (mem_state) - 0: begin - if (mem_do_prefetch || mem_do_rinst || mem_do_rdata) begin - mem_valid <= !mem_la_use_prefetched_high_word; - mem_instr <= mem_do_prefetch || mem_do_rinst; - mem_wstrb <= 0; - mem_state <= 1; - end - if (mem_do_wdata) begin - mem_valid <= 1; - mem_instr <= 0; - mem_state <= 2; - end - end - 1: begin - `ASSERT_(mem_wstrb == 0); - `ASSERT_(mem_do_prefetch || mem_do_rinst || mem_do_rdata); - `ASSERT_(mem_valid == !mem_la_use_prefetched_high_word); - `ASSERT_(mem_instr == (mem_do_prefetch || mem_do_rinst)); - if (mem_xfer) begin - if (COMPRESSED_ISA && mem_la_read) begin - mem_valid <= 1; - mem_la_secondword <= 1; - if (!mem_la_use_prefetched_high_word) mem_16bit_buffer <= mem_rdata[31:16]; - end else begin - mem_valid <= 0; - mem_la_secondword <= 0; - if (COMPRESSED_ISA && !mem_do_rdata) begin - if (~&mem_rdata[1:0] || mem_la_secondword) begin - mem_16bit_buffer <= mem_rdata[31:16]; - prefetched_high_word <= 1; - end else begin - prefetched_high_word <= 2'b0; - end - end - mem_state <= mem_do_rinst || mem_do_rdata ? 2'd0 : 2'd3; - end - end - end - 2: begin - `ASSERT_(mem_wstrb != 0); - `ASSERT_(mem_do_wdata); - if (mem_xfer) begin - mem_valid <= 0; - mem_state <= 0; - end - end - default: begin - `ASSERT_(mem_wstrb == 0); - `ASSERT_(mem_do_prefetch); - if (mem_do_rinst) begin - mem_state <= 0; - end - end - endcase - end - - if (clear_prefetched_high_word) prefetched_high_word <= 0; - end - - - // Instruction Decoder - - reg instr_lui, instr_auipc, instr_jal, instr_jalr; - reg instr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu; - reg instr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw; - reg - instr_addi, - instr_slti, - instr_sltiu, - instr_xori, - instr_ori, - instr_andi, - instr_slli, - instr_srli, - instr_srai; - reg - instr_add, - instr_sub, - instr_sll, - instr_slt, - instr_sltu, - instr_xor, - instr_srl, - instr_sra, - instr_or, - instr_and; - reg instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh, instr_ecall_ebreak; - reg instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer; - wire instr_trap; - - reg [regindex_bits-1:0] decoded_rd, decoded_rs1, decoded_rs2; - reg [31:0] decoded_imm, decoded_imm_uj; - reg decoder_trigger; - reg decoder_trigger_q; - reg decoder_pseudo_trigger; - reg decoder_pseudo_trigger_q; - reg compressed_instr; - - reg is_lui_auipc_jal; - reg is_lb_lh_lw_lbu_lhu; - reg is_slli_srli_srai; - reg is_jalr_addi_slti_sltiu_xori_ori_andi; - reg is_sb_sh_sw; - reg is_sll_srl_sra; - reg is_lui_auipc_jal_jalr_addi_add_sub; - reg is_slti_blt_slt; - reg is_sltiu_bltu_sltu; - reg is_beq_bne_blt_bge_bltu_bgeu; - reg is_lbu_lhu_lw; - reg is_alu_reg_imm; - reg is_alu_reg_reg; - reg is_compare; - - assign instr_trap = (CATCH_ILLINSN || WITH_PCPI) && !{instr_lui, instr_auipc, instr_jal, instr_jalr, - instr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu, - instr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw, - instr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai, - instr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and, - instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh, - instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer}; - - wire is_rdcycle_rdcycleh_rdinstr_rdinstrh; - assign is_rdcycle_rdcycleh_rdinstr_rdinstrh = |{instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh}; - - reg [63:0] new_ascii_instr; - reg [63:0] dbg_ascii_instr; - reg [31:0] dbg_insn_imm; - reg [ 4:0] dbg_insn_rs1; - reg [ 4:0] dbg_insn_rs2; - reg [ 4:0] dbg_insn_rd; - reg [31:0] dbg_rs1val; - reg [31:0] dbg_rs2val; - reg dbg_rs1val_valid; - reg dbg_rs2val_valid; - - always @* begin - new_ascii_instr = "\0"; - - if (instr_lui) new_ascii_instr = "lui"; - if (instr_auipc) new_ascii_instr = "auipc"; - if (instr_jal) new_ascii_instr = "jal"; - if (instr_jalr) new_ascii_instr = "jalr"; - - if (instr_beq) new_ascii_instr = "beq"; - if (instr_bne) new_ascii_instr = "bne"; - if (instr_blt) new_ascii_instr = "blt"; - if (instr_bge) new_ascii_instr = "bge"; - if (instr_bltu) new_ascii_instr = "bltu"; - if (instr_bgeu) new_ascii_instr = "bgeu"; - - if (instr_lb) new_ascii_instr = "lb"; - if (instr_lh) new_ascii_instr = "lh"; - if (instr_lw) new_ascii_instr = "lw"; - if (instr_lbu) new_ascii_instr = "lbu"; - if (instr_lhu) new_ascii_instr = "lhu"; - if (instr_sb) new_ascii_instr = "sb"; - if (instr_sh) new_ascii_instr = "sh"; - if (instr_sw) new_ascii_instr = "sw"; - - if (instr_addi) new_ascii_instr = "addi"; - if (instr_slti) new_ascii_instr = "slti"; - if (instr_sltiu) new_ascii_instr = "sltiu"; - if (instr_xori) new_ascii_instr = "xori"; - if (instr_ori) new_ascii_instr = "ori"; - if (instr_andi) new_ascii_instr = "andi"; - if (instr_slli) new_ascii_instr = "slli"; - if (instr_srli) new_ascii_instr = "srli"; - if (instr_srai) new_ascii_instr = "srai"; - - if (instr_add) new_ascii_instr = "add"; - if (instr_sub) new_ascii_instr = "sub"; - if (instr_sll) new_ascii_instr = "sll"; - if (instr_slt) new_ascii_instr = "slt"; - if (instr_sltu) new_ascii_instr = "sltu"; - if (instr_xor) new_ascii_instr = "xor"; - if (instr_srl) new_ascii_instr = "srl"; - if (instr_sra) new_ascii_instr = "sra"; - if (instr_or) new_ascii_instr = "or"; - if (instr_and) new_ascii_instr = "and"; - - if (instr_rdcycle) new_ascii_instr = "rdcycle"; - if (instr_rdcycleh) new_ascii_instr = "rdcycleh"; - if (instr_rdinstr) new_ascii_instr = "rdinstr"; - if (instr_rdinstrh) new_ascii_instr = "rdinstrh"; - - if (instr_getq) new_ascii_instr = "getq"; - if (instr_setq) new_ascii_instr = "setq"; - if (instr_retirq) new_ascii_instr = "retirq"; - if (instr_maskirq) new_ascii_instr = "maskirq"; - if (instr_waitirq) new_ascii_instr = "waitirq"; - if (instr_timer) new_ascii_instr = "timer"; - end - - reg [63:0] q_ascii_instr; - reg [31:0] q_insn_imm; - reg [31:0] q_insn_opcode; - reg [ 4:0] q_insn_rs1; - reg [ 4:0] q_insn_rs2; - reg [ 4:0] q_insn_rd; - reg dbg_next; - - wire launch_next_insn; - reg dbg_valid_insn; - - reg [63:0] cached_ascii_instr; - reg [31:0] cached_insn_imm; - reg [31:0] cached_insn_opcode; - reg [ 4:0] cached_insn_rs1; - reg [ 4:0] cached_insn_rs2; - reg [ 4:0] cached_insn_rd; - - always @(posedge clk) begin - q_ascii_instr <= dbg_ascii_instr; - q_insn_imm <= dbg_insn_imm; - q_insn_opcode <= dbg_insn_opcode; - q_insn_rs1 <= dbg_insn_rs1; - q_insn_rs2 <= dbg_insn_rs2; - q_insn_rd <= dbg_insn_rd; - dbg_next <= launch_next_insn; - - if (!resetn || trap) dbg_valid_insn <= 0; - else if (launch_next_insn) dbg_valid_insn <= 1; - - if (decoder_trigger_q) begin - cached_ascii_instr <= new_ascii_instr; - cached_insn_imm <= decoded_imm; - if (&next_insn_opcode[1:0]) cached_insn_opcode <= next_insn_opcode; - else cached_insn_opcode <= {16'b0, next_insn_opcode[15:0]}; - cached_insn_rs1 <= decoded_rs1; - cached_insn_rs2 <= decoded_rs2; - cached_insn_rd <= decoded_rd; - end - - if (launch_next_insn) begin - dbg_insn_addr <= next_pc; - end - end - - always @* begin - dbg_ascii_instr = q_ascii_instr; - dbg_insn_imm = q_insn_imm; - dbg_insn_opcode = q_insn_opcode; - dbg_insn_rs1 = q_insn_rs1; - dbg_insn_rs2 = q_insn_rs2; - dbg_insn_rd = q_insn_rd; - - if (dbg_next) begin - if (decoder_pseudo_trigger_q) begin - dbg_ascii_instr = cached_ascii_instr; - dbg_insn_imm = cached_insn_imm; - dbg_insn_opcode = cached_insn_opcode; - dbg_insn_rs1 = cached_insn_rs1; - dbg_insn_rs2 = cached_insn_rs2; - dbg_insn_rd = cached_insn_rd; - end else begin - dbg_ascii_instr = new_ascii_instr; - if (&next_insn_opcode[1:0]) dbg_insn_opcode = next_insn_opcode; - else dbg_insn_opcode = {16'b0, next_insn_opcode[15:0]}; - dbg_insn_imm = decoded_imm; - dbg_insn_rs1 = decoded_rs1; - dbg_insn_rs2 = decoded_rs2; - dbg_insn_rd = decoded_rd; - end - end - end - -`ifdef DEBUGASM - always @(posedge clk) begin - if (dbg_next) begin - $display("debugasm %x %x %s", dbg_insn_addr, dbg_insn_opcode, - dbg_ascii_instr ? dbg_ascii_instr : "*"); - end - end -`endif - -`ifdef DEBUG - always @(posedge clk) begin - if (dbg_next) begin - if (&dbg_insn_opcode[1:0]) - $display( - "DECODE: 0x%08x 0x%08x %-0s", - dbg_insn_addr, - dbg_insn_opcode, - dbg_ascii_instr ? dbg_ascii_instr : "UNKNOWN" - ); - else - $display( - "DECODE: 0x%08x 0x%04x %-0s", - dbg_insn_addr, - dbg_insn_opcode[15:0], - dbg_ascii_instr ? dbg_ascii_instr : "UNKNOWN" - ); - end - end -`endif - - always @(posedge clk) begin - is_lui_auipc_jal <= |{instr_lui, instr_auipc, instr_jal}; - is_lui_auipc_jal_jalr_addi_add_sub <= |{instr_lui, instr_auipc, instr_jal, instr_jalr, instr_addi, instr_add, instr_sub}; - is_slti_blt_slt <= |{instr_slti, instr_blt, instr_slt}; - is_sltiu_bltu_sltu <= |{instr_sltiu, instr_bltu, instr_sltu}; - is_lbu_lhu_lw <= |{instr_lbu, instr_lhu, instr_lw}; - is_compare <= |{is_beq_bne_blt_bge_bltu_bgeu, instr_slti, instr_slt, instr_sltiu, instr_sltu}; - - if (mem_do_rinst && mem_done) begin - instr_lui <= mem_rdata_latched[6:0] == 7'b0110111; - instr_auipc <= mem_rdata_latched[6:0] == 7'b0010111; - instr_jal <= mem_rdata_latched[6:0] == 7'b1101111; - instr_jalr <= mem_rdata_latched[6:0] == 7'b1100111 && mem_rdata_latched[14:12] == 3'b000; - instr_retirq <= mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ; - instr_waitirq <= mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000100 && ENABLE_IRQ; - - is_beq_bne_blt_bge_bltu_bgeu <= mem_rdata_latched[6:0] == 7'b1100011; - is_lb_lh_lw_lbu_lhu <= mem_rdata_latched[6:0] == 7'b0000011; - is_sb_sh_sw <= mem_rdata_latched[6:0] == 7'b0100011; - is_alu_reg_imm <= mem_rdata_latched[6:0] == 7'b0010011; - is_alu_reg_reg <= mem_rdata_latched[6:0] == 7'b0110011; - - { decoded_imm_uj[31:20], decoded_imm_uj[10:1], decoded_imm_uj[11], decoded_imm_uj[19:12], decoded_imm_uj[0] } <= $signed( - {mem_rdata_latched[31:12], 1'b0} - ); - - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= mem_rdata_latched[19:15]; - decoded_rs2 <= mem_rdata_latched[24:20]; - - if (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000000 && ENABLE_IRQ && ENABLE_IRQ_QREGS) - decoded_rs1[regindex_bits-1] <= 1; // instr_getq - - if (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ) - decoded_rs1 <= ENABLE_IRQ_QREGS ? irqregs_offset : 3; // instr_retirq - - compressed_instr <= 0; - if (COMPRESSED_ISA && mem_rdata_latched[1:0] != 2'b11) begin - compressed_instr <= 1; - decoded_rd <= 0; - decoded_rs1 <= 0; - decoded_rs2 <= 0; - - { decoded_imm_uj[31:11], decoded_imm_uj[4], decoded_imm_uj[9:8], decoded_imm_uj[10], decoded_imm_uj[6], - decoded_imm_uj[7], decoded_imm_uj[3:1], decoded_imm_uj[5], decoded_imm_uj[0] } <= $signed( - {mem_rdata_latched[12:2], 1'b0} - ); - - case (mem_rdata_latched[1:0]) - 2'b00: begin // Quadrant 0 - case (mem_rdata_latched[15:13]) - 3'b000: begin // C.ADDI4SPN - is_alu_reg_imm <= |mem_rdata_latched[12:5]; - decoded_rs1 <= 2; - decoded_rd <= 8 + mem_rdata_latched[4:2]; - end - 3'b010: begin // C.LW - is_lb_lh_lw_lbu_lhu <= 1; - decoded_rs1 <= 8 + mem_rdata_latched[9:7]; - decoded_rd <= 8 + mem_rdata_latched[4:2]; - end - 3'b110: begin // C.SW - is_sb_sh_sw <= 1; - decoded_rs1 <= 8 + mem_rdata_latched[9:7]; - decoded_rs2 <= 8 + mem_rdata_latched[4:2]; - end - default: ; - endcase - end - 2'b01: begin // Quadrant 1 - case (mem_rdata_latched[15:13]) - 3'b000: begin // C.NOP / C.ADDI - is_alu_reg_imm <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= mem_rdata_latched[11:7]; - end - 3'b001: begin // C.JAL - instr_jal <= 1; - decoded_rd <= 1; - end - 3'b010: begin // C.LI - is_alu_reg_imm <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= 0; - end - 3'b011: begin - if (mem_rdata_latched[12] || mem_rdata_latched[6:2]) begin - if (mem_rdata_latched[11:7] == 2) begin // C.ADDI16SP - is_alu_reg_imm <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= mem_rdata_latched[11:7]; - end else begin // C.LUI - instr_lui <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= 0; - end - end - end - 3'b100: begin - if (!mem_rdata_latched[11] && !mem_rdata_latched[12]) begin // C.SRLI, C.SRAI - is_alu_reg_imm <= 1; - decoded_rd <= 8 + mem_rdata_latched[9:7]; - decoded_rs1 <= 8 + mem_rdata_latched[9:7]; - decoded_rs2 <= {mem_rdata_latched[12], mem_rdata_latched[6:2]}; - end - if (mem_rdata_latched[11:10] == 2'b10) begin // C.ANDI - is_alu_reg_imm <= 1; - decoded_rd <= 8 + mem_rdata_latched[9:7]; - decoded_rs1 <= 8 + mem_rdata_latched[9:7]; - end - if (mem_rdata_latched[12:10] == 3'b011) begin // C.SUB, C.XOR, C.OR, C.AND - is_alu_reg_reg <= 1; - decoded_rd <= 8 + mem_rdata_latched[9:7]; - decoded_rs1 <= 8 + mem_rdata_latched[9:7]; - decoded_rs2 <= 8 + mem_rdata_latched[4:2]; - end - end - 3'b101: begin // C.J - instr_jal <= 1; - end - 3'b110: begin // C.BEQZ - is_beq_bne_blt_bge_bltu_bgeu <= 1; - decoded_rs1 <= 8 + mem_rdata_latched[9:7]; - decoded_rs2 <= 0; - end - default: begin // C.BNEZ - is_beq_bne_blt_bge_bltu_bgeu <= 1; - decoded_rs1 <= 8 + mem_rdata_latched[9:7]; - decoded_rs2 <= 0; - end - endcase - end - 2'b10: begin // Quadrant 2 - case (mem_rdata_latched[15:13]) - 3'b000: begin // C.SLLI - if (!mem_rdata_latched[12]) begin - is_alu_reg_imm <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= mem_rdata_latched[11:7]; - decoded_rs2 <= {mem_rdata_latched[12], mem_rdata_latched[6:2]}; - end - end - 3'b010: begin // C.LWSP - if (mem_rdata_latched[11:7]) begin - is_lb_lh_lw_lbu_lhu <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= 2; - end - end - 3'b100: begin - if (mem_rdata_latched[12] == 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JR - instr_jalr <= 1; - decoded_rd <= 0; - decoded_rs1 <= mem_rdata_latched[11:7]; - end - if (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] != 0) begin // C.MV - is_alu_reg_reg <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= 0; - decoded_rs2 <= mem_rdata_latched[6:2]; - end - if (mem_rdata_latched[12] != 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JALR - instr_jalr <= 1; - decoded_rd <= 1; - decoded_rs1 <= mem_rdata_latched[11:7]; - end - if (mem_rdata_latched[12] != 0 && mem_rdata_latched[6:2] != 0) begin // C.ADD - is_alu_reg_reg <= 1; - decoded_rd <= mem_rdata_latched[11:7]; - decoded_rs1 <= mem_rdata_latched[11:7]; - decoded_rs2 <= mem_rdata_latched[6:2]; - end - end - 3'b110: begin // C.SWSP - is_sb_sh_sw <= 1; - decoded_rs1 <= 2; - decoded_rs2 <= mem_rdata_latched[6:2]; - end - default: ; - endcase - end - default: ; - endcase - end - end - - if (decoder_trigger && !decoder_pseudo_trigger) begin - pcpi_insn <= WITH_PCPI ? mem_rdata_q : 'bx; - - instr_beq <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b000; - instr_bne <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b001; - instr_blt <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b100; - instr_bge <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b101; - instr_bltu <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b110; - instr_bgeu <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b111; - - instr_lb <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b000; - instr_lh <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b001; - instr_lw <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b010; - instr_lbu <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b100; - instr_lhu <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b101; - - instr_sb <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b000; - instr_sh <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b001; - instr_sw <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b010; - - instr_addi <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b000; - instr_slti <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b010; - instr_sltiu <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b011; - instr_xori <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b100; - instr_ori <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b110; - instr_andi <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b111; - - instr_slli <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000; - instr_srli <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000; - instr_srai <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000; - - instr_add <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0000000; - instr_sub <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0100000; - instr_sll <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000; - instr_slt <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b010 && mem_rdata_q[31:25] == 7'b0000000; - instr_sltu <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b011 && mem_rdata_q[31:25] == 7'b0000000; - instr_xor <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b100 && mem_rdata_q[31:25] == 7'b0000000; - instr_srl <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000; - instr_sra <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000; - instr_or <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b110 && mem_rdata_q[31:25] == 7'b0000000; - instr_and <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b111 && mem_rdata_q[31:25] == 7'b0000000; - - instr_rdcycle <= ((mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000000000000010) || - (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000000100000010)) && ENABLE_COUNTERS; - instr_rdcycleh <= ((mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000000000000010) || - (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000000100000010)) && ENABLE_COUNTERS && ENABLE_COUNTERS64; - instr_rdinstr <= (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000001000000010) && ENABLE_COUNTERS; - instr_rdinstrh <= (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000001000000010) && ENABLE_COUNTERS && ENABLE_COUNTERS64; - - instr_ecall_ebreak <= ((mem_rdata_q[6:0] == 7'b1110011 && !mem_rdata_q[31:21] && !mem_rdata_q[19:7]) || - (COMPRESSED_ISA && mem_rdata_q[15:0] == 16'h9002)); - - instr_getq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000000 && ENABLE_IRQ && ENABLE_IRQ_QREGS; - instr_setq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000001 && ENABLE_IRQ && ENABLE_IRQ_QREGS; - instr_maskirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000011 && ENABLE_IRQ; - instr_timer <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000101 && ENABLE_IRQ && ENABLE_IRQ_TIMER; - - is_slli_srli_srai <= is_alu_reg_imm && |{ - mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000, - mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000, - mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000 - }; - - is_jalr_addi_slti_sltiu_xori_ori_andi <= instr_jalr || is_alu_reg_imm && |{ - mem_rdata_q[14:12] == 3'b000, - mem_rdata_q[14:12] == 3'b010, - mem_rdata_q[14:12] == 3'b011, - mem_rdata_q[14:12] == 3'b100, - mem_rdata_q[14:12] == 3'b110, - mem_rdata_q[14:12] == 3'b111 - }; - - is_sll_srl_sra <= is_alu_reg_reg && |{ - mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000, - mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000, - mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000 - }; - - is_lui_auipc_jal_jalr_addi_add_sub <= 0; - is_compare <= 0; - - //(* parallel_case *) - - if (instr_jal) decoded_imm <= decoded_imm_uj; - else if (|{instr_lui, instr_auipc}) decoded_imm <= mem_rdata_q[31:12] << 12; - else if (|{instr_jalr, is_lb_lh_lw_lbu_lhu, is_alu_reg_imm}) - decoded_imm <= $signed(mem_rdata_q[31:20]); - else if (is_beq_bne_blt_bge_bltu_bgeu) - decoded_imm <= $signed( - {mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8], 1'b0} - ); - else if (is_sb_sh_sw) decoded_imm <= $signed({mem_rdata_q[31:25], mem_rdata_q[11:7]}); - else decoded_imm <= 1'bx; - end - - if (!resetn) begin - is_beq_bne_blt_bge_bltu_bgeu <= 0; - is_compare <= 0; - - instr_beq <= 0; - instr_bne <= 0; - instr_blt <= 0; - instr_bge <= 0; - instr_bltu <= 0; - instr_bgeu <= 0; - - instr_addi <= 0; - instr_slti <= 0; - instr_sltiu <= 0; - instr_xori <= 0; - instr_ori <= 0; - instr_andi <= 0; - - instr_add <= 0; - instr_sub <= 0; - instr_sll <= 0; - instr_slt <= 0; - instr_sltu <= 0; - instr_xor <= 0; - instr_srl <= 0; - instr_sra <= 0; - instr_or <= 0; - instr_and <= 0; - end - end - - - // Main State Machine - - localparam cpu_state_trap = 8'b10000000; - localparam cpu_state_fetch = 8'b01000000; - localparam cpu_state_ld_rs1 = 8'b00100000; - localparam cpu_state_ld_rs2 = 8'b00010000; - localparam cpu_state_exec = 8'b00001000; - localparam cpu_state_shift = 8'b00000100; - localparam cpu_state_stmem = 8'b00000010; - localparam cpu_state_ldmem = 8'b00000001; - - reg [ 7:0] cpu_state; - reg [ 1:0] irq_state; - - reg [127:0] dbg_ascii_state; - - always @* begin - dbg_ascii_state = ""; - if (cpu_state == cpu_state_trap) dbg_ascii_state = "trap"; - if (cpu_state == cpu_state_fetch) dbg_ascii_state = "fetch"; - if (cpu_state == cpu_state_ld_rs1) dbg_ascii_state = "ld_rs1"; - if (cpu_state == cpu_state_ld_rs2) dbg_ascii_state = "ld_rs2"; - if (cpu_state == cpu_state_exec) dbg_ascii_state = "exec"; - if (cpu_state == cpu_state_shift) dbg_ascii_state = "shift"; - if (cpu_state == cpu_state_stmem) dbg_ascii_state = "stmem"; - if (cpu_state == cpu_state_ldmem) dbg_ascii_state = "ldmem"; - end - - reg set_mem_do_rinst; - reg set_mem_do_rdata; - reg set_mem_do_wdata; - - reg latched_store; - reg latched_stalu; - reg latched_branch; - reg latched_compr; - reg latched_trace; - reg latched_is_lu; - reg latched_is_lh; - reg latched_is_lb; - reg [regindex_bits-1:0] latched_rd; - - reg [ 31:0] current_pc; - assign next_pc = latched_store && latched_branch ? reg_out & ~1 : reg_next_pc; - - reg [ 3:0] pcpi_timeout_counter; - reg pcpi_timeout; - - reg [31:0] next_irq_pending; - reg do_waitirq; - - reg [31:0] alu_out, alu_out_q; - reg alu_out_0, alu_out_0_q; - reg alu_wait, alu_wait_2; - - reg [31:0] alu_add_sub; - reg [31:0] alu_shl, alu_shr; - reg signed [32:0] alu_shr_tmp; - reg alu_eq, alu_ltu, alu_lts; - - generate - if (TWO_CYCLE_ALU) begin : g_two_cycle_alu - always @(posedge clk) begin - alu_add_sub <= instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2; - alu_eq <= reg_op1 == reg_op2; - alu_lts <= $signed(reg_op1) < $signed(reg_op2); - alu_ltu <= reg_op1 < reg_op2; - alu_shl <= reg_op1 << reg_op2[4:0]; - alu_shr <= $signed( - {instr_sra || instr_srai ? reg_op1[31] : 1'b0, reg_op1} - ) >>> reg_op2[4:0]; - end - end else begin : g_alu - always @* begin - alu_add_sub = instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2; - alu_eq = reg_op1 == reg_op2; - alu_lts = $signed(reg_op1) < $signed(reg_op2); - alu_ltu = reg_op1 < reg_op2; - alu_shl = reg_op1 << reg_op2[4:0]; - alu_shr_tmp = $signed({(instr_sra || instr_srai) ? reg_op1[31] : 1'b0, reg_op1}) >>> - reg_op2[4:0]; - alu_shr = alu_shr_tmp[31:0]; - end - end - endgenerate - - always @* begin - alu_out_0 = 1'b0; - - if (instr_beq) alu_out_0 = alu_eq; - else if (instr_bne) alu_out_0 = !alu_eq; - else if (instr_bge) alu_out_0 = !alu_lts; - else if (instr_bgeu) alu_out_0 = !alu_ltu; - else if (is_slti_blt_slt && (!TWO_CYCLE_COMPARE || !{instr_beq,instr_bne,instr_bge,instr_bgeu})) - alu_out_0 = alu_lts; - else if (is_sltiu_bltu_sltu && (!TWO_CYCLE_COMPARE || !{instr_beq,instr_bne,instr_bge,instr_bgeu})) - alu_out_0 = alu_ltu; - - - alu_out = 'bx; - - if (is_lui_auipc_jal_jalr_addi_add_sub) alu_out = alu_add_sub; - else if (is_compare) alu_out = alu_out_0; - else if (instr_xori || instr_xor) alu_out = reg_op1 ^ reg_op2; - else if (instr_ori || instr_or) alu_out = reg_op1 | reg_op2; - else if (instr_andi || instr_and) alu_out = reg_op1 & reg_op2; - else if (BARREL_SHIFTER && (instr_sll || instr_slli)) alu_out = alu_shl; - else if (BARREL_SHIFTER && (instr_srl || instr_srli || instr_sra || instr_srai)) - alu_out = alu_shr; - - -`ifdef RISCV_FORMAL_BLACKBOX_ALU - alu_out_0 = $anyseq; - alu_out = $anyseq; -`endif - end - - reg clear_prefetched_high_word_q; - always @(posedge clk) clear_prefetched_high_word_q <= clear_prefetched_high_word; - - always @* begin - clear_prefetched_high_word = clear_prefetched_high_word_q; - if (!prefetched_high_word) clear_prefetched_high_word = 0; - if (latched_branch || irq_state || !resetn) clear_prefetched_high_word = COMPRESSED_ISA; - end - - reg cpuregs_write; - reg [ 31:0] cpuregs_wrdata; - reg [ 31:0] cpuregs_rs1; - reg [ 31:0] cpuregs_rs2; - reg [regindex_bits-1:0] decoded_rs; - - always @* begin - cpuregs_write = 0; - cpuregs_wrdata = 'bx; - - if (cpu_state == cpu_state_fetch) - if (latched_branch) begin - cpuregs_wrdata = reg_pc + (latched_compr ? 2 : 4); - cpuregs_write = 1; - end else if (latched_store && !latched_branch) begin - cpuregs_wrdata = latched_stalu ? alu_out_q : reg_out; - cpuregs_write = 1; - end else if (ENABLE_IRQ && irq_state[0]) begin - cpuregs_wrdata = reg_next_pc | latched_compr; - cpuregs_write = 1; - end else if (ENABLE_IRQ && irq_state[1]) begin - cpuregs_wrdata = irq_pending & ~irq_mask; - cpuregs_write = 1; - end - end - - always @(posedge clk) begin - if (resetn && cpuregs_write && latched_rd) cpuregs[latched_rd] <= cpuregs_wrdata; - end - - always @ ( - cpuregs[1], - cpuregs[3], - cpuregs[4], - cpuregs[5], - cpuregs[6], - cpuregs[7], - cpuregs[8], - cpuregs[9], - cpuregs[10], - cpuregs[11], - cpuregs[12], - cpuregs[13], - cpuregs[14], - cpuregs[15], - cpuregs[16], - cpuregs[17], - cpuregs[18], - cpuregs[19], - cpuregs[20], - cpuregs[21], - cpuregs[22], - cpuregs[23], - cpuregs[24], - cpuregs[25], - cpuregs[26], - cpuregs[27], - cpuregs[28], - cpuregs[29], - cpuregs[30], - cpuregs[31], - decoded_rs1, - decoded_rs2, - decoded_rs, - cpu_state, - cpu_state_ld_rs2, - cpuregs_rs1) - - begin - decoded_rs = 5'hx; - if (ENABLE_REGS_DUALPORT) begin -`ifndef RISCV_FORMAL_BLACKBOX_REGS - cpuregs_rs1 = decoded_rs1 ? cpuregs[decoded_rs1] : 0; - cpuregs_rs2 = decoded_rs2 ? cpuregs[decoded_rs2] : 0; -`else - cpuregs_rs1 = decoded_rs1 ? $anyseq : 0; - cpuregs_rs2 = decoded_rs2 ? $anyseq : 0; -`endif - end else begin - decoded_rs = (cpu_state == cpu_state_ld_rs2) ? decoded_rs2 : decoded_rs1; -`ifndef RISCV_FORMAL_BLACKBOX_REGS - cpuregs_rs1 = decoded_rs ? cpuregs[decoded_rs] : 0; -`else - cpuregs_rs1 = decoded_rs ? $anyseq : 0; -`endif - cpuregs_rs2 = cpuregs_rs1; - end - end - - assign launch_next_insn = cpu_state == cpu_state_fetch && decoder_trigger && (!ENABLE_IRQ || irq_delay || irq_active || !(irq_pending & ~irq_mask)); - - always @(posedge clk) begin - trap <= 0; - reg_sh <= 5'hx; - reg_out <= 'bx; - set_mem_do_rinst = 0; - set_mem_do_rdata = 0; - set_mem_do_wdata = 0; - - alu_out_0_q <= alu_out_0; - alu_out_q <= alu_out; - - alu_wait <= 0; - alu_wait_2 <= 0; - - if (launch_next_insn) begin - dbg_rs1val <= 'bx; - dbg_rs2val <= 'bx; - dbg_rs1val_valid <= 0; - dbg_rs2val_valid <= 0; - end - - if (WITH_PCPI && CATCH_ILLINSN) begin - if (resetn && pcpi_valid && !pcpi_int_wait) begin - if (pcpi_timeout_counter) pcpi_timeout_counter <= pcpi_timeout_counter - 1'b1; - end else pcpi_timeout_counter <= ~(4'd0); - pcpi_timeout <= !pcpi_timeout_counter; - end - - if (ENABLE_COUNTERS) begin - count_cycle <= resetn ? count_cycle + 1 : 0; - if (!ENABLE_COUNTERS64) count_cycle[63:32] <= 0; - end else begin - count_cycle <= 'bx; - count_instr <= 'bx; - end - - next_irq_pending = ENABLE_IRQ ? irq_pending & LATCHED_IRQ : 'bx; - - if (ENABLE_IRQ && ENABLE_IRQ_TIMER && timer) begin - if (timer - 1 == 0) next_irq_pending[irq_timer] = 1; - timer <= timer - 1; - end - - if (ENABLE_IRQ) begin - next_irq_pending = next_irq_pending | irq; - end - - decoder_trigger <= mem_do_rinst && mem_done; - decoder_trigger_q <= decoder_trigger; - decoder_pseudo_trigger <= 0; - decoder_pseudo_trigger_q <= decoder_pseudo_trigger; - do_waitirq <= 0; - - trace_valid <= 0; - - if (!ENABLE_TRACE) trace_data <= 'bx; - - if (!resetn) begin - reg_pc <= PROGADDR_RESET; - reg_next_pc <= PROGADDR_RESET; - if (ENABLE_COUNTERS) count_instr <= 0; - latched_store <= 0; - latched_stalu <= 0; - latched_branch <= 0; - latched_trace <= 0; - latched_is_lu <= 0; - latched_is_lh <= 0; - latched_is_lb <= 0; - pcpi_valid <= 0; - pcpi_timeout <= 0; - irq_active <= 0; - irq_delay <= 0; - irq_mask <= ~0; - next_irq_pending = 0; - irq_state <= 0; - eoi <= 0; - timer <= 0; - if (~STACKADDR) begin - latched_store <= 1; - latched_rd <= 2; - reg_out <= STACKADDR; - end - cpu_state <= cpu_state_fetch; - end else - //(* parallel_case, full_case *) - case (cpu_state) - cpu_state_trap: begin - trap <= 1; - end - - cpu_state_fetch: begin - mem_do_rinst <= !decoder_trigger && !do_waitirq; - mem_wordsize <= 0; - - current_pc = reg_next_pc; - - if (latched_branch) begin - current_pc = latched_store ? (latched_stalu ? alu_out_q : reg_out) & ~1 : reg_next_pc; - `DEBUG_($display( - "ST_RD: %2d 0x%08x, BRANCH 0x%08x", - latched_rd, - reg_pc + (latched_compr ? 2 : 4), - current_pc - )); - end else if (latched_store && !latched_branch) begin - `DEBUG_($display( - "ST_RD: %2d 0x%08x", latched_rd, latched_stalu ? alu_out_q : reg_out)); - end else if (ENABLE_IRQ && irq_state[0]) begin - current_pc = PROGADDR_IRQ; - irq_active <= 1; - mem_do_rinst <= 1; - end else if (ENABLE_IRQ && irq_state[1]) begin - eoi <= irq_pending & ~irq_mask; - next_irq_pending = next_irq_pending & irq_mask; - end - - - if (ENABLE_TRACE && latched_trace) begin - latched_trace <= 0; - trace_valid <= 1; - if (latched_branch) - trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_BRANCH | (current_pc & 32'hfffffffe); - else - trace_data <= (irq_active ? TRACE_IRQ : 0) | (latched_stalu ? alu_out_q : reg_out); - end - - reg_pc <= current_pc; - reg_next_pc <= current_pc; - - latched_store <= 0; - latched_stalu <= 0; - latched_branch <= 0; - latched_is_lu <= 0; - latched_is_lh <= 0; - latched_is_lb <= 0; - latched_rd <= decoded_rd; - latched_compr <= compressed_instr; - - if (ENABLE_IRQ && ((decoder_trigger && !irq_active && !irq_delay && |(irq_pending & ~irq_mask)) || irq_state)) begin - irq_state <= irq_state == 2'b00 ? 2'b01 : irq_state == 2'b01 ? 2'b10 : 2'b00; - latched_compr <= latched_compr; - if (ENABLE_IRQ_QREGS) latched_rd <= irqregs_offset | irq_state[0]; - else latched_rd <= irq_state[0] ? 4 : 3; - end else if (ENABLE_IRQ && (decoder_trigger || do_waitirq) && instr_waitirq) begin - if (irq_pending) begin - latched_store <= 1; - reg_out <= irq_pending; - reg_next_pc <= current_pc + (compressed_instr ? 2 : 4); - mem_do_rinst <= 1; - end else do_waitirq <= 1; - end else if (decoder_trigger) begin - `DEBUG_($display("-- %-0t", $time())); - irq_delay <= irq_active; - reg_next_pc <= current_pc + (compressed_instr ? 2 : 4); - if (ENABLE_TRACE) latched_trace <= 1; - if (ENABLE_COUNTERS) begin - count_instr <= count_instr + 1; - if (!ENABLE_COUNTERS64) count_instr[63:32] <= 0; - end - if (instr_jal) begin - mem_do_rinst <= 1; - reg_next_pc <= current_pc + decoded_imm_uj; - latched_branch <= 1; - end else begin - mem_do_rinst <= 0; - mem_do_prefetch <= !instr_jalr && !instr_retirq; - cpu_state <= cpu_state_ld_rs1; - end - end - end - - cpu_state_ld_rs1: begin - reg_op1 <= 'bx; - reg_op2 <= 'bx; - - if ((CATCH_ILLINSN || WITH_PCPI) && instr_trap) begin - if (WITH_PCPI) begin - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_op1 <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - if (ENABLE_REGS_DUALPORT) begin - pcpi_valid <= 1; - `DEBUG_($display("LD_RS2: %2d 0x%08x", decoded_rs2, cpuregs_rs2)); - reg_sh <= cpuregs_rs2[4:0]; - reg_op2 <= cpuregs_rs2; - dbg_rs2val <= cpuregs_rs2; - dbg_rs2val_valid <= 1; - if (pcpi_int_ready) begin - mem_do_rinst <= 1; - pcpi_valid <= 0; - reg_out <= pcpi_int_rd; - latched_store <= pcpi_int_wr; - cpu_state <= cpu_state_fetch; - end else if (CATCH_ILLINSN && (pcpi_timeout || instr_ecall_ebreak)) begin - pcpi_valid <= 0; - `DEBUG_($display("EBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc)); - if (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin - next_irq_pending[irq_ebreak] = 1; - cpu_state <= cpu_state_fetch; - end else cpu_state <= cpu_state_trap; - end - end else begin - cpu_state <= cpu_state_ld_rs2; - end - end else begin - `DEBUG_($display("EBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc)); - if (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin - next_irq_pending[irq_ebreak] = 1; - cpu_state <= cpu_state_fetch; - end else cpu_state <= cpu_state_trap; - end - end else if (ENABLE_COUNTERS && is_rdcycle_rdcycleh_rdinstr_rdinstrh) begin - - if (instr_rdcycle) reg_out <= count_cycle[31:0]; - else if (instr_rdcycleh && ENABLE_COUNTERS64) reg_out <= count_cycle[63:32]; - else if (instr_rdinstr) reg_out <= count_instr[31:0]; - else if (instr_rdinstrh && ENABLE_COUNTERS64) reg_out <= count_instr[63:32]; - - latched_store <= 1; - cpu_state <= cpu_state_fetch; - end else if (is_lui_auipc_jal) begin - reg_op1 <= instr_lui ? 0 : reg_pc; - reg_op2 <= decoded_imm; - if (TWO_CYCLE_ALU) alu_wait <= 1; - else mem_do_rinst <= mem_do_prefetch; - cpu_state <= cpu_state_exec; - end else if (ENABLE_IRQ && ENABLE_IRQ_QREGS && instr_getq) begin - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_out <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - latched_store <= 1; - cpu_state <= cpu_state_fetch; - end else if (ENABLE_IRQ && ENABLE_IRQ_QREGS && instr_setq) begin - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_out <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - latched_rd <= latched_rd | irqregs_offset; - latched_store <= 1; - cpu_state <= cpu_state_fetch; - end else if (ENABLE_IRQ && instr_retirq) begin - eoi <= 0; - irq_active <= 0; - latched_branch <= 1; - latched_store <= 1; - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_out <= CATCH_MISALIGN ? (cpuregs_rs1 & 32'hfffffffe) : cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - cpu_state <= cpu_state_fetch; - end else if (ENABLE_IRQ && instr_maskirq) begin - latched_store <= 1; - reg_out <= irq_mask; - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - irq_mask <= cpuregs_rs1 | MASKED_IRQ; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - cpu_state <= cpu_state_fetch; - end else if (ENABLE_IRQ && ENABLE_IRQ_TIMER && instr_timer) begin - latched_store <= 1; - reg_out <= timer; - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - timer <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - cpu_state <= cpu_state_fetch; - end else if (is_lb_lh_lw_lbu_lhu && !instr_trap) begin - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_op1 <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - cpu_state <= cpu_state_ldmem; - mem_do_rinst <= 1; - end else if (is_slli_srli_srai && !BARREL_SHIFTER) begin - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_op1 <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - reg_sh <= decoded_rs2; - cpu_state <= cpu_state_shift; - end - else if (is_jalr_addi_slti_sltiu_xori_ori_andi || is_slli_srli_srai && BARREL_SHIFTER) begin - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_op1 <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - reg_op2 <= is_slli_srli_srai && BARREL_SHIFTER ? decoded_rs2 : decoded_imm; - if (TWO_CYCLE_ALU) alu_wait <= 1; - else mem_do_rinst <= mem_do_prefetch; - cpu_state <= cpu_state_exec; - end else begin - `DEBUG_($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1)); - reg_op1 <= cpuregs_rs1; - dbg_rs1val <= cpuregs_rs1; - dbg_rs1val_valid <= 1; - if (ENABLE_REGS_DUALPORT) begin - `DEBUG_($display("LD_RS2: %2d 0x%08x", decoded_rs2, cpuregs_rs2)); - reg_sh <= cpuregs_rs2[4:0]; - reg_op2 <= cpuregs_rs2; - dbg_rs2val <= cpuregs_rs2; - dbg_rs2val_valid <= 1; - - if (is_sb_sh_sw) begin - cpu_state <= cpu_state_stmem; - mem_do_rinst <= 1; - end else if (is_sll_srl_sra && !BARREL_SHIFTER) begin - cpu_state <= cpu_state_shift; - end else begin - if (TWO_CYCLE_ALU || (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu)) begin - alu_wait_2 <= TWO_CYCLE_ALU && (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu); - alu_wait <= 1; - end else mem_do_rinst <= mem_do_prefetch; - cpu_state <= cpu_state_exec; - end - - end else cpu_state <= cpu_state_ld_rs2; - end - end - - cpu_state_ld_rs2: begin - `DEBUG_($display("LD_RS2: %2d 0x%08x", decoded_rs2, cpuregs_rs2)); - reg_sh <= cpuregs_rs2[4:0]; - reg_op2 <= cpuregs_rs2; - dbg_rs2val <= cpuregs_rs2; - dbg_rs2val_valid <= 1; - - if (WITH_PCPI && instr_trap) begin - pcpi_valid <= 1; - if (pcpi_int_ready) begin - mem_do_rinst <= 1; - pcpi_valid <= 0; - reg_out <= pcpi_int_rd; - latched_store <= pcpi_int_wr; - cpu_state <= cpu_state_fetch; - end else if (CATCH_ILLINSN && (pcpi_timeout || instr_ecall_ebreak)) begin - pcpi_valid <= 0; - `DEBUG_($display("EBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc)); - if (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin - next_irq_pending[irq_ebreak] = 1; - cpu_state <= cpu_state_fetch; - end else cpu_state <= cpu_state_trap; - end - end else if (is_sb_sh_sw) begin - cpu_state <= cpu_state_stmem; - mem_do_rinst <= 1; - end else if (is_sll_srl_sra && !BARREL_SHIFTER) begin - cpu_state <= cpu_state_shift; - end else begin - if (TWO_CYCLE_ALU || (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu)) begin - alu_wait_2 <= TWO_CYCLE_ALU && (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu); - alu_wait <= 1; - end else mem_do_rinst <= mem_do_prefetch; - cpu_state <= cpu_state_exec; - end - end - - cpu_state_exec: begin - reg_out <= reg_pc + decoded_imm; - if ((TWO_CYCLE_ALU || TWO_CYCLE_COMPARE) && (alu_wait || alu_wait_2)) begin - mem_do_rinst <= mem_do_prefetch && !alu_wait_2; - alu_wait <= alu_wait_2; - end else if (is_beq_bne_blt_bge_bltu_bgeu) begin - latched_rd <= 0; - latched_store <= TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0; - latched_branch <= TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0; - if (mem_done) cpu_state <= cpu_state_fetch; - if (TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0) begin - decoder_trigger <= 0; - set_mem_do_rinst = 1; - end - end else begin - latched_branch <= instr_jalr; - latched_store <= 1; - latched_stalu <= 1; - cpu_state <= cpu_state_fetch; - end - end - - cpu_state_shift: begin - latched_store <= 1; - if (reg_sh == 0) begin - reg_out <= reg_op1; - mem_do_rinst <= mem_do_prefetch; - cpu_state <= cpu_state_fetch; - end else if (TWO_STAGE_SHIFT && reg_sh >= 4) begin - - if (instr_slli || instr_sll) reg_op1 <= reg_op1 << 4; - else if (instr_srli || instr_srl) reg_op1 <= reg_op1 >> 4; - else if (instr_srai || instr_sra) reg_op1 <= $signed(reg_op1) >>> 4; - - reg_sh <= reg_sh - 5'd4; - end else begin - - if (instr_slli || instr_sll) reg_op1 <= reg_op1 << 1; - else if (instr_srli || instr_srl) reg_op1 <= reg_op1 >> 1; - else if (instr_srai || instr_sra) reg_op1 <= $signed(reg_op1) >>> 1; - - reg_sh <= reg_sh - 1'b1; - end - end - - cpu_state_stmem: begin - if (ENABLE_TRACE) reg_out <= reg_op2; - if (!mem_do_prefetch || mem_done) begin - if (!mem_do_wdata) begin - - if (instr_sb) mem_wordsize <= 2; - else if (instr_sh) mem_wordsize <= 1; - else if (instr_sw) mem_wordsize <= 0; - - if (ENABLE_TRACE) begin - trace_valid <= 1; - trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | ((reg_op1 + decoded_imm) & 32'hffffffff); - end - reg_op1 <= reg_op1 + decoded_imm; - set_mem_do_wdata = 1; - end - if (!mem_do_prefetch && mem_done) begin - cpu_state <= cpu_state_fetch; - decoder_trigger <= 1; - decoder_pseudo_trigger <= 1; - end - end - end - - cpu_state_ldmem: begin - latched_store <= 1; - if (!mem_do_prefetch || mem_done) begin - if (!mem_do_rdata) begin - - if (instr_lb || instr_lbu) mem_wordsize <= 2; - else if (instr_lh || instr_lhu) mem_wordsize <= 1; - else if (instr_lw) mem_wordsize <= 0; - - latched_is_lu <= is_lbu_lhu_lw; - latched_is_lh <= instr_lh; - latched_is_lb <= instr_lb; - if (ENABLE_TRACE) begin - trace_valid <= 1; - trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | ((reg_op1 + decoded_imm) & 32'hffffffff); - end - reg_op1 <= reg_op1 + decoded_imm; - set_mem_do_rdata = 1; - end - if (!mem_do_prefetch && mem_done) begin - - if (latched_is_lu) reg_out <= mem_rdata_word; - else if (latched_is_lh) reg_out <= $signed(mem_rdata_word[15:0]); - else if (latched_is_lb) reg_out <= $signed(mem_rdata_word[7:0]); - - decoder_trigger <= 1; - decoder_pseudo_trigger <= 1; - cpu_state <= cpu_state_fetch; - end - end - end - default: ; - endcase - - if (CATCH_MISALIGN && resetn && (mem_do_rdata || mem_do_wdata)) begin - if (mem_wordsize == 0 && reg_op1[1:0] != 0) begin - `DEBUG_($display("MISALIGNED WORD: 0x%08x", reg_op1)); - if (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin - next_irq_pending[irq_buserror] = 1; - end else cpu_state <= cpu_state_trap; - end - if (mem_wordsize == 1 && reg_op1[0] != 0) begin - `DEBUG_($display("MISALIGNED HALFWORD: 0x%08x", reg_op1)); - if (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin - next_irq_pending[irq_buserror] = 1; - end else cpu_state <= cpu_state_trap; - end - end - if (CATCH_MISALIGN && resetn && mem_do_rinst && (COMPRESSED_ISA ? reg_pc[0] : |reg_pc[1:0])) begin - `DEBUG_($display("MISALIGNED INSTRUCTION: 0x%08x", reg_pc)); - if (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin - next_irq_pending[irq_buserror] = 1; - end else cpu_state <= cpu_state_trap; - end - if (!CATCH_ILLINSN && decoder_trigger_q && !decoder_pseudo_trigger_q && instr_ecall_ebreak) begin - cpu_state <= cpu_state_trap; - end - - if (!resetn || mem_done) begin - mem_do_prefetch <= 0; - mem_do_rinst <= 0; - mem_do_rdata <= 0; - mem_do_wdata <= 0; - end - - if (set_mem_do_rinst) mem_do_rinst <= 1; - if (set_mem_do_rdata) mem_do_rdata <= 1; - if (set_mem_do_wdata) mem_do_wdata <= 1; - - irq_pending <= next_irq_pending & ~MASKED_IRQ; - - if (!CATCH_MISALIGN) begin - if (COMPRESSED_ISA) begin - reg_pc[0] <= 0; - reg_next_pc[0] <= 0; - end else begin - reg_pc[1:0] <= 0; - reg_next_pc[1:0] <= 0; - end - end - current_pc = 'bx; - end - -`ifdef RISCV_FORMAL - reg dbg_irq_call; - reg dbg_irq_enter; - reg [31:0] dbg_irq_ret; - always @(posedge clk) begin - rvfi_valid <= resetn && (launch_next_insn || trap) && dbg_valid_insn; - rvfi_order <= resetn ? rvfi_order + rvfi_valid : 0; - - rvfi_insn <= dbg_insn_opcode; - rvfi_rs1_addr <= dbg_rs1val_valid ? dbg_insn_rs1 : 0; - rvfi_rs2_addr <= dbg_rs2val_valid ? dbg_insn_rs2 : 0; - rvfi_pc_rdata <= dbg_insn_addr; - rvfi_rs1_rdata <= dbg_rs1val_valid ? dbg_rs1val : 0; - rvfi_rs2_rdata <= dbg_rs2val_valid ? dbg_rs2val : 0; - rvfi_trap <= trap; - rvfi_halt <= trap; - rvfi_intr <= dbg_irq_enter; - rvfi_mode <= 3; - - if (!resetn) begin - dbg_irq_call <= 0; - dbg_irq_enter <= 0; - end else if (rvfi_valid) begin - dbg_irq_call <= 0; - dbg_irq_enter <= dbg_irq_call; - end else if (irq_state == 1) begin - dbg_irq_call <= 1; - dbg_irq_ret <= next_pc; - end - - if (!resetn) begin - rvfi_rd_addr <= 0; - rvfi_rd_wdata <= 0; - end else if (cpuregs_write && !irq_state) begin - rvfi_rd_addr <= latched_rd; - rvfi_rd_wdata <= latched_rd ? cpuregs_wrdata : 0; - end else if (rvfi_valid) begin - rvfi_rd_addr <= 0; - rvfi_rd_wdata <= 0; - end - - casez (dbg_insn_opcode) - 32'b0000000_?????_000??_???_?????_0001011: begin // getq - rvfi_rs1_addr <= 0; - rvfi_rs1_rdata <= 0; - end - 32'b0000001_?????_?????_???_000??_0001011: begin // setq - rvfi_rd_addr <= 0; - rvfi_rd_wdata <= 0; - end - 32'b0000010_?????_00000_???_00000_0001011: begin // retirq - rvfi_rs1_addr <= 0; - rvfi_rs1_rdata <= 0; - end - default: ; - endcase - - if (!dbg_irq_call) begin - if (dbg_mem_instr) begin - rvfi_mem_addr <= 0; - rvfi_mem_rmask <= 0; - rvfi_mem_wmask <= 0; - rvfi_mem_rdata <= 0; - rvfi_mem_wdata <= 0; - end else if (dbg_mem_valid && dbg_mem_ready) begin - rvfi_mem_addr <= dbg_mem_addr; - rvfi_mem_rmask <= dbg_mem_wstrb ? 0 : ~0; - rvfi_mem_wmask <= dbg_mem_wstrb; - rvfi_mem_rdata <= dbg_mem_rdata; - rvfi_mem_wdata <= dbg_mem_wdata; - end - end - end - - always @* begin - rvfi_pc_wdata = dbg_irq_call ? dbg_irq_ret : dbg_insn_addr; - end -`endif - - // Formal Verification -`ifdef FORMAL - reg [3:0] last_mem_nowait; - always @(posedge clk) last_mem_nowait <= {last_mem_nowait, mem_ready || !mem_valid}; - - // stall the memory interface for max 4 cycles - restrict property (|last_mem_nowait || mem_ready || !mem_valid); - - // resetn low in first cycle, after that resetn high - restrict property (resetn != $initstate); - - // this just makes it much easier to read traces. uncomment as needed. - // assume property (mem_valid || !mem_ready); - - reg ok; - always @* begin - if (resetn) begin - // instruction fetches are read-only - if (mem_valid && mem_instr) ASSERT_(mem_wstrb == 0); - - // cpu_state must be valid - ok = 0; - if (cpu_state == cpu_state_trap) ok = 1; - if (cpu_state == cpu_state_fetch) ok = 1; - if (cpu_state == cpu_state_ld_rs1) ok = 1; - if (cpu_state == cpu_state_ld_rs2) ok = !ENABLE_REGS_DUALPORT; - if (cpu_state == cpu_state_exec) ok = 1; - if (cpu_state == cpu_state_shift) ok = 1; - if (cpu_state == cpu_state_stmem) ok = 1; - if (cpu_state == cpu_state_ldmem) ok = 1; - ASSERT_(ok); - end - end - - reg last_mem_la_read = 0; - reg last_mem_la_write = 0; - reg [31:0] last_mem_la_addr; - reg [31:0] last_mem_la_wdata; - reg [ 3:0] last_mem_la_wstrb = 0; - - always @(posedge clk) begin - last_mem_la_read <= mem_la_read; - last_mem_la_write <= mem_la_write; - last_mem_la_addr <= mem_la_addr; - last_mem_la_wdata <= mem_la_wdata; - last_mem_la_wstrb <= mem_la_wstrb; - - if (last_mem_la_read) begin - ASSERT_(mem_valid); - ASSERT_(mem_addr == last_mem_la_addr); - ASSERT_(mem_wstrb == 0); - end - if (last_mem_la_write) begin - ASSERT_(mem_valid); - ASSERT_(mem_addr == last_mem_la_addr); - ASSERT_(mem_wdata == last_mem_la_wdata); - ASSERT_(mem_wstrb == last_mem_la_wstrb); - end - if (mem_la_read || mem_la_write) begin - ASSERT_(!mem_valid || mem_ready); - end - end -`endif -endmodule // picorv32 - - -/*************************************************************** - * picorv32_pcpi_mul - ***************************************************************/ - -module picorv32_pcpi_mul #( - parameter STEPS_AT_ONCE = 1, - parameter CARRY_CHAIN = 4 -) ( - input clk, - resetn, - - input pcpi_valid, - input [31:0] pcpi_insn, - input [31:0] pcpi_rs1, - input [31:0] pcpi_rs2, - output reg pcpi_wr, - output reg [31:0] pcpi_rd, - output reg pcpi_wait, - output reg pcpi_ready -); - reg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu; - wire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu}; - wire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu}; - wire instr_rs1_signed = |{instr_mulh, instr_mulhsu}; - wire instr_rs2_signed = |{instr_mulh}; - - reg pcpi_wait_q; - wire mul_start = pcpi_wait && !pcpi_wait_q; - - always @(posedge clk) begin - instr_mul <= 0; - instr_mulh <= 0; - instr_mulhsu <= 0; - instr_mulhu <= 0; - - if (resetn && pcpi_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin - case (pcpi_insn[14:12]) - 3'b000: instr_mul <= 1; - 3'b001: instr_mulh <= 1; - 3'b010: instr_mulhsu <= 1; - 3'b011: instr_mulhu <= 1; - default: ; - endcase - end - - pcpi_wait <= instr_any_mul; - pcpi_wait_q <= pcpi_wait; - end - - reg [63:0] rs1, rs2, rd, rdx; - reg [63:0] next_rs1, next_rs2, this_rs2; - reg [63:0] next_rd, next_rdx, next_rdt; - reg [6:0] mul_counter; - reg mul_waiting; - reg mul_finish; - integer i, j; - - // carry save accumulator - always @* begin - next_rd = rd; - next_rdx = rdx; - next_rs1 = rs1; - next_rs2 = rs2; - - for (i = 0; i < STEPS_AT_ONCE; i = i + 1) begin - this_rs2 = next_rs1[0] ? next_rs2 : 0; - if (CARRY_CHAIN == 0) begin - next_rdt = next_rd ^ next_rdx ^ this_rs2; - next_rdx = ((next_rd & next_rdx) | (next_rd & this_rs2) | (next_rdx & this_rs2)) << 1; - next_rd = next_rdt; - end else begin - next_rdt = 0; - for (j = 0; j < 64; j = j + CARRY_CHAIN) - {next_rdt[j+CARRY_CHAIN-1], next_rd[j +: CARRY_CHAIN]} = - next_rd[j +: CARRY_CHAIN] + next_rdx[j +: CARRY_CHAIN] + this_rs2[j +: CARRY_CHAIN]; - next_rdx = next_rdt << 1; - end - next_rs1 = next_rs1 >> 1; - next_rs2 = next_rs2 << 1; - end - end - - always @(posedge clk) begin - mul_finish <= 0; - if (!resetn) begin - mul_waiting <= 1; - end else if (mul_waiting) begin - if (instr_rs1_signed) rs1 <= $signed(pcpi_rs1); - else rs1 <= $unsigned(pcpi_rs1); - - if (instr_rs2_signed) rs2 <= $signed(pcpi_rs2); - else rs2 <= $unsigned(pcpi_rs2); - - rd <= 0; - rdx <= 0; - mul_counter <= (instr_any_mulh ? 63 - STEPS_AT_ONCE : 31 - STEPS_AT_ONCE); - mul_waiting <= !mul_start; - end else begin - rd <= next_rd; - rdx <= next_rdx; - rs1 <= next_rs1; - rs2 <= next_rs2; - - mul_counter <= mul_counter - STEPS_AT_ONCE; - if (mul_counter[6]) begin - mul_finish <= 1; - mul_waiting <= 1; - end - end - end - - always @(posedge clk) begin - pcpi_wr <= 0; - pcpi_ready <= 0; - if (mul_finish && resetn) begin - pcpi_wr <= 1; - pcpi_ready <= 1; - pcpi_rd <= instr_any_mulh ? rd >> 32 : rd; - end - end -endmodule - -module picorv32_pcpi_fast_mul #( - parameter EXTRA_MUL_FFS = 0, - parameter EXTRA_INSN_FFS = 0, - parameter MUL_CLKGATE = 0 -) ( - input clk, - resetn, - - input pcpi_valid, - input [31:0] pcpi_insn, - input [31:0] pcpi_rs1, - input [31:0] pcpi_rs2, - output pcpi_wr, - output [31:0] pcpi_rd, - output pcpi_wait, - output pcpi_ready -); - reg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu; - wire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu}; - wire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu}; - wire instr_rs1_signed = |{instr_mulh, instr_mulhsu}; - wire instr_rs2_signed = |{instr_mulh}; - - reg shift_out; - reg [3:0] active; - reg [32:0] rs1, rs2, rs1_q, rs2_q; - reg [63:0] rd, rd_q; - - wire pcpi_insn_valid = pcpi_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001; - reg pcpi_insn_valid_q; - - always @* begin - instr_mul = 0; - instr_mulh = 0; - instr_mulhsu = 0; - instr_mulhu = 0; - - if (resetn && (EXTRA_INSN_FFS ? pcpi_insn_valid_q : pcpi_insn_valid)) begin - case (pcpi_insn[14:12]) - 3'b000: instr_mul = 1; - 3'b001: instr_mulh = 1; - 3'b010: instr_mulhsu = 1; - 3'b011: instr_mulhu = 1; - default: ; - endcase - end - end - - always @(posedge clk) begin - pcpi_insn_valid_q <= pcpi_insn_valid; - if (!MUL_CLKGATE || active[0]) begin - rs1_q <= rs1; - rs2_q <= rs2; - end - if (!MUL_CLKGATE || active[1]) begin - rd <= $signed(EXTRA_MUL_FFS ? rs1_q : rs1) * $signed(EXTRA_MUL_FFS ? rs2_q : rs2); - end - if (!MUL_CLKGATE || active[2]) begin - rd_q <= rd; - end - end - - always @(posedge clk) begin - if (instr_any_mul && !(EXTRA_MUL_FFS ? active[3:0] : active[1:0])) begin - if (instr_rs1_signed) rs1 <= $signed(pcpi_rs1); - else rs1 <= $unsigned(pcpi_rs1); - - if (instr_rs2_signed) rs2 <= $signed(pcpi_rs2); - else rs2 <= $unsigned(pcpi_rs2); - active[0] <= 1; - end else begin - active[0] <= 0; - end - - active[3:1] <= active[2:0]; - shift_out <= instr_any_mulh; - - if (!resetn) active <= 0; - end - - assign pcpi_wr = active[EXTRA_MUL_FFS?3 : 1]; - assign pcpi_wait = 0; - assign pcpi_ready = active[EXTRA_MUL_FFS?3 : 1]; -`ifdef RISCV_FORMAL_ALTOPS - assign pcpi_rd = - instr_mul ? (pcpi_rs1 + pcpi_rs2) ^ 32'h5876063e : - instr_mulh ? (pcpi_rs1 + pcpi_rs2) ^ 32'hf6583fb7 : - instr_mulhsu ? (pcpi_rs1 - pcpi_rs2) ^ 32'hecfbe137 : - instr_mulhu ? (pcpi_rs1 + pcpi_rs2) ^ 32'h949ce5e8 : 1'bx; -`else - assign pcpi_rd = shift_out ? (EXTRA_MUL_FFS ? rd_q : rd) >> 32 : (EXTRA_MUL_FFS ? rd_q : rd); -`endif -endmodule - - -/*************************************************************** - * picorv32_pcpi_div - ***************************************************************/ - -module picorv32_pcpi_div ( - input clk, - resetn, - - input pcpi_valid, - input [31:0] pcpi_insn, - input [31:0] pcpi_rs1, - input [31:0] pcpi_rs2, - output reg pcpi_wr, - output reg [31:0] pcpi_rd, - output reg pcpi_wait, - output reg pcpi_ready -); - reg instr_div, instr_divu, instr_rem, instr_remu; - wire instr_any_div_rem = |{instr_div, instr_divu, instr_rem, instr_remu}; - - reg pcpi_wait_q; - wire start = pcpi_wait && !pcpi_wait_q; - - always @(posedge clk) begin - instr_div <= 0; - instr_divu <= 0; - instr_rem <= 0; - instr_remu <= 0; - - if (resetn && pcpi_valid && !pcpi_ready && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin - case (pcpi_insn[14:12]) - 3'b100: instr_div <= 1; - 3'b101: instr_divu <= 1; - 3'b110: instr_rem <= 1; - 3'b111: instr_remu <= 1; - default: ; - endcase - end - - pcpi_wait <= instr_any_div_rem && resetn; - pcpi_wait_q <= pcpi_wait && resetn; - end - - reg [31:0] dividend; - reg [62:0] divisor; - reg [31:0] quotient; - reg [31:0] quotient_msk; - reg running; - reg outsign; - - always @(posedge clk) begin - pcpi_ready <= 0; - pcpi_wr <= 0; - pcpi_rd <= 'bx; - - if (!resetn) begin - running <= 0; - end else if (start) begin - running <= 1; - dividend <= (instr_div || instr_rem) && pcpi_rs1[31] ? -pcpi_rs1 : pcpi_rs1; - divisor <= ((instr_div || instr_rem) && pcpi_rs2[31] ? -pcpi_rs2 : pcpi_rs2) << 31; - outsign <= (instr_div && (pcpi_rs1[31] != pcpi_rs2[31]) && |pcpi_rs2) || (instr_rem && pcpi_rs1[31]); - quotient <= 0; - quotient_msk <= 1 << 31; - end else if (!quotient_msk && running) begin - running <= 0; - pcpi_ready <= 1; - pcpi_wr <= 1; -`ifdef RISCV_FORMAL_ALTOPS - - if (instr_div) pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h7f8529ec; - else if (instr_divu) pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h10e8fd70; - else if (instr_rem) pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h8da68fa5; - else if (instr_remu) pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h3138d0e1; -`else - if (instr_div || instr_divu) pcpi_rd <= outsign ? -quotient : quotient; - else pcpi_rd <= outsign ? -dividend : dividend; -`endif - end else begin - if (divisor <= dividend) begin - dividend <= dividend - divisor; - quotient <= quotient | quotient_msk; - end - divisor <= divisor >> 1; -`ifdef RISCV_FORMAL_ALTOPS - quotient_msk <= quotient_msk >> 5; -`else - quotient_msk <= quotient_msk >> 1; -`endif - end - end -endmodule - - -/*************************************************************** - * picorv32_axi - ***************************************************************/ - -module picorv32_axi #( - parameter [ 0:0] ENABLE_COUNTERS = 1, - parameter [ 0:0] ENABLE_COUNTERS64 = 1, - parameter [ 0:0] ENABLE_REGS_16_31 = 1, - parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, - parameter [ 0:0] TWO_STAGE_SHIFT = 1, - parameter [ 0:0] BARREL_SHIFTER = 0, - parameter [ 0:0] TWO_CYCLE_COMPARE = 0, - parameter [ 0:0] TWO_CYCLE_ALU = 0, - parameter [ 0:0] COMPRESSED_ISA = 0, - parameter [ 0:0] CATCH_MISALIGN = 1, - parameter [ 0:0] CATCH_ILLINSN = 1, - parameter [ 0:0] ENABLE_PCPI = 0, - parameter [ 0:0] ENABLE_MUL = 0, - parameter [ 0:0] ENABLE_FAST_MUL = 0, - parameter [ 0:0] ENABLE_DIV = 0, - parameter [ 0:0] ENABLE_IRQ = 0, - parameter [ 0:0] ENABLE_IRQ_QREGS = 1, - parameter [ 0:0] ENABLE_IRQ_TIMER = 1, - parameter [ 0:0] ENABLE_TRACE = 0, - parameter [ 0:0] REGS_INIT_ZERO = 0, - parameter [31:0] MASKED_IRQ = 32'h0000_0000, - parameter [31:0] LATCHED_IRQ = 32'hffff_ffff, - parameter [31:0] PROGADDR_RESET = 32'h0000_0000, - parameter [31:0] PROGADDR_IRQ = 32'h0000_0010, - parameter [31:0] STACKADDR = 32'hffff_ffff -) ( - input clk, - resetn, - output trap, - - // AXI4-lite master memory interface - - output mem_axi_awvalid, - input mem_axi_awready, - output [31:0] mem_axi_awaddr, - output [ 2:0] mem_axi_awprot, - - output mem_axi_wvalid, - input mem_axi_wready, - output [31:0] mem_axi_wdata, - output [ 3:0] mem_axi_wstrb, - - input mem_axi_bvalid, - output mem_axi_bready, - - output mem_axi_arvalid, - input mem_axi_arready, - output [31:0] mem_axi_araddr, - output [ 2:0] mem_axi_arprot, - - input mem_axi_rvalid, - output mem_axi_rready, - input [31:0] mem_axi_rdata, - - // Pico Co-Processor Interface (PCPI) - output pcpi_valid, - output [31:0] pcpi_insn, - output [31:0] pcpi_rs1, - output [31:0] pcpi_rs2, - input pcpi_wr, - input [31:0] pcpi_rd, - input pcpi_wait, - input pcpi_ready, - - // IRQ interface - input [31:0] irq, - output [31:0] eoi, - -`ifdef RISCV_FORMAL - output rvfi_valid, - output [63:0] rvfi_order, - output [31:0] rvfi_insn, - output rvfi_trap, - output rvfi_halt, - output rvfi_intr, - output [ 4:0] rvfi_rs1_addr, - output [ 4:0] rvfi_rs2_addr, - output [31:0] rvfi_rs1_rdata, - output [31:0] rvfi_rs2_rdata, - output [ 4:0] rvfi_rd_addr, - output [31:0] rvfi_rd_wdata, - output [31:0] rvfi_pc_rdata, - output [31:0] rvfi_pc_wdata, - output [31:0] rvfi_mem_addr, - output [ 3:0] rvfi_mem_rmask, - output [ 3:0] rvfi_mem_wmask, - output [31:0] rvfi_mem_rdata, - output [31:0] rvfi_mem_wdata, -`endif - - // Trace Interface - output trace_valid, - output [35:0] trace_data -); - wire mem_valid; - wire [31:0] mem_addr; - wire [31:0] mem_wdata; - wire [ 3:0] mem_wstrb; - wire mem_instr; - wire mem_ready; - wire [31:0] mem_rdata; - - picorv32_axi_adapter axi_adapter ( - .clk (clk), - .resetn (resetn), - .mem_axi_awvalid(mem_axi_awvalid), - .mem_axi_awready(mem_axi_awready), - .mem_axi_awaddr (mem_axi_awaddr), - .mem_axi_awprot (mem_axi_awprot), - .mem_axi_wvalid (mem_axi_wvalid), - .mem_axi_wready (mem_axi_wready), - .mem_axi_wdata (mem_axi_wdata), - .mem_axi_wstrb (mem_axi_wstrb), - .mem_axi_bvalid (mem_axi_bvalid), - .mem_axi_bready (mem_axi_bready), - .mem_axi_arvalid(mem_axi_arvalid), - .mem_axi_arready(mem_axi_arready), - .mem_axi_araddr (mem_axi_araddr), - .mem_axi_arprot (mem_axi_arprot), - .mem_axi_rvalid (mem_axi_rvalid), - .mem_axi_rready (mem_axi_rready), - .mem_axi_rdata (mem_axi_rdata), - .mem_valid (mem_valid), - .mem_instr (mem_instr), - .mem_ready (mem_ready), - .mem_addr (mem_addr), - .mem_wdata (mem_wdata), - .mem_wstrb (mem_wstrb), - .mem_rdata (mem_rdata) - ); - - picorv32 #( - .ENABLE_COUNTERS (ENABLE_COUNTERS), - .ENABLE_COUNTERS64 (ENABLE_COUNTERS64), - .ENABLE_REGS_16_31 (ENABLE_REGS_16_31), - .ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT), - .TWO_STAGE_SHIFT (TWO_STAGE_SHIFT), - .BARREL_SHIFTER (BARREL_SHIFTER), - .TWO_CYCLE_COMPARE (TWO_CYCLE_COMPARE), - .TWO_CYCLE_ALU (TWO_CYCLE_ALU), - .COMPRESSED_ISA (COMPRESSED_ISA), - .CATCH_MISALIGN (CATCH_MISALIGN), - .CATCH_ILLINSN (CATCH_ILLINSN), - .ENABLE_PCPI (ENABLE_PCPI), - .ENABLE_MUL (ENABLE_MUL), - .ENABLE_FAST_MUL (ENABLE_FAST_MUL), - .ENABLE_DIV (ENABLE_DIV), - .ENABLE_IRQ (ENABLE_IRQ), - .ENABLE_IRQ_QREGS (ENABLE_IRQ_QREGS), - .ENABLE_IRQ_TIMER (ENABLE_IRQ_TIMER), - .ENABLE_TRACE (ENABLE_TRACE), - .REGS_INIT_ZERO (REGS_INIT_ZERO), - .MASKED_IRQ (MASKED_IRQ), - .LATCHED_IRQ (LATCHED_IRQ), - .PROGADDR_RESET (PROGADDR_RESET), - .PROGADDR_IRQ (PROGADDR_IRQ), - .STACKADDR (STACKADDR) - ) picorv32_core ( - .clk (clk), - .resetn(resetn), - .trap (trap), - - .mem_valid(mem_valid), - .mem_addr (mem_addr), - .mem_wdata(mem_wdata), - .mem_wstrb(mem_wstrb), - .mem_instr(mem_instr), - .mem_ready(mem_ready), - .mem_rdata(mem_rdata), - - .pcpi_valid(pcpi_valid), - .pcpi_insn (pcpi_insn), - .pcpi_rs1 (pcpi_rs1), - .pcpi_rs2 (pcpi_rs2), - .pcpi_wr (pcpi_wr), - .pcpi_rd (pcpi_rd), - .pcpi_wait (pcpi_wait), - .pcpi_ready(pcpi_ready), - - .irq(irq), - .eoi(eoi), - -`ifdef RISCV_FORMAL - .rvfi_valid (rvfi_valid), - .rvfi_order (rvfi_order), - .rvfi_insn (rvfi_insn), - .rvfi_trap (rvfi_trap), - .rvfi_halt (rvfi_halt), - .rvfi_intr (rvfi_intr), - .rvfi_rs1_addr (rvfi_rs1_addr), - .rvfi_rs2_addr (rvfi_rs2_addr), - .rvfi_rs1_rdata(rvfi_rs1_rdata), - .rvfi_rs2_rdata(rvfi_rs2_rdata), - .rvfi_rd_addr (rvfi_rd_addr), - .rvfi_rd_wdata (rvfi_rd_wdata), - .rvfi_pc_rdata (rvfi_pc_rdata), - .rvfi_pc_wdata (rvfi_pc_wdata), - .rvfi_mem_addr (rvfi_mem_addr), - .rvfi_mem_rmask(rvfi_mem_rmask), - .rvfi_mem_wmask(rvfi_mem_wmask), - .rvfi_mem_rdata(rvfi_mem_rdata), - .rvfi_mem_wdata(rvfi_mem_wdata), -`endif - - .trace_valid(trace_valid), - .trace_data (trace_data) - ); -endmodule - - -/*************************************************************** - * picorv32_axi_adapter - ***************************************************************/ - -module picorv32_axi_adapter ( - input clk, - resetn, - - // AXI4-lite master memory interface - - output mem_axi_awvalid, - input mem_axi_awready, - output [31:0] mem_axi_awaddr, - output [ 2:0] mem_axi_awprot, - - output mem_axi_wvalid, - input mem_axi_wready, - output [31:0] mem_axi_wdata, - output [ 3:0] mem_axi_wstrb, - - input mem_axi_bvalid, - output mem_axi_bready, - - output mem_axi_arvalid, - input mem_axi_arready, - output [31:0] mem_axi_araddr, - output [ 2:0] mem_axi_arprot, - - input mem_axi_rvalid, - output mem_axi_rready, - input [31:0] mem_axi_rdata, - - // Native PicoRV32 memory interface - - input mem_valid, - input mem_instr, - output mem_ready, - input [31:0] mem_addr, - input [31:0] mem_wdata, - input [ 3:0] mem_wstrb, - output [31:0] mem_rdata -); - reg ack_awvalid; - reg ack_arvalid; - reg ack_wvalid; - reg xfer_done; - - assign mem_axi_awvalid = mem_valid && |mem_wstrb && !ack_awvalid; - assign mem_axi_awaddr = mem_addr; - assign mem_axi_awprot = 0; - - assign mem_axi_arvalid = mem_valid && !mem_wstrb && !ack_arvalid; - assign mem_axi_araddr = mem_addr; - assign mem_axi_arprot = mem_instr ? 3'b100 : 3'b000; - - assign mem_axi_wvalid = mem_valid && |mem_wstrb && !ack_wvalid; - assign mem_axi_wdata = mem_wdata; - assign mem_axi_wstrb = mem_wstrb; - - assign mem_ready = mem_axi_bvalid || mem_axi_rvalid; - assign mem_axi_bready = mem_valid && |mem_wstrb; - assign mem_axi_rready = mem_valid && !mem_wstrb; - assign mem_rdata = mem_axi_rdata; - - always @(posedge clk) begin - if (!resetn) begin - ack_awvalid <= 0; - end else begin - xfer_done <= mem_valid && mem_ready; - if (mem_axi_awready && mem_axi_awvalid) ack_awvalid <= 1; - if (mem_axi_arready && mem_axi_arvalid) ack_arvalid <= 1; - if (mem_axi_wready && mem_axi_wvalid) ack_wvalid <= 1; - if (xfer_done || !mem_valid) begin - ack_awvalid <= 0; - ack_arvalid <= 0; - ack_wvalid <= 0; - end - end - end -endmodule - - -/*************************************************************** - * picorv32_wb - ***************************************************************/ - -module picorv32_wb #( - parameter [ 0:0] ENABLE_COUNTERS = 1, - parameter [ 0:0] ENABLE_COUNTERS64 = 1, - parameter [ 0:0] ENABLE_REGS_16_31 = 1, - parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, - parameter [ 0:0] TWO_STAGE_SHIFT = 1, - parameter [ 0:0] BARREL_SHIFTER = 0, - parameter [ 0:0] TWO_CYCLE_COMPARE = 0, - parameter [ 0:0] TWO_CYCLE_ALU = 0, - parameter [ 0:0] COMPRESSED_ISA = 0, - parameter [ 0:0] CATCH_MISALIGN = 1, - parameter [ 0:0] CATCH_ILLINSN = 1, - parameter [ 0:0] ENABLE_PCPI = 0, - parameter [ 0:0] ENABLE_MUL = 0, - parameter [ 0:0] ENABLE_FAST_MUL = 0, - parameter [ 0:0] ENABLE_DIV = 0, - parameter [ 0:0] ENABLE_IRQ = 0, - parameter [ 0:0] ENABLE_IRQ_QREGS = 1, - parameter [ 0:0] ENABLE_IRQ_TIMER = 1, - parameter [ 0:0] ENABLE_TRACE = 0, - parameter [ 0:0] REGS_INIT_ZERO = 0, - parameter [31:0] MASKED_IRQ = 32'h0000_0000, - parameter [31:0] LATCHED_IRQ = 32'hffff_ffff, - parameter [31:0] PROGADDR_RESET = 32'h0000_0000, - parameter [31:0] PROGADDR_IRQ = 32'h0000_0010, - parameter [31:0] STACKADDR = 32'hffff_ffff -) ( - output trap, - - // Wishbone interfaces - input wb_rst_i, - input wb_clk_i, - - output reg [31:0] wbm_adr_o, - output reg [31:0] wbm_dat_o, - input [31:0] wbm_dat_i, - output reg wbm_we_o, - output reg [ 3:0] wbm_sel_o, - output reg wbm_stb_o, - input wbm_ack_i, - output reg wbm_cyc_o, - - // Pico Co-Processor Interface (PCPI) - output pcpi_valid, - output [31:0] pcpi_insn, - output [31:0] pcpi_rs1, - output [31:0] pcpi_rs2, - input pcpi_wr, - input [31:0] pcpi_rd, - input pcpi_wait, - input pcpi_ready, - - // IRQ interface - input [31:0] irq, - output [31:0] eoi, - -`ifdef RISCV_FORMAL - output rvfi_valid, - output [63:0] rvfi_order, - output [31:0] rvfi_insn, - output rvfi_trap, - output rvfi_halt, - output rvfi_intr, - output [ 4:0] rvfi_rs1_addr, - output [ 4:0] rvfi_rs2_addr, - output [31:0] rvfi_rs1_rdata, - output [31:0] rvfi_rs2_rdata, - output [ 4:0] rvfi_rd_addr, - output [31:0] rvfi_rd_wdata, - output [31:0] rvfi_pc_rdata, - output [31:0] rvfi_pc_wdata, - output [31:0] rvfi_mem_addr, - output [ 3:0] rvfi_mem_rmask, - output [ 3:0] rvfi_mem_wmask, - output [31:0] rvfi_mem_rdata, - output [31:0] rvfi_mem_wdata, -`endif - - // Trace Interface - output trace_valid, - output [35:0] trace_data, - - output mem_instr -); - wire mem_valid; - wire [31:0] mem_addr; - wire [31:0] mem_wdata; - wire [ 3:0] mem_wstrb; - reg mem_ready; - reg [31:0] mem_rdata; - - wire clk; - wire resetn; - - assign clk = wb_clk_i; - assign resetn = ~wb_rst_i; - - picorv32 #( - .ENABLE_COUNTERS (ENABLE_COUNTERS), - .ENABLE_COUNTERS64 (ENABLE_COUNTERS64), - .ENABLE_REGS_16_31 (ENABLE_REGS_16_31), - .ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT), - .TWO_STAGE_SHIFT (TWO_STAGE_SHIFT), - .BARREL_SHIFTER (BARREL_SHIFTER), - .TWO_CYCLE_COMPARE (TWO_CYCLE_COMPARE), - .TWO_CYCLE_ALU (TWO_CYCLE_ALU), - .COMPRESSED_ISA (COMPRESSED_ISA), - .CATCH_MISALIGN (CATCH_MISALIGN), - .CATCH_ILLINSN (CATCH_ILLINSN), - .ENABLE_PCPI (ENABLE_PCPI), - .ENABLE_MUL (ENABLE_MUL), - .ENABLE_FAST_MUL (ENABLE_FAST_MUL), - .ENABLE_DIV (ENABLE_DIV), - .ENABLE_IRQ (ENABLE_IRQ), - .ENABLE_IRQ_QREGS (ENABLE_IRQ_QREGS), - .ENABLE_IRQ_TIMER (ENABLE_IRQ_TIMER), - .ENABLE_TRACE (ENABLE_TRACE), - .REGS_INIT_ZERO (REGS_INIT_ZERO), - .MASKED_IRQ (MASKED_IRQ), - .LATCHED_IRQ (LATCHED_IRQ), - .PROGADDR_RESET (PROGADDR_RESET), - .PROGADDR_IRQ (PROGADDR_IRQ), - .STACKADDR (STACKADDR) - ) picorv32_core ( - .clk (clk), - .resetn(resetn), - .trap (trap), - - .mem_valid(mem_valid), - .mem_addr (mem_addr), - .mem_wdata(mem_wdata), - .mem_wstrb(mem_wstrb), - .mem_instr(mem_instr), - .mem_ready(mem_ready), - .mem_rdata(mem_rdata), - - .pcpi_valid(pcpi_valid), - .pcpi_insn (pcpi_insn), - .pcpi_rs1 (pcpi_rs1), - .pcpi_rs2 (pcpi_rs2), - .pcpi_wr (pcpi_wr), - .pcpi_rd (pcpi_rd), - .pcpi_wait (pcpi_wait), - .pcpi_ready(pcpi_ready), - - .irq(irq), - .eoi(eoi), - -`ifdef RISCV_FORMAL - .rvfi_valid (rvfi_valid), - .rvfi_order (rvfi_order), - .rvfi_insn (rvfi_insn), - .rvfi_trap (rvfi_trap), - .rvfi_halt (rvfi_halt), - .rvfi_intr (rvfi_intr), - .rvfi_rs1_addr (rvfi_rs1_addr), - .rvfi_rs2_addr (rvfi_rs2_addr), - .rvfi_rs1_rdata(rvfi_rs1_rdata), - .rvfi_rs2_rdata(rvfi_rs2_rdata), - .rvfi_rd_addr (rvfi_rd_addr), - .rvfi_rd_wdata (rvfi_rd_wdata), - .rvfi_pc_rdata (rvfi_pc_rdata), - .rvfi_pc_wdata (rvfi_pc_wdata), - .rvfi_mem_addr (rvfi_mem_addr), - .rvfi_mem_rmask(rvfi_mem_rmask), - .rvfi_mem_wmask(rvfi_mem_wmask), - .rvfi_mem_rdata(rvfi_mem_rdata), - .rvfi_mem_wdata(rvfi_mem_wdata), -`endif - - .trace_valid(trace_valid), - .trace_data (trace_data) - ); - - localparam IDLE = 2'b00; - localparam WBSTART = 2'b01; - localparam WBEND = 2'b10; - - reg [1:0] state; - - wire we; - assign we = (mem_wstrb[0] | mem_wstrb[1] | mem_wstrb[2] | mem_wstrb[3]); - - always @(posedge wb_clk_i) begin - if (wb_rst_i) begin - wbm_adr_o <= 0; - wbm_dat_o <= 0; - wbm_we_o <= 0; - wbm_sel_o <= 0; - wbm_stb_o <= 0; - wbm_cyc_o <= 0; - state <= IDLE; - end else begin - case (state) - IDLE: begin - if (mem_valid) begin - wbm_adr_o <= mem_addr; - wbm_dat_o <= mem_wdata; - wbm_we_o <= we; - wbm_sel_o <= mem_wstrb; - - wbm_stb_o <= 1'b1; - wbm_cyc_o <= 1'b1; - state <= WBSTART; - end else begin - mem_ready <= 1'b0; - - wbm_stb_o <= 1'b0; - wbm_cyc_o <= 1'b0; - wbm_we_o <= 1'b0; - end - end - WBSTART: begin - if (wbm_ack_i) begin - mem_rdata <= wbm_dat_i; - mem_ready <= 1'b1; - - state <= WBEND; - - wbm_stb_o <= 1'b0; - wbm_cyc_o <= 1'b0; - wbm_we_o <= 1'b0; - end - end - WBEND: begin - mem_ready <= 1'b0; - - state <= IDLE; - end - default: state <= IDLE; - endcase - end - end -endmodule diff --git a/lib/hardware/iob_picorv32/iob_picorv32.py b/lib/hardware/iob_picorv32/iob_picorv32.py deleted file mode 100644 index 1830190a8..000000000 --- a/lib/hardware/iob_picorv32/iob_picorv32.py +++ /dev/null @@ -1,120 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "confs": [ - { - "name": "ADDR_W", - "type": "P", - "val": "32", - "min": "1", - "max": "?", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "1", - "max": "?", - "descr": "description here", - }, - { - "name": "USE_COMPRESSED", - "type": "P", - "val": "1", - "min": "0", - "max": "1", - "descr": "description here", - }, - { - "name": "USE_MUL_DIV", - "type": "P", - "val": "1", - "min": "0", - "max": "1", - "descr": "description here", - }, - ], - "ports": [ - { - "name": "clk_en_rst_i", - "signals": [ - { - "name": "clk", - "direction": "input", - "width": "1", - "descr": "Clock input", - }, - { - "name": "cke", - "direction": "input", - "width": "1", - "descr": "Clock enable input", - }, - { - "name": "rst", - "direction": "input", - "width": "1", - "descr": "Synchronous reset input", - }, - ], - "descr": "Clock, enable and synchronous reset", - }, - { - "name": "general_o", - "descr": "General interface signals", - "signals": [ - { - "name": "trap", - "direction": "output", - "width": "1", - "descr": "CPU trap output", - }, - ], - }, - { - "name": "i_bus_m", - "interface": { - "type": "iob", - "subtype": "master", - "file_prefix": "iob_picorv32_ibus_", - "port_prefix": "ibus_", - "wire_prefix": "ibus_", - "DATA_W": "DATA_W", - "ADDR_W": "ADDR_W", - }, - "descr": "iob-picorv32 instruction bus", - }, - { - "name": "d_bus_m", - "interface": { - "type": "iob", - "subtype": "master", - "file_prefix": "iob_picorv32_dbus_", - "port_prefix": "dbus_", - "wire_prefix": "dbus_", - "DATA_W": "DATA_W", - "ADDR_W": "ADDR_W", - }, - "descr": "iob-picorv32 data bus", - }, - ], - "blocks": [ - { - "core_name": "iob_reg", - "instance_name": "iob_reg_inst", - }, - { - "core_name": "iob_edge_detect", - "instance_name": "iob_edge_detect_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/iob_regfileif/README.md b/lib/hardware/iob_regfileif/README.md deleted file mode 100644 index 1c1edf070..000000000 --- a/lib/hardware/iob_regfileif/README.md +++ /dev/null @@ -1,131 +0,0 @@ - - -# README # - -# iob-regfileif - -## What is this repository for? ## - -The IObundle REGFILEIF is a RISC-V-based Peripheral written in Verilog, which users can download, modify, simulate and implement in FPGA or ASIC. -This peripheral contains registers to buffer communication between two systems using their respective peripheral buses. -It provides an external IOb-native interface to the secondary system, allowing it to be controlled via registers by another primary system. - -This peripheral has two IOb-native interfaces: -- It has the internal IOb-native interface that connects to the peripheral bus of the system that will be controlled and contains the registers. -- It also has an external IOb-native interface that connects to the native peripheral bus of an external (primary) system. - -This repository also provides a peripheral for the primary system, that allows it to access registers of the secondary system. -This peripheral, named IOBNATIVEBRIDGEIF is located in the `nativebridgeif_wrappper` directory of this repository. - -The IOBNATIVEBRIDGEIF can be used by primary systems like the [IOb-SoC-Tester](https://github.com/IObundle/iob-soc-tester) [project](https://nlnet.nl/project/OpenCryptoTester#ack), to access registers of a secondary system like the [IOb-SoC-SUT](https://github.com/IObundle/iob-soc-sut). - -## Integrate in SoC ## - -* Check out [IOb-SoC-SUT](https://github.com/IObundle/iob-soc-sut) - -## Usage - -This peripheral receives its register configuration via module parameters. - -The module parameters passed to this peripheral should be a dictionary that contains a list of registers as a value to the `regs` key of the dictionary. -The list of the `regs` item has a similar structure to the `regs` list of any IOb-SoC-based peripheral's setup module, such as in the `iob_uart_setup.py` script of the [IOb-uart](https://github.com/IObundle/iob-uart) peripheral. - -The `iob_soc_sut_setup.py` script of the [IOb-SoC-SUT](https://github.com/IObundle/iob-soc-sut) system, has the following example configuration for the REGFILEIF peripheral: -```Python -regfileif_options = { - 'regs': - [ - {'name': 'regfileif', 'descr':'REGFILEIF software accessible registers.', 'regs': [ - {'name':'REG1','type':'W', 'n_bits':8, 'rst_val':0, 'addr':-1, 'log2n_items':0, 'autologic':True, 'descr':'Write register: 8 bit'}, - {'name':'REG2','type':'W', 'n_bits':16, 'rst_val':0, 'addr':-1, 'log2n_items':0, 'autologic':True, 'descr':'Write register: 16 bit'}, - {'name':'REG3','type':'R', 'n_bits':8, 'rst_val':0, 'addr':-1, 'log2n_items':0, 'autologic':True, 'descr':'Read register: 8 bit'}, - {'name':'REG4','type':'R', 'n_bits':16, 'rst_val':0, 'addr':-1, 'log2n_items':0, 'autologic':True, 'descr':'Read register 16 bit'}, - {'name':'REG5','type':'R', 'n_bits':32, 'rst_val':0, 'addr':-1, 'log2n_items':0, 'autologic':True, 'descr':'Read register 32 bit.'}, - ]} - ] -} -``` - -These options are then passed to the REGFILEIF peripheral of the system using module parameters. -To do this, use a tuple in the `modules` list of the `submodules` dictionary, where the first element of the tuple is the module name and the second element is the module parameters. - -The `iob_soc_sut_setup.py` script uses the following tuple to pass the module parameters above to the REGFILEIF peripheral: -```Python -('REGFILEIF',regfileif_options) -``` - -To instantiate the peripheral, add a dictionary describing the peripheral in the `peripherals` list of the `blocks` dictionary in the setup python module of the system. - -The `iob_soc_sut_setup.py` script uses the following dictionary to instantiate a REGFILEIF peripheral with the instance name `REGFILEIF0`: -```Python -blocks = \ -[ - # Other blocks here... - - {'name':'peripherals', 'descr':'peripheral modules', 'blocks': [ - {'name':'REGFILEIF0', 'type':'REGFILEIF', 'descr':'Register file interface', 'params':{}}, - - # Other peripheral instances here... - ]}, -] -``` - -### Connecting peripheral buses of SUT and Tester systems - -When using two systems, such as [IOb-SoC-SUT](https://github.com/IObundle/iob-soc-sut) and [IOb-SoC-Tester](https://github.com/IObundle/iob-soc-tester), the REGFILEIF is a peripheral of the SUT. - -The connection between the external IOb-native interface of the system with the REGFILEIF and the peripheral bus of the Tester can be made using the `peripheral_portmap` list of the Tester. - -However, to connect using the portmap, the IOb-native bus signals of the Tester must be externally accessible (the portmap configuration can only map signals that can be accessed externally). - -To do this, we use the peripheral **IOBNATIVEBRIDGEIF**, located inside the `nativebridgeif_wrappper` directory of this repository. -This peripheral also has two IOb-native interfaces, one internal and one external. -However, unlike REGFILEIF, the external interface of this peripheral is a master interface. This allows it to be connected to the slave IOb-native interface of another module, such as the REGFILEIF. -The IOBNATIVEBRIDGEIF, allows the peripheral bus signals of the system to be accessed externally. - -We use the IOBNATIVEBRIDGEIF as a peripheral of the Tester to allow its peripheral bus signals to be accessed externally, and therefore be port mapped. - -To instantiate the IOBNATIVEBRIDGEIF peripheral we add a new dictionary to the `peripherals` list of the `blocks` dictionary, similar to the REGFILEIF instantiation. - -In the `iob_soc_sut_setup.py` script, the IOBNATIVEBRIDGEIF is a peripheral of the Tester, therefore it is instantiated in the `extra_peripherals` list of the Tester module parameters: -```Python - 'extra_peripherals': - [ - {'name':'IOBNATIVEBRIDGEIF0', 'type':'IOBNATIVEBRIDGEIF', 'descr':'IOb native interface for communication with SUT. Essentially a REGFILEIF without any registers.', 'params':{}}, - - # Other peripheral instances here... - ], -``` - -To connect the external IOb-native (slave) interface of the REGFILEIF of the SUT to the external IOb-native (master) interface of the IOBNATIVEBRIDGEIF of the Tester, the `iob_soc_sut_setup.py` script contains the following configuration in the `peripheral_portmap` list of the Tester module parameters: -```Python - 'peripheral_portmap': - [ - ({'corename':'SUT0', 'if_name':'REGFILEIF0', 'port':'external_iob_valid_i', 'bits':[]}, {'corename':'IOBNATIVEBRIDGEIF0', 'if_name':'iob_m_port', 'port':'iob_valid_o', 'bits':[]}), - ({'corename':'SUT0', 'if_name':'REGFILEIF0', 'port':'external_iob_addr_i', 'bits':[]}, {'corename':'IOBNATIVEBRIDGEIF0', 'if_name':'iob_m_port', 'port':'iob_addr_o', 'bits':[]}), - ({'corename':'SUT0', 'if_name':'REGFILEIF0', 'port':'external_iob_wdata_i', 'bits':[]}, {'corename':'IOBNATIVEBRIDGEIF0', 'if_name':'iob_m_port', 'port':'iob_wdata_o', 'bits':[]}), - ({'corename':'SUT0', 'if_name':'REGFILEIF0', 'port':'external_iob_wstrb_i', 'bits':[]}, {'corename':'IOBNATIVEBRIDGEIF0', 'if_name':'iob_m_port', 'port':'iob_wstrb_o', 'bits':[]}), - ({'corename':'SUT0', 'if_name':'REGFILEIF0', 'port':'external_iob_rvalid_o', 'bits':[]}, {'corename':'IOBNATIVEBRIDGEIF0', 'if_name':'iob_m_port', 'port':'iob_rvalid_i', 'bits':[]}), - ({'corename':'SUT0', 'if_name':'REGFILEIF0', 'port':'external_iob_rdata_o', 'bits':[]}, {'corename':'IOBNATIVEBRIDGEIF0', 'if_name':'iob_m_port', 'port':'iob_rdata_i', 'bits':[]}), - ({'corename':'SUT0', 'if_name':'REGFILEIF0', 'port':'external_iob_ready_o', 'bits':[]}, {'corename':'IOBNATIVEBRIDGEIF0', 'if_name':'iob_m_port', 'port':'iob_ready_i', 'bits':[]}), - - # Other portmap entries here... - ], -``` - -# Acknowledgement -The [OpenCryptoTester](https://nlnet.nl/project/OpenCryptoTester#ack) project is funded through the NGI Assure Fund, a fund established by NLnet -with financial support from the European Commission's Next Generation Internet -programme, under the aegis of DG Communications Networks, Content and Technology -under grant agreement No 957073. - - - - - - -
NLnet foundation logoNGI Assure logo
diff --git a/lib/hardware/iob_regfileif/config.mk b/lib/hardware/iob_regfileif/config.mk deleted file mode 100644 index e9295d88d..000000000 --- a/lib/hardware/iob_regfileif/config.mk +++ /dev/null @@ -1,31 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -TOP_MODULE=iob_regfileif - -#PATHS -LIB_DIR ?=$(REGFILEIF_DIR)/submodules/LIB -REGFILEIF_HW_DIR:=$(REGFILEIF_DIR)/hardware - -#MAKE SW ACCESSIBLE REGISTER -MKREGS:=$(shell find $(LIB_DIR) -name mkregs.py) - -#Check that configuration file exists -ifeq (,$(wildcard $(ROOT_DIR)/sut_csrs.vh)) - $(error Missing 'sut_csrs.vh' configuration file in root directory!) -endif - -# VERSION -VERSION ?=V0.1 -$(TOP_MODULE)_version.txt: - echo $(VERSION) > version.txt - -#cpu accessible registers -iob_regfileif_csrs_def.vh iob_regfileif_csrs_gen.vh: $(REGFILEIF_DIR)/mkregs.conf - $(REGFILEIF_DIR)/software/python/mkregsregfileif.py $< HW $(shell dirname $(MKREGS)) iob_regfileif - -regfileif-gen-clean: - @rm -rf *# *~ version.txt - -.PHONY: regfileif-gen-clean diff --git a/lib/hardware/iob_regfileif/hardware/hardware.mk b/lib/hardware/iob_regfileif/hardware/hardware.mk deleted file mode 100755 index 59ee8e52e..000000000 --- a/lib/hardware/iob_regfileif/hardware/hardware.mk +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -ifeq ($(filter REGFILEIF, $(HW_MODULES)),) - -include $(REGFILEIF_DIR)/config.mk - -#add itself to HW_MODULES list -HW_MODULES+=REGFILEIF - - -REGFILEIF_INC_DIR:=$(REGFILEIF_HW_DIR)/include -REGFILEIF_SRC_DIR:=$(REGFILEIF_HW_DIR)/src - -#include files -VHDR+=$(wildcard $(REGFILEIF_INC_DIR)/*.vh) -VHDR+=iob_regfileif_csrs_gen.vh iob_regfileif_csrs_def.vh -VHDR+=$(LIB_DIR)/hardware/include/iob_lib.vh $(LIB_DIR)/hardware/include/iob_s_if.vh $(LIB_DIR)/hardware/include/iob_gen_if.vh - - - -#hardware include dirs -INCLUDE+=$(incdir). $(incdir)$(REGFILEIF_INC_DIR) $(incdir)$(LIB_DIR)/hardware/include - -#sources -VSRC+=$(REGFILEIF_SRC_DIR)/iob_regfileif.v - -regfileif-hw-clean: regfileif-gen-clean - @rm -f *.v *.vh - -.PHONY: regfileif-hw-clean - -endif diff --git a/lib/hardware/iob_regfileif/hardware/include/inst.vh b/lib/hardware/iob_regfileif/hardware/include/inst.vh deleted file mode 100644 index 22b949397..000000000 --- a/lib/hardware/iob_regfileif/hardware/include/inst.vh +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - - // - // /**/ - // - - iob_regfileif /**/ - ( - .clk (clk), - .rst (reset), - - // Register file interface - .valid_ext (/**/_valid), - .address_ext (/**/_address), - .wdata_ext (/**/_wdata), - .wstrb_ext (/**/_wstrb), - .rdata_ext (/**/_rdata), - .ready_ext (/**/_ready), - - // CPU interface - .valid (slaves_req[`valid(`/**/)]), - .address (slaves_req[`address(`/**/,`iob_regfileif_csrs_ADDR_W+2)-2]), - .wdata (slaves_req[`wdata(`/**/)]), - .wstrb (slaves_req[`wstrb(`/**/)]), - .rdata (slaves_resp[`rdata(`/**/)]), - .ready (slaves_resp[`ready(`/**/)]) - ); diff --git a/lib/hardware/iob_regfileif/hardware/include/pio.vh b/lib/hardware/iob_regfileif/hardware/include/pio.vh deleted file mode 100644 index 2e3ef7361..000000000 --- a/lib/hardware/iob_regfileif/hardware/include/pio.vh +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - - // REGFILEIF - input /**/_valid, - input [`iob_regfileif_csrs_ADDR_W-1:0] /**/_address, - input [`DATA_W-1:0] /**/_wdata, - input [`DATA_W/8-1:0] /**/_wstrb, - output [`DATA_W-1:0] /**/_rdata, - output /**/_ready, diff --git a/lib/hardware/iob_regfileif/iob_regfileif.py b/lib/hardware/iob_regfileif/iob_regfileif.py deleted file mode 100755 index ce061aee5..000000000 --- a/lib/hardware/iob_regfileif/iob_regfileif.py +++ /dev/null @@ -1,226 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -import copy -import json - - -def setup(py_params_dict): - params = { - "version": "0.7", - "internal_csr_if": "iob", - "external_csr_if": "iob", - # FIXME: Make ADDR_W automatic - "internal_csr_if_widths": {"ADDR_W": 32, "DATA_W": 32}, - "external_csr_if_widths": {"ADDR_W": 32, "DATA_W": 32}, - "csrs": [], - "autoaddr": True, - "test": False, # Enable this to use random registers - } - - # Update params with values from py_params_dict - for param in py_params_dict: - if param in params: - params[param] = py_params_dict[param] - - # If we are in "test" mode, generate this core with random registers - if params["test"]: - params["csrs"] = [ - { - "name": "reg_group", - "descr": "Test register group", - "regs": [ - { - "name": "reg1", - "type": "R", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Test register 1", - }, - { - "name": "reg2", - "type": "W", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Test register 1", - }, - { - "name": "reg3", - "type": "RW", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Test register 3", - }, - ], - } - ] - confs = [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "32", - "descr": "Data bus width", - }, - ] - - assert params["csrs"], "Error: Register list empty." - - reg_wires = [] - external_reg_connections = {} - internal_reg_connections = {} - - # Invert CSRS direction for internal CPU - csrs_inverted = copy.deepcopy(params["csrs"]) - for csr_group in csrs_inverted: - for csr in csr_group["regs"]: - if csr["type"] == "W": - csr["type"] = "R" - elif csr["type"] == "R": - csr["type"] = "W" - # Do nothing for type "RW" - - # Ensure autoreg is enabled for all CSRs - assert csr[ - "autoreg" - ], f"Error: CSR '{csr['name']}' must have 'autoreg' set." - - # Create wire for reg - reg_wires.append( - { - "name": csr["name"], - "descr": "", - "signals": [ - {"name": csr["name"], "width": csr["n_bits"]}, - ], - }, - ) - if csr["type"] == "RW": - reg_wires[-1]["signals"].append( - {"name": csr["name"] + "_2", "width": csr["n_bits"]}, - ) - reg_wires.append( - { - "name": csr["name"] + "_inv", - "descr": "", - "signals": [ - {"name": csr["name"] + "_2"}, - {"name": csr["name"]}, - ], - }, - ) - - # Connect register interfaces - external_reg_connections[csr["name"]] = csr["name"] - internal_reg_connections[csr["name"]] = csr["name"] - if csr["type"] == "RW": - internal_reg_connections[csr["name"]] = csr["name"] + "_inv" - - if py_params_dict["instantiator"]: - confs = py_params_dict["instantiator"]["confs"] - - attributes_dict = { - "version": "0.1", - } - attributes_dict |= { - "confs": confs, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "internal_control_if_s", - "interface": { - "type": params["internal_csr_if"], - "subtype": "slave", - **params["internal_csr_if_widths"], - }, - "descr": "Internal CPU native interface. Registers have their direction inverted from this CPU's perspective.", - }, - { - "name": "external_control_if_s", - "interface": { - "type": params["external_csr_if"], - "subtype": "slave", - "port_prefix": "external_", - **params["external_csr_if_widths"], - }, - "descr": "External CPU native interface.", - }, - ], - "wires": reg_wires - + [ - { - "name": "csrs_iob", - "descr": "Internal CSRs iob interface", - "interface": { - "type": "iob", - "wire_prefix": "csrs_", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - { - "name": "internal_iob2", - "descr": "Internal iob interface", - "interface": { - "type": "iob", - "wire_prefix": "internal2_", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - ], - "blocks": [ - { - "core_name": "csrs", - "instance_name": "csrs_external", - "instance_description": "Control/Status Registers for external CPU", - "csrs": params["csrs"], - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "external_control_if_s", - "csrs_iob_o": "csrs_iob", - **external_reg_connections, - }, - "csr_if": params["external_csr_if"], - # TODO: Support external_csr_if_widths - "version": params["version"], - "autoaddr": params["autoaddr"], - }, - { - "core_name": "csrs", - "name": attributes_dict["name"] + "_inverted_csrs", - "instance_name": "csrs_internal_inverted", - "instance_description": "Control/Status Registers for internal CPU (inverted registers)", - "csrs": csrs_inverted, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "internal_control_if_s", - "csrs_iob_o": "internal_iob2", - **internal_reg_connections, - }, - "csr_if": params["internal_csr_if"], - # TODO: Support internal_csr_if_widths - "version": attributes_dict["version"], - "autoaddr": params["autoaddr"], - }, - ], - } - - print(json.dumps(attributes_dict, indent=4)) # DEBUG - - return attributes_dict diff --git a/lib/hardware/iob_regfileif/nativebridgeif_wrappper/hardware/src/iob_nativebridgeif.v b/lib/hardware/iob_regfileif/nativebridgeif_wrappper/hardware/src/iob_nativebridgeif.v deleted file mode 100644 index 9a9ef2df9..000000000 --- a/lib/hardware/iob_regfileif/nativebridgeif_wrappper/hardware/src/iob_nativebridgeif.v +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns/1ps -`include "iob_lib.vh" -`include "iob_nativebridgeif_conf.vh" -`include "iob_nativebridgeif_swreg_def.vh" - -module iob_nativebridgeif # ( - `include "iob_nativebridgeif_params.vs" - ) ( - `include "iob_nativebridgeif_io.vs" - ); - - // Connect interfaces - assign iob_valid_o = iob_valid_i; - assign iob_addr_o = iob_addr_i; - assign iob_wdata_o = iob_wdata_i; - assign iob_wstrb_o = iob_wstrb_i; - assign iob_rdata_o = iob_rdata_i; - assign iob_ready_o = iob_ready_i; - assign iob_rvalid_o = iob_rvalid_i; - -endmodule - diff --git a/lib/hardware/iob_regfileif/nativebridgeif_wrappper/iob_nativebridgeif_setup.py b/lib/hardware/iob_regfileif/nativebridgeif_wrappper/iob_nativebridgeif_setup.py deleted file mode 100755 index b4ebacc9f..000000000 --- a/lib/hardware/iob_regfileif/nativebridgeif_wrappper/iob_nativebridgeif_setup.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 - -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -import os, sys - -sys.path.insert(0, os.getcwd() + "/submodules/LIB/scripts") -import setup -from submodule_utils import import_setup - -name = "iob_nativebridgeif" -flows = "" -setup_dir = os.path.dirname(__file__) -submodules = {} - -regfileif_core_module = import_setup(f"{setup_dir}/..") -# Copy some fields from the regfileif core -version = regfileif_core_module.version -flows = regfileif_core_module.flows -if setup.is_top_module(sys.modules[__name__]): - build_dir = f"../{name}_{version}" - -confs = regfileif_core_module.confs - -ios = [ - {"name": "iob_s_port", "descr": "Slave CPU native interface", "ports": []}, - {"name": "iob_m_port", "descr": "Master CPU native interface", "ports": []}, -] - -regs = [ - { - "name": "dummy", - "descr": "Dummy registers to run register setup functions", - "regs": [ - { - "name": "DUMMY", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "addr": -1, - "log2n_items": 0, - "autologic": False, - "descr": "Dummy Register", - }, - ], - } -] - -blocks = [] - - -# Main function to setup this core and its components -def main(): - setup.setup(sys.modules[__name__]) - - # Remove csrs_gen and csrs_inst files - os.remove(f"{build_dir}/hardware/src/{name}_csrs_inst.vs") - os.remove(f"{build_dir}/hardware/src/{name}_csrs_gen.v") - - # Modify iob_nativebridgeif_csrs_def.vh - with open(f"{build_dir}/hardware/src/{name}_csrs_def.vh", "w") as file: - file.write('`include "iob_regfileif_csrs_def.vh"\n') - file.write( - "`define IOB_NATIVEBRIDGEIF_csrs_ADDR_W `IOB_REGFILEIF_csrs_ADDR_W\n" - ) - - -if __name__ == "__main__": - main() diff --git a/lib/hardware/iob_regfileif/software/python/iobnativebridge.py b/lib/hardware/iob_regfileif/software/python/iobnativebridge.py deleted file mode 100755 index a65541e98..000000000 --- a/lib/hardware/iob_regfileif/software/python/iobnativebridge.py +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/env python3 - -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# -# Create IOBNATIVEBRIDGEIF in directory specified -# The IOBNATIVEBRIDGEIF is a peripheral that only contains wires. It bridges the peripheral bus with an external interface. -# It allows the peripheral bus signals to be accessed externally. -# - -import sys -import os -import re - - -def create_iobnativebridgeif(directory): - iobnativebridgeif_dir = os.path.join(directory, "IOBNATIVEBRIDGEIF") - - if os.path.isdir(iobnativebridgeif_dir): - raise Exception( - "{} already exists! Delete that folder before generating this peripheral.".format( - iobnativebridgeif_dir - ) - ) - - # ~~~~~~~~~~~~ Create IOBNATIVEBRIDGEIF folder ~~~~~~~~~~~~ - os.mkdir(iobnativebridgeif_dir) - - # ~~~~~~~~~~~~ Create harware, software and submodule dirs ~~~~~~~~~~~~ - os.mkdir(os.path.join(iobnativebridgeif_dir, "hardware")) - os.mkdir(os.path.join(iobnativebridgeif_dir, "software")) - os.mkdir(os.path.join(iobnativebridgeif_dir, "submodules")) - - # symlink LIB submodule from REGFILEIF - os.symlink( - os.path.relpath( - os.path.join(os.path.dirname(__file__), "../../submodules/LIB"), - os.path.join(iobnativebridgeif_dir, "submodules"), - ), - os.path.join(iobnativebridgeif_dir, "submodules/LIB"), - ) - - # ~~~~~~~~~~~~ Create config.mk ~~~~~~~~~~~~ - config_mk_str = """ -TOP_MODULE=iob_nativebridgeif - -#PATHS -LIB_DIR ?=$(IOBNATIVEBRIDGEIF_DIR)/submodules/LIB -IOBNATIVEBRIDGEIF_HW_DIR:=$(IOBNATIVEBRIDGEIF_DIR)/hardware - -# VERSION -VERSION ?=V0.1 -$(TOP_MODULE)_version.txt: - echo $(VERSION) > version.txt - -#MAKE SW ACCESSIBLE REGISTER -MKREGS:=$(shell find -L $(LIB_DIR) -name mkregs.py) - -#target to create (and update) csrs for nativebridgeif based on sut_csrs.conf -$(IOBNATIVEBRIDGEIF_DIR)/mkregs.conf: $(IOBNATIVEBRIDGEIF_DIR)/../../sut_csrs.vh - $(IOBNATIVEBRIDGEIF_DIR)/software/python/createIObNativeIfcsrs.py $(IOBNATIVEBRIDGEIF_DIR)/../.. - -#cpu accessible registers -iob_nativebridgeif_csrs_def.vh iob_nativebridgeif_csrs_gen.vh: $(IOBNATIVEBRIDGEIF_DIR)/mkregs.conf - $(IOBNATIVEBRIDGEIF_DIR)/software/python/mkregsregfileif.py $< HW $(shell dirname $(MKREGS)) iob_nativebridgeif - - """ - fout = open(os.path.join(iobnativebridgeif_dir, "config.mk"), "w") - fout.write(config_mk_str) - fout.close() - - # ~~~~~~~~~~~~ Create gitignore (for csrs) ~~~~~~~~~~~~ - gitignore_str = """\ -hardware/include/iob_nativebridgeif_csrs.vh - """ - fout = open(os.path.join(iobnativebridgeif_dir, ".gitignore"), "w") - fout.write(gitignore_str) - fout.close() - - # ~~~~~~~~~~~~ Create hardware.mk ~~~~~~~~~~~~ - harware_mk_str = """ -ifeq ($(filter IOBNATIVEBRIDGEIF, $(HW_MODULES)),) - -include $(IOBNATIVEBRIDGEIF_DIR)/config.mk - -#add itself to HW_MODULES list -HW_MODULES+=IOBNATIVEBRIDGEIF - -IOBNATIVEBRIDGEIF_INC_DIR:=$(IOBNATIVEBRIDGEIF_HW_DIR)/include -IOBNATIVEBRIDGEIF_SRC_DIR:=$(IOBNATIVEBRIDGEIF_HW_DIR)/src - -#include files -VHDR+=$(wildcard $(IOBNATIVEBRIDGEIF_INC_DIR)/*.vh) -VHDR+=iob_nativebridgeif_csrs_gen.vh iob_nativebridgeif_csrs_def.vh -VHDR+=$(LIB_DIR)/hardware/include/iob_lib.vh $(LIB_DIR)/hardware/include/iob_s_if.vh $(LIB_DIR)/hardware/include/iob_gen_if.vh - - - -#hardware include dirs -INCLUDE+=$(incdir). $(incdir)$(IOBNATIVEBRIDGEIF_INC_DIR) $(incdir)$(LIB_DIR)/hardware/include - -#sources -VSRC+=$(IOBNATIVEBRIDGEIF_SRC_DIR)/iob_nativebridgeif.v - -endif - """ - fout = open(os.path.join(iobnativebridgeif_dir, "hardware", "hardware.mk"), "w") - fout.write(harware_mk_str) - fout.close() - - # ~~~~~~~~~~~~ Create include, iob_nativebridgeif.vh, inst.vh and pio.vh ~~~~~~~~~~~~ - os.mkdir(os.path.join(iobnativebridgeif_dir, "hardware/include")) - # inst.vh - fin = open( - os.path.join(os.path.dirname(__file__), "../../hardware/include/inst.vh"), "r" - ) - instv_content = fin.readlines() - fin.close() - for i in range(len(instv_content)): - instv_content[i] = re.sub("regfileif", "nativebridgeif", instv_content[i]) - fout = open(os.path.join(iobnativebridgeif_dir, "hardware/include", "inst.vh"), "w") - fout.writelines(instv_content) - fout.close() - # pio.vh - fin = open( - os.path.join(os.path.dirname(__file__), "../../hardware/include/pio.vh"), "r" - ) - pio_content = fin.readlines() - fin.close() - for i in range(len(pio_content)): - if "input" in pio_content[i]: - pio_content[i] = re.sub( - "REGFILEIF", - "IOBNATIVEBRIDGEIF", - re.sub( - "regfileif", - "nativebridgeif", - re.sub("input", "output", pio_content[i]), - ), - ) - else: - pio_content[i] = re.sub( - "REGFILEIF", - "IOBNATIVEBRIDGEIF", - re.sub( - "regfileif", - "nativebridgeif", - re.sub("output", "input", pio_content[i]), - ), - ) - fout = open(os.path.join(iobnativebridgeif_dir, "hardware/include", "pio.vh"), "w") - fout.writelines(pio_content) - fout.close() - # createIObNativeIfcsrs.py - os.mkdir(os.path.join(iobnativebridgeif_dir, "software/python")) - csrs_python_creator = """\ -#!/usr/bin/env python3 -#Script created by iobnativebridge.py -# Call this script with ROOT_DIR to create the iob_nativebridge_csrs.vh -import os -import sys -import re - -fin = open (os.path.join(sys.argv[1], 'sut_csrs.vh'), 'r') -csrs_content=fin.readlines() -fin.close() -for i in range(len(csrs_content)): - csrs_content[i] = re.sub('REGFILEIF','IOBNATIVEBRIDGEIF', csrs_content[i]) -fout = open (os.path.join(os.path.dirname(__file__),"../../","mkregs.conf"), 'w') -fout.writelines(csrs_content) -fout.close() - """ - fout = open( - os.path.join( - iobnativebridgeif_dir, "software/python", "createIObNativeIfcsrs.py" - ), - "w", - ) - fout.writelines(csrs_python_creator) - fout.close() - os.chmod( - os.path.join( - iobnativebridgeif_dir, "software/python", "createIObNativeIfcsrs.py" - ), - 0o755, - ) - # link mkregsregfileif.py - os.symlink( - os.path.relpath( - os.path.join(os.path.dirname(__file__), "mkregsregfileif.py"), - os.path.join(iobnativebridgeif_dir, "software/python"), - ), - os.path.join(iobnativebridgeif_dir, "software/python", "mkregsregfileif.py"), - ) - - # ~~~~~~~~~~~~ Create verilog source ~~~~~~~~~~~~ - os.mkdir(os.path.join(iobnativebridgeif_dir, "hardware/src")) - verilog_source_str = """ -`timescale 1ns/1ps -`include "iob_lib.vh" -`include "iob_nativebridgeif_csrs_def.vh" - -module iob_nativebridgeif - # ( - parameter DATA_W = `DATA_W, - parameter ADDR_W = `iob_nativebridgeif_csrs_ADDR_W - ) - ( - - // CPU interface -`include "iob_s_if.vh" - - // External interface - `IOB_OUTPUT(valid_ext, 1), - `IOB_OUTPUT(address_ext, ADDR_W), - `IOB_OUTPUT(wdata_ext, DATA_W), - `IOB_OUTPUT(wstrb_ext, DATA_W/8), - `IOB_INPUT(rdata_ext, DATA_W), - `IOB_INPUT(ready_ext, 1), - - -`include "iob_gen_if.vh" - ); - - // Connect interfaces - assign valid_ext = valid; - assign address_ext = address; - assign wdata_ext = wdata; - assign wstrb_ext = wstrb; - assign rdata = rdata_ext; - assign ready = ready_ext; - -endmodule - """ - fout = open( - os.path.join(iobnativebridgeif_dir, "hardware/src", "iob_nativebridgeif.v"), "w" - ) - fout.write(verilog_source_str) - fout.close() - - # ~~~~~~~~~~~~ Create software.mk ~~~~~~~~~~~~ - software_str = """ -include $(IOBNATIVEBRIDGEIF_DIR)/config.mk - -IOBNATIVEBRIDGEIF_SW_DIR:=$(IOBNATIVEBRIDGEIF_DIR)/software - -#include -INCLUDE+= - -#headers -HDR+=iob_nativebridgeif_csrs.h - -#sources -SRC+= - -iob_nativebridgeif_csrs.h iob_nativebridgeif_inverted_csrs.h: $(IOBNATIVEBRIDGEIF_DIR)/mkregs.conf - $(IOBNATIVEBRIDGEIF_DIR)/software/python/mkregsregfileif.py $< SW $(shell dirname $(MKREGS)) iob_nativebridgeif - """ - fout = open(os.path.join(iobnativebridgeif_dir, "software", "software.mk"), "w") - fout.write(software_str) - fout.close() - - # ~~~~~~~~~~~~ Create embedded.mk ~~~~~~~~~~~~ - os.mkdir(os.path.join(iobnativebridgeif_dir, "software/embedded")) - embedded_str = """ -ifeq ($(filter IOBNATIVEBRIDGEIF, $(SW_MODULES)),) - -SW_MODULES+=IOBNATIVEBRIDGEIF - -include $(IOBNATIVEBRIDGEIF_DIR)/software/software.mk - -# add embeded sources -SRC+=iob_nativebridgeif_csrs_emb.c - -iob_nativebridgeif_csrs_emb.c: iob_nativebridgeif_csrs.h - - -endif - """ - fout = open( - os.path.join(iobnativebridgeif_dir, "software/embedded", "embedded.mk"), "w" - ) - fout.write(embedded_str) - fout.close() - - # ~~~~~~~~~~~~ Create pc-emul.mk ~~~~~~~~~~~~ - os.mkdir(os.path.join(iobnativebridgeif_dir, "software/pc-emul")) - pc_emul_str = """ -#nativebridge common parameters -include $(IOBNATIVEBRIDGEIF_DIR)/software/software.mk - -#pc sources -SRC+=$(IOBNATIVEBRIDGEIF_SW_DIR)/pc-emul/iob_nativebridgeif_csrs_emul.c - """ - fout = open( - os.path.join(iobnativebridgeif_dir, "software/pc-emul", "pc-emul.mk"), "w" - ) - fout.write(pc_emul_str) - fout.close() - # iob-nativebridgeif.c for pc-emul - fin = open( - os.path.join( - os.path.dirname(__file__), "../pc-emul/iob_regfileif_csrs_pc_emul.c" - ), - "r", - ) - src_content = fin.readlines() - fin.close() - for i in range(len(src_content)): - src_content[i] = re.sub( - "regfile", - "iobnativebridge", - re.sub("REGFILE", "IOBNATIVEBRIDGE", src_content[i]), - ) - fout = open( - os.path.join( - iobnativebridgeif_dir, "software/pc-emul", "iob_nativebridgeif_csrs_emul.c" - ), - "w", - ) - fout.writelines(src_content) - fout.close() - - -# Main function -if __name__ == "__main__": - if len(sys.argv) != 2: - print("Usage: {} ".format(sys.argv[0])) - print(" : Create IOBNATIVEBRIDGEIF in given path.") - quit() - else: - create_iobnativebridgeif(sys.argv[1]) diff --git a/lib/hardware/iob_regfileif/software/python/mkregsregfileif.py b/lib/hardware/iob_regfileif/software/python/mkregsregfileif.py deleted file mode 100755 index 8b0fbd343..000000000 --- a/lib/hardware/iob_regfileif/software/python/mkregsregfileif.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/python3 - -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# -# Build configured REGFILEIF/NATIVEBRIDGEIF registers and signals -# - -import sys -import os -import re - -mkregs_dir = "" - -if __name__ == "__main__": - # parse command line to get mkregs_dir - if len(sys.argv) != 5: - print( - "Usage: {} iob_corename_csrs.vh [HW|SW] [mkregs.py dir] [top_module_name]".format( - sys.argv[0] - ) - ) - print( - " iob_regfileif_csrs.vh:the software accessible registers definitions file" - ) - print( - " [HW|SW]: use HW to generate the hardware files or SW to generate the software files" - ) - print(" [mkregs.py dir]: directory of mkregs.py") - print(" [top_module_name]: Top/core module name") - quit() - else: - mkregs_dir = sys.argv[3] - -# Add folder to path that contains python scripts to be imported -sys.path.append(mkregs_dir) -from mkregs import * - - -# Change _gen.vh to connect to external native bus -def connect_to_external_native(filename): - fin = open(filename, "r") - file_contents = fin.readlines() - fin.close() - - for i in range(len(file_contents)): - file_contents[i] = re.sub( - "valid", - "valid_ext", - re.sub( - "wstrb", - "wstrb_ext", - re.sub( - "wdata", - "wdata_ext", - re.sub( - "rdata", - "rdata_ext", - re.sub( - "address", - "address_ext", - re.sub( - "ready", - "ready_ext", - re.sub("addr_reg", "addr_ext_reg", file_contents[i]), - ), - ), - ), - ), - ), - ) - - fout = open(filename, "w") - fout.writelines(file_contents) - fout.close() - - -# Create file with registers and connect wires from internal native and external native interfaces -def create_regs(filename, program): - file_contents = [] - - for line in program: - if line.startswith("//"): - continue # commented line - - subline = re.sub("\[|\]|:|,|//|\;", " ", line) - subline = re.sub("\(", " ", subline, 1) - subline = re.sub("\)", " ", subline, 1) - - flds = subline.split() - if not flds: - continue # empty line - # print flds[0] - if "csrs_" in flds[0]: # software accessible registers - reg_name = flds[1] # register name - reg_size_bits = int(flds[2]) * 8 # register size - reg_rst_val = flds[3] # register name - - file_contents.append("`IOB_WIRE({}, {})\n".format(reg_name, reg_size_bits)) - file_contents.append( - "iob_reg #(.DATA_W({}),.RST_VAL({}))\n".format( - reg_size_bits, reg_rst_val - ) - ) - file_contents.append("{} (\n".format(reg_name.lower())) - file_contents.append( - ".clk (clk),\n\ - .arst (rst),\n\ - .rst (rst),\n" - ) - # register type - if "_W" in flds[0]: # write register - file_contents.append( - ".en ({reg_name}_en),\n\ - .data_in ({reg_name}_wdata_ext),\n\ - .data_out ({reg_name}_INVERTED_rdata)\n".format( - reg_name=reg_name - ) - ) - else: # read register - file_contents.append( - ".en ({reg_name}_INVERTED_en),\n\ - .data_in ({reg_name}_INVERTED_wdata),\n\ - .data_out ({reg_name}_rdata_ext)\n".format( - reg_name=reg_name - ) - ) - file_contents.append(");\n") - - else: - continue # not a recognized macro - - fout = open(filename, "w") - fout.writelines(file_contents) - fout.close() - - -# Main function -if __name__ == "__main__": - infile = sys.argv[1] - hwsw = sys.argv[2] - corename = sys.argv[4] - - fin = open(infile, "r") - defsfile = fin.readlines() - fin.close() - - # Create normal csrs - csrs_parse(defsfile, hwsw, corename) - - if hwsw == "HW": - # Create regs - create_regs(corename + "_csrs_regs.vh", defsfile) - - # Change _gen.vh to connect to external native bus - connect_to_external_native(corename + "_csrs_gen.vh") - - # Create csrs with read and write registers inverted - corename = corename + "_inverted" - # invert registers type - for i in range(len(defsfile)): - if "csrs_W" in defsfile[i]: - defsfile[i] = re.sub( - "csrs_W\(([^,]+),", "csrs_R(\g<1>_INVERTED,", defsfile[i] - ) - else: - defsfile[i] = re.sub( - "csrs_R\(([^,]+),", "csrs_W(\g<1>_INVERTED,", defsfile[i] - ) - - if hwsw == "HW": - # write iob_COREPREFIX_inverted.vh file - fout = open(corename + ".vh", "w") - fout.writelines(defsfile) - fout.close() - - # create generated inverted files - csrs_parse(defsfile, hwsw, corename) - - # Hack to rename 'write_reg' and 'read_reg' inside iob_COREPREFIX_inverted_csrs_gen.vh, because it would cause duplicates if inverted and non inverted csrs_gen.vh files were included - if hwsw == "HW": - fin = open(corename + "_csrs_gen.vh", "r") - file_contents = fin.readlines() - fin.close() - - for i in range(len(file_contents)): - file_contents[i] = re.sub( - "write_reg", "write_reg_inverted", file_contents[i] - ) - file_contents[i] = re.sub("read_reg", "read_reg_inverted", file_contents[i]) - - fout = open(corename + "_csrs_gen.vh", "w") - fout.writelines(file_contents) - fout.close() diff --git a/lib/hardware/iob_system/README.md b/lib/hardware/iob_system/README.md deleted file mode 100644 index 4169f08c0..000000000 --- a/lib/hardware/iob_system/README.md +++ /dev/null @@ -1,328 +0,0 @@ - - -# IOb-SoC - -IOb-SoC is a System-on-Chip (SoC) template comprising an open-source RISC-V -processor (picorv32), an internal SRAM memory subsystem, a UART, and -an optional interface to external memory. If the external memory interface is -selected, an instruction L1 cache, a data L1 cache, and a shared L2 cache are -added to the system. The L2 cache communicates with a 3rd party memory -controller IP (typically a DDR controller) using an AXI4 master bus. - -## Nix environment - -You can use -[nix-shell](https://nixos.org/download.html#nix-install-linux) to run -IOb-SoC in a [Nix](https://nixos.org/) environment with all dependencies -available except for Vivado and Quartus for FPGA compilation and running. - -After installing `nix-shell,` it can be initialized by calling any Makefile target in the IOb-SoC root directory, for example -```Bash -make setup -``` - -The first time it runs, `nix-shell` will automatically install all the required dependencies. This can take a couple of hours, but after that, you can enjoy IOb-SoC and not worry about installing software tools. - - -## Dependencies - -If you prefer, you may install all the dependencies manually and run IOb-SoC without nix-shell. The following tools should be installed: -- GNU Bash >=5.1.16 -- GNU Make >=4.3 -- RISC-V GNU Compiler Toolchain =2022.06.10 (Instructions at the end of this README) -- Python3 >=3.10.6 -- Python3-Parse >=1.19.0 - -Optional tools, depending on the desired run strategy: -- Icarus Verilog >=10.3 -- Verilator >=5.002 -- gtkwave >=3.3.113 -- Vivado >=2020.2 -- Quartus >=20.1 - -Older versions of the dependencies above may work but still need to be tested. - - - -## Operating Systems - -IOb-SoC can be used in Linux Operating Systems. The following instructions work -for CentOS 7 and Ubuntu 18.04, 20.04, and 22.04 LTS. - -## Clone the repository - -The first step is to clone this repository. IOb-SoC uses git sub-module trees, and -GitHub will ask for your password for each downloaded module if you clone it by *https*. To avoid this, -setup GitHub access with *ssh* and type: - -```Bash -git clone --recursive git@github.com:IObundle/iob-system.git -cd iob-system -``` - -Alternatively, you can still clone this repository using *https* if you cache -your credentials before cloning the repository, using: ``git config --global -credential.helper 'cache --timeout='`` - - -## Configure your SoC - -To configure your system, edit the `iob_system.py` file, which can be found at the -repository root. This file has the system configuration variables; -hopefully, each variable is explained by a comment. - - -## Set environment variables for local or remote building and running - -The various simulators, FPGA compilers, and FPGA boards may run locally or -remotely. For running a tool remotely, you need to set two environmental -variables: the server logical name and the server user name. Consider placing -these settings in your `.bashrc` file so that they apply to every session. - - -### Set up the remote simulator server - -Using the open-source simulator Icarus Verilog (`iverilog`) as an example, note that in -`submodules/hardware/simulation/icarus.mk,` the variable for the server logical name, -`SIM_SERVER,` is set to `IVSIM_SERVER,` and the variable for the user name, -`SIM_USER` is set to `IVSIM_USER`. - -To run the simulator on the server *mysimserver.myorg.com* as user *ivsimuser*, set the following environmental -variables beforehand, or place them in your `.bashrc` file: - -```Bash -export IVSIM_SERVER=ivsimserver.myorg.com -export IVSIM_USER=ivsimuser -``` - -When you start the simulation, IOb-SoC's simulation Makefile will log you on to the server using `ssh,` then `rsync` the files to a remote build directory and run the simulation there. If you do not set these variables, the simulator will run locally if installed. - -### Set up the remote FPGA toolchain and board servers - -Using the cyclonev_gt_dk board as an example, note that in -`hardware/fpga/quartus/cyclonev_gt_dk/Makefile,` the variable for the FPGA tool -server logical name, `FPGA_SERVER,` is set to `QUARTUS_SERVER,` and the -variable for the user name, `FPGA_USER`, is set to `QUARTUS_USER`; the -variable for the board server, `BOARD_SERVER,` is set to `CYC5_SERVER`, and -the variable for the board user, `BOARD_USER,` is set to `CYC5_USER`. As in the -previous example, set these variables as follows: - -```Bash -export QUARTUS_SERVER=quartusserver.myorg.com -export QUARTUS_USER=quartususer -export CYC5_SERVER=cyc5server.myorg.com -export CYC5_USER=cyc5username -``` - -In each remote server, the environment variable for the license server used must be defined as in the following example: - -```Bash -export LM_LICENSE_FILE=port@licenseserver.myorg.com;lic_or_dat_file -``` - -## Create the build directory - -IOb-SoC uses intricate Python scripting to create a build directory with all the necessary files and makefiles to run the different tools. The build directory is placed in the folder above at ../iob_system_Vx.y by running the following command from the root directory. -```Bash -make setup -``` - -If you want to avoid getting into the complications of our Python scripts, use the ../iob_system_Vx.y directory to build your SoC. It only has code files and a few Makefiles. Enter this directory and call the available Makefile targets. Alternatively, using another Makefile in the IOb-SoC root directory, the same targets can be called. For example, to run the simulation, the IOb-SoC's top Makefile has the following target: - -```Bash -sim-run: - nix-shell --run 'make clean setup INIT_MEM=$(INIT_MEM) USE_EXTMEM=$(USE_EXTMEM) && make -C ../$(CORE)_V*/ sim-run SIMULATOR=$(SIMULATOR)' -``` -The above target invokes the `nix-shell` environment to call the local targets `clean` and `setup` and the target `sim-run` in the build directory. Below, the targets available in IOb-SoC's top Makefile are explained. - -## Emulate the system on PC - -You can *emulate* IOb-SoC's on a PC to develop and debug your embedded system. There is also a model to emulate the UART, which communicates with a run-time Python script server. If you develop peripherals, you can build embedded software models to run them using PC emulation. To emulate IOb-SoC's embedded software on a PC, type: - -```Bash -make pc-emul-run -``` - -The Makefile compiles and runs the software in the `../iob_system_Vx.y/software/` directory. The Makefile includes the `sw_build.mk` segment supplied initially in the `./software/` directory in the IOb-SoC root. Please feel free to change this file for your specific project. To run an emulation test comparing the result to the expected result, run -```Bash -make pc-emul-test -``` - -## Simulate the system - -To simulate IOb-SoC's RTL using a Verilog simulator, run -```Bash -make sim-run [SIMULATOR=icarus!verilator|xcelium|vcs|questa] [INIT_MEM=0|1] [USE_EXTMEM=0|1] -``` - -The INIT_MEM variable specifies whether the firmware is initially loaded in the memory, skipping the boot process, and the USE_EXTMEM variable indicates whether an external memory such as DRAM is used, in which case the cache system described above is instantiated. - -The Makefile compiles and runs the software in the `../iob_system_Vx.y/hardware/simulation` directory. The Makefile includes the `./hardware/simulation/sim_build.mk`, which you can change for your project. To run a simulation test comprising several simulations with different parameters, run -```Bash -make sim-test -``` -The simulation test contents can be edited in IOb-SoC's top Makefile. - -Each simulator must be described in the `./submodules/LIB/hardware/simulation/.mk` file. For example, the file `vcs.mk` describes the VCS simulator. - -The host machine must run an access server, a Python program in `./submodules/LIB/scripts/board_server.py,` set up to run as a service. The client connects to the host using the SSH protocol and runs the board client program `/submodules/LIB/scripts/board_client.py.` Note that the term *board* is used instead of *simulator* because the same server/client programs control the access to the board and FPGA compilers. The client requests the simulator for GRAB_TIMEOUT seconds, which is 300 seconds by default. Its value can be specified in the `./hardware/fpga/fpga_build.mk` Makefile segment, for example, as -```Bash -GRAB_TIMEOUT ?= 3600 -``` - - -## Build and run on FPGA board - -To build and run IOb-SoC on an FPGA board, the FPGA design tools must be -installed locally or remotely. The FPGA board must also be attached to the local -or remote host, not necessarily the same host where the design tools are installed. - -Each board must be described under the `/submodules/LIB/hardware/fpga//` directory. For example, the `hardware/fpga/vivado/BASYS3` -directory contents describe the board BASYS3, which has an FPGA device that can be programmed by the Xilinx/AMD Vivado design tool. The access to the board is controlled by the same server/client programs described above for the simulators. -To build an FPGA design of an IOb-SoC system and run it on the board located in the `board_dir` directory, type -```Bash -make fpga-run [BOARD=] [INIT_MEM=0|1] [USE_EXTMEM=0|1] -``` - -To run an FPGA test comparing the result to the expected result, run -```Bash -make fpga-test -``` -The FPGA test contents can be edited in IOb-SoC's top Makefile. - - -The remote machines that have an FPGA board attached to it must run our board -access control service script, which can be found in -`submodules/LIB/scripts/board_server.py`. When IOb-SoC needs to access a remote -FPGA server, it runs the board access script located in -`submodules/LIB/scripts/board_server.py`. - -To install `board_server.py` as a service, run the following command on the remote FPGA server: -``` -sudo make board_server_install -``` - -To uninstall the service, run - -``` -sudo make board_server_uninstall -``` - -Finally, to query the board status, run -``` -sudo make board_server_uninstall -``` - - -## Compile the documentation - -To compile documents, the LaTeX software must be installed. Three document types are generated: the Product Brief (pb), the User Guide (ug), and a presentation. To build a given document type DOC, run -```Bash -make doc-build [DOC=pb|ug|presentation] -``` - -To generate the three documents as a test, run -```Bash -make doc-test -``` - - -## Total test - -To run all simulation, FPGA board, and documentation tests, type: - -```Bash -make test-all -``` - -## Running more Makefile Targets - -The examples above are the Makefile targets at IOb-SoC's root directory that call the targets in the top Makefile in the build directory. Please explore the available targets in the build directory's top Makefile to add more targets to the root directory Makefile. - -## Cleaning the build directory -To clean the build directory, run -```Bash -make clean -``` - -## Instructions for Installing the RISC-V GNU Compiler Toolchain - -### Get sources and check out the supported stable version - -```Bash -git clone https://github.com/riscv/riscv-gnu-toolchain -cd riscv-gnu-toolchain -git checkout 2022.06.10 -``` - -### Prerequisites - -For the Ubuntu OS and its variants: - -```Bash -sudo apt install autoconf automake autotools-dev curl python3 python2 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev -``` - -For CentOS and its variants: - -```Bash -sudo yum install autoconf automake python3 python2 libmpc-devel mpfr-devel gmp-devel gawk bison flex texinfo patchutils gcc gcc-c++ zlib-devel expat-devel -``` - -### Installation - -```Bash -./configure --prefix=/path/to/riscv --enable-multilib -sudo make -j$(nproc) -``` - -This will take a while. After it is done, type: - -```Bash -export PATH=$PATH:/path/to/riscv/bin -``` - -The above command should be added to your `~/.bashrc` file so you do not have to type it on every session. - -## Ethernet - -To setup the system with ethernet capability, set the `USE_ETHERNET` macro value to `True`. - -When running the system with ethernet, please set the `RMAC_ADDR` and `IOB_CONSOLE_PYTHON_ENV` environment variables. -These values will select which network interface and which python environment to use for the console. - -For example, you can add the following to your `~/.bashrc`: - -```Bash -# IOb-SoC console network interface (loopback interfacce) -export RMAC_ADDR=000000000000 -# Custom IOb-SoC console python interperter with `CAP_NET_RAW` capability. -export IOB_CONSOLE_PYTHON_ENV=/opt/pyeth3/bin/python -``` - -You could also set those variables in the build directory's `config_build.mk` file. - -# Acknowledgements - -First of all, we acknowledge all the volunteer contributors for all their valuable pull requests, issues, and discussions. - -The work has been partially performed in the scope of the A-IQ Ready project, which receives funding within Chips Joint Undertaking (Chips JU) - the Public-Private Partnership for research, development, and innovation under Horizon Europe – and National Authorities under grant agreement No. 101096658. - -The A-IQ Ready project is supported by the Chips Joint Undertaking (Chips JU) - the Public-Private Partnership for research, development, and innovation under Horizon Europe – and National Authorities under Grant Agreement No. 101096658. - -![image](https://github.com/IObundle/iob-system/assets/5718971/78f2a3ee-d10b-4989-b221-71154fe6e409) ![image](https://github.com/IObundle/iob-system/assets/5718971/d57e0430-bb60-42e3-82a3-c5b6b0417322) - - -This project provides the basic infrastructure to other projects funded through the NGI Assure Fund, a fund established by NLnet -with financial support from the European Commission's Next Generation Internet program under the aegis of DG Communications Networks, Content, and Technology. - - - - - - -
NLnet foundation logoNGI Assure logo
diff --git a/lib/hardware/iob_system/document/doc_build.mk b/lib/hardware/iob_system/document/doc_build.mk deleted file mode 100644 index 16bafba69..000000000 --- a/lib/hardware/iob_system/document/doc_build.mk +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# (c) 2022-Present IObundle, Lda, all rights reserved -# -# This makefile segment is used at build-time in $(BUILD_DIR)/doc/Makefile -# - -#Set ASICSYNTH to 1 to include an ASIC synthesis section -ASICSYNTH?=0 - -#include implementation results; requires EDA tools -#default is 0 as EDA tools may not be accessible -RESULTS ?= 1 -#results for intel FPGA -INT_FAMILY ?=cyclonev_gt_dk -#results for xilinx fpga -XIL_FAMILY ?=aes_ku040_db_g - diff --git a/lib/hardware/iob_system/document/figures/bd.odg b/lib/hardware/iob_system/document/figures/bd.odg deleted file mode 100644 index 7baf0b05c10c654ad22b37f615efddf0fab36e64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24528 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

*Y^hTQ0S`w;ySOoqnKY-Py`u zyG8lpqx1`#Lx-gl7hj>yY}#|?~?85fJGVlGn2)~BWobBv+bBeb0x?s}j(?i*xmc(rn3rlzOM{oFDe zS6A7f_e)p&wx3`YS~Qe6nU1^cr9y10hMV*D5BWP>h)+Cs2GPtV=oie)u+n?>QN@Ua zPYZtqbK*O{vXr(_hNrPxV(Q6`<3zidz~MY@1HjLFFJo` ze*dpZOyqjdf1aklGr<3&^cU0bSK8l?s?wi!maO!X3I10T{>$%IEW-~L;7_|shBes# zBm@4_@z)wya)$0NM#uekhQnW;;J@(uf7ZzUbe-b+eZ}lwDu3WqbC_n%~U&tcZ+4jDUcFfI#HPqys_b9^gPgK=@Dl*M;C<<6z}+piX5wz+eI2k8LOB*W>*Z-x;jgQCD-pRz>!jNs98nvE}UK?BV<$(f>zl z?oLkj|Fbpf|IV7Lla;H5o7?{^N%!AK+Ble4S-9~?+qgTJIJ^B1&i;2ny1ARUd;DLF z{&#Z!3DVib(Zc?Ju=n4EX<}w(VQ=woJe~e)I503U{@1_sFS7qbul{jv?mqSwZrt7u z_WSy)uB-0|12$`nZEPRn59EXusjS$uew*`nvRGI(+d_WO6DEKAjDn8BB?hWXd!%Wj6l18EQIl|FstcDUVouO}6`3qej^gb;;d(RqBR`Tqb8HSviPZ4unMhUg z;S0^Tq6N(w`eWiy>iv1*N@K~A*;K8CbtU1>pn61=L8!S&w{)}va_(aMCA(SG1fjK1 z3PC2n{A{O#n9jZYJz?4}SFvW&41On%CdV^dW&Cj2h8lX*<^UJNhKfj(lFbu)j!Y$%`~urQ zBF$%euT|GX96y^p`_tM_T0NSt*158hgdPvyqs=FB{6Vt3|3o}tdDf^sNndwP^|{s# z-OaC0r>i!%Y=TfYU4^s+oVUvmUjk05>WjoC9Qa62u<#>|)N^L(Fb*|EL`RPUrF+;% z00q=b@(SOKapt0Hp#o0bKmx{PI2wBU>AJ;f4}~q3=5Zp@W5LhsuYb4ad5pW% zY}3tEz_dw4*n!30%U#V2Rr>x%NDGC9&Gb)!DUMdGOg>cJ(tOYXaWy+9MG=9*)yy5wfOP6ogo@LN$73h3*LmOyh<(sopth66sc39JY3Gcp zd#;ulZ6L}=P(l17L7@Ccq}_Z@bt>WJi{PMC3ww@nPkv0x@kczjD-EUtRz+#1z%

Q(?tqWc{}e|1B!S%ORU+< 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 O;7b-v(Wj%=ul@~D*f^>H diff --git a/lib/hardware/iob_system/document/figures/bd2.odg.license b/lib/hardware/iob_system/document/figures/bd2.odg.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_system/document/figures/bd2.odg.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_system/document/figures/csr_gen.odg b/lib/hardware/iob_system/document/figures/csr_gen.odg deleted file mode 100644 index a25d31e9f56fdc1c40dcc767eae1340cc3b344a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22060 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 diff --git a/lib/hardware/iob_system/document/figures/csr_gen.odg.license b/lib/hardware/iob_system/document/figures/csr_gen.odg.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_system/document/figures/csr_gen.odg.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_system/document/figures/inst.odg b/lib/hardware/iob_system/document/figures/inst.odg deleted file mode 100644 index 25da4a5fead1ea87b6ef91f56101f6cb4855b029..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11379 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/}, where is either *vivado* or *quartus*. Use the existing board directories as examples -\item Then issue the following command:\\ - {\tt make fpga-run BOARD= INIT\_MEM=0}\\ - This will compile the software and the hardware, produce an FPGA bitstream, - load it to the device, load the firmware binary using the UART ({\tt INIT\_MEM=0} prevents the FPGA memory initialisation), start the - program and direct the standard output to your PC terminal. -\item If you change only the firmware and repeat the above command, only the - firmware will be recompiled, reloaded and rerun. -\end{itemize} -\end{frame} - - -\begin{frame}[fragile]{Run IOb-SoC on an FPGA board (2)} -\begin{itemize} -\item When running IOb-SoC on an FPGA with the default settings and the firmware - pre-initialised in the memory, the following should be printed: -\end{itemize} - -\begin{tiny} - \begin{lstlisting} - -+-----------------------------------------------+ -| IOb-Console | -+-----------------------------------------------+ - - BaudRate = 115200 - StopBits = 1 - Parity = None - -IOb-Console: connecting... - -IOb-Bootloader: connected! -IOb-Bootloader: Restart CPU to run user program... - -Hello world! - -Execution time: 114466 clock cycles - -Execution time: 1145us @100MHz - -IOb-Console: exiting... - - \end{lstlisting} -\end{tiny} -\end{frame} - -\begin{frame}{Conclusion} - \begin{itemize} - \item A tutorial on creating a simple SoC using IOb-SoC has been presented - \item The addition of an example peripheral IP core has been illustrated - \item A simple firmware that uses the IP core driver functions has been explained - \item RTL simulation of the system running the firmware has been demonstrated - \item FPGA board example of the system running the firmware has been demonstrated - \end{itemize} -\end{frame} - -% for including figures in slides: -%\begin{frame}{Introduction} -%\begin{center} -% \begin{columns}[onlytextwidth] -% \column{0.5\textwidth} -% bla -% \column{0.5\textwidth} -% \begin{figure} -% \centering -% \includegraphics[width=0.9\textwidth]{turb.jpg} -% \caption{1. Flow visualisation (source: www.bronkhorst.com).} -% \label{fig:my_label} -% \end{figure} -% \end{columns} -%\end{center} -%\end{frame} - -\end{document} diff --git a/lib/hardware/iob_system/document/tsrc/results.tex b/lib/hardware/iob_system/document/tsrc/results.tex deleted file mode 100644 index de0a759fc..000000000 --- a/lib/hardware/iob_system/document/tsrc/results.tex +++ /dev/null @@ -1,5 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -The results obtained use all the default user configurable parameters, which affect the RAM usage results. diff --git a/lib/hardware/iob_system/document/tsrc/title.tex b/lib/hardware/iob_system/document/tsrc/title.tex deleted file mode 100644 index 7a9e793db..000000000 --- a/lib/hardware/iob_system/document/tsrc/title.tex +++ /dev/null @@ -1,6 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\title{IOb-SoC, a RISC-V SoC} -\header{IOb-SoC, a RISC-V SoC} diff --git a/lib/hardware/iob_system/document/tsrc/ug_title.tex b/lib/hardware/iob_system/document/tsrc/ug_title.tex deleted file mode 100644 index 8e72c5c5d..000000000 --- a/lib/hardware/iob_system/document/tsrc/ug_title.tex +++ /dev/null @@ -1,13 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -%replace ipcore-name by the name of your ip core (e.g. IOb-Cache) and description by a brief description (e.g. a Configurable Cache) - -\title{% -\Huge IOb-SoC \\ - \vspace*{3cm} -\Large System-on-Chip (SoC) template -} - -\header{IOb-SoC, System-on-Chip (SoC) template} diff --git a/lib/hardware/iob_system/hardware/fpga/fpga_build.mk b/lib/hardware/iob_system/hardware/fpga/fpga_build.mk deleted file mode 100644 index d775651dc..000000000 --- a/lib/hardware/iob_system/hardware/fpga/fpga_build.mk +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -include auto_fpga_build.mk - -# Add iob-system software as a build dependency -RUN_DEPS+=iob_system_bootrom.hex iob_system_firmware.hex -# Don't add firmware to BUILD_DEPS if we are not initializing memory since we don't want to rebuild the bitstream when we modify it. -BUILD_DEPS+=iob_system_bootrom.hex $(if $(filter $(INIT_MEM),1),iob_system_firmware.hex) - -QUARTUS_SEED ?=5 - -ROOT_DIR :=../.. -include $(ROOT_DIR)/software/sw_build.mk diff --git a/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/board.tcl b/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/board.tcl deleted file mode 100644 index 42b49e371..000000000 --- a/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/board.tcl +++ /dev/null @@ -1,513 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -set FAMILY "Cyclone V" -set PART "5CGTFD9E5F35C7" - -if {$IS_FPGA == "1"} { - set_global_assignment -name IOBANK_VCCIO 1.5V -section_id 4A - - # - # IO banks and pins - # - - set_global_assignment -name IOBANK_VCCIO 1.5V -section_id 4A - - # Pin & Location Assignments - - #System - - #DDR PLL ref clock 100MHz - #set_location_assignment PIN_H19 -to clk100 - #set_instance_assignment -name IO_STANDARD LVDS -to clk100 - - #User clock 50MHz - #set_location_assignment PIN_V28 -to clk50 - #set_instance_assignment -name IO_STANDARD "1.5 V" -to clk50 - set_location_assignment PIN_V28 -to clk_i - set_instance_assignment -name IO_STANDARD "1.5 V" -to clk_i - - #User reset - set_location_assignment PIN_AD29 -to resetn_i - set_instance_assignment -name IO_STANDARD "1.8 V" -to resetn_i - - - #Leds - #set_location_assignment PIN_AM23 -to led - #set_instance_assignment -name IO_STANDARD "2.5-V" -to led - - #set_location_assignment PIN_AE25 -to trap_o - #set_instance_assignment -name IO_STANDARD "1.5-V" -to trap_o - - #set_instance_assignment -name SLEW_RATE 1 -to trap_o - #set_instance_assignment -name CURRENT_STRENGTH_NEW DEFAULT -to trap_o - - #Uart - set_location_assignment PIN_F10 -to txd_o - set_instance_assignment -name IO_STANDARD "2.5-V" -to txd_o - #set_instance_assignment -name SLEW_RATE 1 -to txd_o - #set_instance_assignment -name CURRENT_STRENGTH_NEW DEFAULT -to txd_o - set_location_assignment PIN_C12 -to rxd_i - set_instance_assignment -name IO_STANDARD "2.5-V" -to rxd_i - - # #Ethernet - # set_location_assignment PIN_AN9 -to enet_resetn_o - # set_location_assignment PIN_AM10 -to enet_rx_clk_i - # set_location_assignment PIN_AP7 -to enet_gtx_clk_o - # set_location_assignment PIN_AK14 -to enet_rx_d0_i - # set_location_assignment PIN_AL10 -to enet_rx_d1_i - # set_location_assignment PIN_AJ14 -to enet_rx_d2_i - # set_location_assignment PIN_AK12 -to enet_rx_d3_i - # set_location_assignment PIN_AH14 -to enet_rx_dv_i - # set_location_assignment PIN_AB14 -to enet_tx_d0_o - # set_location_assignment PIN_AD15 -to enet_tx_d1_o - # set_location_assignment PIN_AB15 -to enet_tx_d2_o - # set_location_assignment PIN_AB13 -to enet_tx_d3_o - # set_location_assignment PIN_AC14 -to enet_tx_en_o - - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_resetn_o - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_rx_clk_i - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_gtx_clk_o - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_rx_d0_i - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_rx_d1_i - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_rx_d2_i - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_rx_d3_i - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_rx_dv_i - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_tx_d0_o - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_tx_d1_o - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_tx_d2_o - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_tx_d3_o - # set_instance_assignment -name IO_STANDARD "2.5-V" -to enet_tx_en_o - - #Force registers into IOBs - set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to * - set_instance_assignment -name FAST_INPUT_REGISTER ON -to * - set_instance_assignment -name FAST_OUTPUT_ENABLE_REGISTER ON -to * - - if { $USE_EXTMEM > 0 } { - - #set_instance_assignment -name IO_STANDARD "1.5-V" -to rzqin_i - #set_instance_assignment -name IO_STANDARD "1.5 V" -to rzqin_1_5v - - #DDR3 A - # set_location_assignment PIN_AA16 -to ddr3a_csn - # set_location_assignment PIN_AJ22 -to ddr3a_resetn - # set_location_assignment PIN_AA18 -to ddr3a_clk_p - # set_location_assignment PIN_AA17 -to ddr3a_clk_n - # set_location_assignment PIN_AN12 -to ddr3a_wen - # set_location_assignment PIN_AP14 -to ddr3a_rasn - # set_location_assignment PIN_AP15 -to ddr3a_casn - # set_location_assignment PIN_AP26 -to ddr3a_cke - # set_location_assignment PIN_AN21 -to ddr3a_odt - - # set_location_assignment PIN_AK18 -to ddr3a_a[0] - # set_location_assignment PIN_AL18 -to ddr3a_a[1] - # set_location_assignment PIN_AM18 -to ddr3a_a[2] - # set_location_assignment PIN_AN18 -to ddr3a_a[3] - # set_location_assignment PIN_AH17 -to ddr3a_a[4] - # set_location_assignment PIN_AJ17 -to ddr3a_a[5] - # set_location_assignment PIN_AK17 -to ddr3a_a[6] - # set_location_assignment PIN_AL17 -to ddr3a_a[7] - # set_location_assignment PIN_AH16 -to ddr3a_a[8] - # set_location_assignment PIN_AJ16 -to ddr3a_a[9] - # set_location_assignment PIN_AL16 -to ddr3a_a[10] - # set_location_assignment PIN_AM16 -to ddr3a_a[11] - # set_location_assignment PIN_AM13 -to ddr3a_a[12] - # set_location_assignment PIN_AN13 -to ddr3a_a[13] - # set_location_assignment PIN_AN16 -to ddr3a_ba[0] - # set_location_assignment PIN_AN17 -to ddr3a_ba[1] - # set_location_assignment PIN_AP17 -to ddr3a_ba[2] - - # set_location_assignment PIN_AN19 -to ddr3a_dq[0] - # set_location_assignment PIN_AM19 -to ddr3a_dq[1] - # set_location_assignment PIN_AP20 -to ddr3a_dq[2] - # set_location_assignment PIN_AP21 -to ddr3a_dq[3] - # set_location_assignment PIN_AH19 -to ddr3a_dq[4] - # set_location_assignment PIN_AG19 -to ddr3a_dq[5] - # set_location_assignment PIN_AJ19 -to ddr3a_dq[6] - # set_location_assignment PIN_AM21 -to ddr3a_dq[7] - # set_location_assignment PIN_AM20 -to ddr3a_dq[8] - # set_location_assignment PIN_AL20 -to ddr3a_dq[9] - # set_location_assignment PIN_AN22 -to ddr3a_dq[10] - # set_location_assignment PIN_AN23 -to ddr3a_dq[11] - # set_location_assignment PIN_AP24 -to ddr3a_dq[12] - # set_location_assignment PIN_AP25 -to ddr3a_dq[13] - # set_location_assignment PIN_AN26 -to ddr3a_dq[14] - # set_location_assignment PIN_AN24 -to ddr3a_dq[15] - # # set_location_assignment PIN_AP27 -to ddr3a_dq[16] - # # set_location_assignment PIN_AN27 -to ddr3a_dq[17] - # # set_location_assignment PIN_AK22 -to ddr3a_dq[18] - # # set_location_assignment PIN_AJ21 -to ddr3a_dq[19] - # # set_location_assignment PIN_AH21 -to ddr3a_dq[20] - # # set_location_assignment PIN_AH22 -to ddr3a_dq[21] - # # set_location_assignment PIN_AP30 -to ddr3a_dq[22] - # # set_location_assignment PIN_AN28 -to ddr3a_dq[23] - # # set_location_assignment PIN_AL23 -to ddr3a_dq[24] - # # set_location_assignment PIN_AK23 -to ddr3a_dq[25] - # # set_location_assignment PIN_AL25 -to ddr3a_dq[26] - # # set_location_assignment PIN_AM26 -to ddr3a_dq[27] - # # set_location_assignment PIN_AK24 -to ddr3a_dq[28] - # # set_location_assignment PIN_AJ24 -to ddr3a_dq[29] - # # set_location_assignment PIN_AN31 -to ddr3a_dq[30] - # # set_location_assignment PIN_AL28 -to ddr3a_dq[31] - # # set_location_assignment PIN_AH23 -to ddr3a_dq[32] - # # set_location_assignment PIN_AG23 -to ddr3a_dq[33] - # # set_location_assignment PIN_AN32 -to ddr3a_dq[34] - # # set_location_assignment PIN_AN29 -to ddr3a_dq[35] - # # set_location_assignment PIN_AK25 -to ddr3a_dq[36] - # # set_location_assignment PIN_AJ25 -to ddr3a_dq[37] - # # set_location_assignment PIN_AK28 -to ddr3a_dq[38] - # # set_location_assignment PIN_AM30 -to ddr3a_dq[39] - - # set_location_assignment PIN_AL21 -to ddr3a_dm[0] - # set_location_assignment PIN_AM24 -to ddr3a_dm[1] - # # set_location_assignment PIN_AM28 -to ddr3a_dm[2] - # # set_location_assignment PIN_AL27 -to ddr3a_dm[3] - # # set_location_assignment PIN_AL30 -to ddr3a_dm[4] - - # set_location_assignment PIN_AB19 -to ddr3a_dqs_p[0] - # set_location_assignment PIN_AD19 -to ddr3a_dqs_p[1] - # # set_location_assignment PIN_AJ20 -to ddr3a_dqs_p[2] - # # set_location_assignment PIN_Y20 -to ddr3a_dqs_p[3] - # # set_location_assignment PIN_AC21 -to ddr3a_dqs_p[4] - - # set_location_assignment PIN_AC19 -to ddr3a_dqs_n[0] - # set_location_assignment PIN_AE19 -to ddr3a_dqs_n[1] - # # set_location_assignment PIN_AK20 -to ddr3a_dqs_n[2] - # # set_location_assignment PIN_AA20 -to ddr3a_dqs_n[3] - # # set_location_assignment PIN_AD21 -to ddr3a_dqs_n[4] - - - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_csn - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_resetn - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_clk_p - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_clk_n - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_cke - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_wen - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_rasn - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_casn - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_odt - - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[0] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[1] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[2] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[3] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[4] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[5] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[6] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[7] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[8] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[9] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[10] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[11] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[12] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_a[13] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_ba[0] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_ba[1] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_ba[2] - - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[0] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[1] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[2] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[3] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[4] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[5] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[6] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[7] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[8] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[9] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[10] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[11] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[12] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[13] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[14] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[15] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[16] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[17] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[18] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[19] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[20] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[21] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[22] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[23] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[24] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[25] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[26] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[27] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[28] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[29] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[30] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[31] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[32] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[33] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[34] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[35] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[36] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[37] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[38] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dq[39] - - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dm[0] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dm[1] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dm[2] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dm[3] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dm[4] - - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_p[0] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_p[1] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_p[2] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_p[3] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_p[4] - - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_n[0] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_n[1] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_n[2] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_n[3] - # # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3a_dqs_n[4] - - - #DDR B - - set_location_assignment PIN_V27 -to ddr3b_csn_o - set_location_assignment PIN_R29 -to ddr3b_clk_n_o - set_location_assignment PIN_R30 -to ddr3b_clk_p_o - set_location_assignment PIN_AF32 -to ddr3b_cke_o - set_location_assignment PIN_AG31 -to ddr3b_resetn_o - set_location_assignment PIN_AA32 -to ddr3b_odt_o - set_location_assignment PIN_Y32 -to ddr3b_rasn_o - set_location_assignment PIN_N27 -to ddr3b_casn_o - set_location_assignment PIN_AM34 -to ddr3b_wen_o - - set_location_assignment PIN_H29 -to ddr3b_a_o[0] - set_location_assignment PIN_K28 -to ddr3b_a_o[1] - set_location_assignment PIN_K34 -to ddr3b_a_o[2] - set_location_assignment PIN_L32 -to ddr3b_a_o[3] - set_location_assignment PIN_R32 -to ddr3b_a_o[4] - set_location_assignment PIN_R33 -to ddr3b_a_o[5] - set_location_assignment PIN_N32 -to ddr3b_a_o[6] - set_location_assignment PIN_G33 -to ddr3b_a_o[7] - set_location_assignment PIN_AE34 -to ddr3b_a_o[8] - set_location_assignment PIN_L27 -to ddr3b_a_o[9] - set_location_assignment PIN_V33 -to ddr3b_a_o[10] - set_location_assignment PIN_U33 -to ddr3b_a_o[11] - set_location_assignment PIN_T31 -to ddr3b_a_o[12] - set_location_assignment PIN_T30 -to ddr3b_a_o[13] - set_location_assignment PIN_J31 -to ddr3b_ba_o[0] - set_location_assignment PIN_N29 -to ddr3b_ba_o[1] - set_location_assignment PIN_P27 -to ddr3b_ba_o[2] - - set_location_assignment PIN_AF31 -to ddr3b_dq_io[0] - set_location_assignment PIN_AD30 -to ddr3b_dq_io[1] - set_location_assignment PIN_AJ32 -to ddr3b_dq_io[2] - set_location_assignment PIN_AC31 -to ddr3b_dq_io[3] - set_location_assignment PIN_AH32 -to ddr3b_dq_io[4] - set_location_assignment PIN_Y28 -to ddr3b_dq_io[5] - set_location_assignment PIN_AN34 -to ddr3b_dq_io[6] - set_location_assignment PIN_Y27 -to ddr3b_dq_io[7] - set_location_assignment PIN_AD32 -to ddr3b_dq_io[8] - set_location_assignment PIN_AH33 -to ddr3b_dq_io[9] - set_location_assignment PIN_AB31 -to ddr3b_dq_io[10] - set_location_assignment PIN_AJ34 -to ddr3b_dq_io[11] - set_location_assignment PIN_AA31 -to ddr3b_dq_io[12] - set_location_assignment PIN_AK34 -to ddr3b_dq_io[13] - set_location_assignment PIN_W31 -to ddr3b_dq_io[14] - set_location_assignment PIN_AG33 -to ddr3b_dq_io[15] - # set_location_assignment PIN_AD34 -to ddr3b_dq_io[16] - # set_location_assignment PIN_AC33 -to ddr3b_dq_io[17] - # set_location_assignment PIN_AG34 -to ddr3b_dq_io[18] - # set_location_assignment PIN_AB33 -to ddr3b_dq_io[19] - # set_location_assignment PIN_AE33 -to ddr3b_dq_io[20] - # set_location_assignment PIN_V32 -to ddr3b_dq_io[21] - # set_location_assignment PIN_AH34 -to ddr3b_dq_io[22] - # set_location_assignment PIN_W32 -to ddr3b_dq_io[23] - # set_location_assignment PIN_U29 -to ddr3b_dq_io[24] - # set_location_assignment PIN_V34 -to ddr3b_dq_io[25] - # set_location_assignment PIN_U34 -to ddr3b_dq_io[26] - # set_location_assignment PIN_AA33 -to ddr3b_dq_io[27] - # set_location_assignment PIN_R34 -to ddr3b_dq_io[28] - # set_location_assignment PIN_Y33 -to ddr3b_dq_io[29] - # set_location_assignment PIN_P34 -to ddr3b_dq_io[30] - # set_location_assignment PIN_U28 -to ddr3b_dq_io[31] - # set_location_assignment PIN_T32 -to ddr3b_dq_io[32] - # set_location_assignment PIN_N33 -to ddr3b_dq_io[33] - # set_location_assignment PIN_T33 -to ddr3b_dq_io[34] - # set_location_assignment PIN_L33 -to ddr3b_dq_io[35] - # set_location_assignment PIN_T28 -to ddr3b_dq_io[36] - # set_location_assignment PIN_J34 -to ddr3b_dq_io[37] - # set_location_assignment PIN_T27 -to ddr3b_dq_io[38] - # set_location_assignment PIN_M34 -to ddr3b_dq_io[39] - # set_location_assignment PIN_K33 -to ddr3b_dq_io[40] - # set_location_assignment PIN_N31 -to ddr3b_dq_io[41] - # set_location_assignment PIN_G34 -to ddr3b_dq_io[42] - # set_location_assignment PIN_R28 -to ddr3b_dq_io[43] - # set_location_assignment PIN_H33 -to ddr3b_dq_io[44] - # set_location_assignment PIN_P32 -to ddr3b_dq_io[45] - # set_location_assignment PIN_H34 -to ddr3b_dq_io[46] - # set_location_assignment PIN_R27 -to ddr3b_dq_io[47] - # set_location_assignment PIN_N28 -to ddr3b_dq_io[48] - # set_location_assignment PIN_L30 -to ddr3b_dq_io[49] - # set_location_assignment PIN_P30 -to ddr3b_dq_io[50] - # set_location_assignment PIN_K30 -to ddr3b_dq_io[51] - # set_location_assignment PIN_J32 -to ddr3b_dq_io[52] - # set_location_assignment PIN_H32 -to ddr3b_dq_io[53] - # set_location_assignment PIN_M31 -to ddr3b_dq_io[54] - # set_location_assignment PIN_H31 -to ddr3b_dq_io[55] - # set_location_assignment PIN_G30 -to ddr3b_dq_io[56] - # set_location_assignment PIN_K29 -to ddr3b_dq_io[57] - # set_location_assignment PIN_G31 -to ddr3b_dq_io[58] - # set_location_assignment PIN_M30 -to ddr3b_dq_io[59] - # set_location_assignment PIN_J30 -to ddr3b_dq_io[60] - # set_location_assignment PIN_M29 -to ddr3b_dq_io[61] - # set_location_assignment PIN_J29 -to ddr3b_dq_io[62] - # set_location_assignment PIN_L28 -to ddr3b_dq_io[63] - - - set_location_assignment PIN_AE30 -to ddr3b_dm_o[0] - set_location_assignment PIN_AE32 -to ddr3b_dm_o[1] - # set_location_assignment PIN_AC34 -to ddr3b_dm_o[2] - # set_location_assignment PIN_W34 -to ddr3b_dm_o[3] - # set_location_assignment PIN_M33 -to ddr3b_dm_o[4] - # set_location_assignment PIN_K32 -to ddr3b_dm_o[5] - # set_location_assignment PIN_L31 -to ddr3b_dm_o[6] - # set_location_assignment PIN_H28 -to ddr3b_dm_o[7] - - set_location_assignment PIN_Y29 -to ddr3b_dqs_p_io[0] - set_location_assignment PIN_W29 -to ddr3b_dqs_p_io[1] - # set_location_assignment PIN_V24 -to ddr3b_dqs_p_io[2] - # set_location_assignment PIN_U24 -to ddr3b_dqs_p_io[3] - # set_location_assignment PIN_U23 -to ddr3b_dqs_p_io[4] - # set_location_assignment PIN_T25 -to ddr3b_dqs_p_io[5] - # set_location_assignment PIN_R23 -to ddr3b_dqs_p_io[6] - # set_location_assignment PIN_P24 -to ddr3b_dqs_p_io[7] - set_location_assignment PIN_Y30 -to ddr3b_dqs_n_io[0] - set_location_assignment PIN_W30 -to ddr3b_dqs_n_io[1] - # set_location_assignment PIN_V23 -to ddr3b_dqs_n_io[2] - # set_location_assignment PIN_U25 -to ddr3b_dqs_n_io[3] - # set_location_assignment PIN_T23 -to ddr3b_dqs_n_io[4] - # set_location_assignment PIN_R25 -to ddr3b_dqs_n_io[5] - # set_location_assignment PIN_R24 -to ddr3b_dqs_n_io[6] - # set_location_assignment PIN_P25 -to ddr3b_dqs_n_io[7] - - - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_csn_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_resetn_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_clk_n_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_clk_p_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_cke_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_wen_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_rasn_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_casn_o - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_odt_o - - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[0] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[1] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[10] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[11] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[12] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[13] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[2] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[3] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[4] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[5] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[6] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[7] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[8] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_a_o[9] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_ba_o[0] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_ba_o[1] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_ba_o[2] - - - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[0] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[1] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[2] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[3] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[4] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[5] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[6] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[7] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[8] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[9] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[10] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[11] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[12] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[13] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[14] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[15] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[16] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[17] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[18] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[19] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[20] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[21] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[22] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[23] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[24] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[25] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[26] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[27] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[28] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[29] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[30] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[31] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[32] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[33] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[34] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[35] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[36] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[37] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[38] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[39] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[40] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[41] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[42] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[43] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[44] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[45] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[46] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[47] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[48] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[49] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[50] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[51] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[52] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[53] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[54] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[55] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[56] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[57] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[58] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[59] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[60] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[61] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[62] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dq_io[63] - - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[0] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[1] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[2] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[3] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[4] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[5] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[6] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dm_o[7] - - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[0] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[1] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[2] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[3] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[4] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[5] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[6] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_p_io[7] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[0] - set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[1] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[2] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[3] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[4] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[5] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[6] - # set_instance_assignment -name IO_STANDARD "SSTL-15 CLASS I" -to ddr3b_dqs_n_io[7] - } -} diff --git a/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.py b/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.py deleted file mode 100644 index 7efb04118..000000000 --- a/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.py +++ /dev/null @@ -1,547 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - params = py_params_dict["iob_system_params"] - - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "AXI_ID_W", - "descr": "AXI ID bus width", - "type": "F", - "val": "1", - "min": "1", - "max": "32", - }, - { - "name": "AXI_LEN_W", - "descr": "AXI burst length width", - "type": "F", - "val": "4", - "min": "1", - "max": "8", - }, - { - "name": "AXI_ADDR_W", - "descr": "AXI address bus width", - "type": "F", - "val": "`DDR_ADDR_W" if params["use_extmem"] else "15", - "min": "1", - "max": "32", - }, - { - "name": "AXI_DATA_W", - "descr": "AXI data bus width", - "type": "F", - "val": "`DDR_DATA_W", - "min": "1", - "max": "32", - }, - ], - } - # - # Ports - # - attributes_dict["ports"] = [ - { - "name": "clk_rst_i", - "descr": "Clock and reset", - "signals": [ - {"name": "clk", "direction": "input", "width": "1"}, - {"name": "resetn", "direction": "input", "width": "1"}, - ], - }, - { - "name": "rs232", - "descr": "Serial port", - "signals": [ - {"name": "txd", "direction": "output", "width": "1"}, - {"name": "rxd", "direction": "input", "width": "1"}, - ], - }, - ] - if params["use_extmem"]: - attributes_dict["ports"] += [ - { - "name": "ddr3", - "descr": "External DDR3 memory interface", - "signals": [ - {"name": "ddr3b_a", "direction": "output", "width": "14"}, - {"name": "ddr3b_ba", "direction": "output", "width": "3"}, - {"name": "ddr3b_rasn", "direction": "output", "width": "1"}, - {"name": "ddr3b_casn", "direction": "output", "width": "1"}, - {"name": "ddr3b_wen", "direction": "output", "width": "1"}, - {"name": "ddr3b_dm", "direction": "output", "width": "2"}, - {"name": "ddr3b_dq", "direction": "inout", "width": "16"}, - {"name": "ddr3b_clk_n", "direction": "output", "width": "1"}, - {"name": "ddr3b_clk_p", "direction": "output", "width": "1"}, - {"name": "ddr3b_cke", "direction": "output", "width": "1"}, - {"name": "ddr3b_csn", "direction": "output", "width": "1"}, - {"name": "ddr3b_dqs_n", "direction": "inout", "width": "2"}, - {"name": "ddr3b_dqs_p", "direction": "inout", "width": "2"}, - {"name": "ddr3b_odt", "direction": "output", "width": "1"}, - {"name": "ddr3b_resetn", "direction": "output", "width": "1"}, - ], - }, - { - "name": "rzqin_i", - "descr": "", - "signals": [ - {"name": "rzqin", "direction": "input", "width": "1"}, - ], - }, - ] - if params["use_ethernet"]: - attributes_dict["ports"] += [ - { - "name": "mii", - "descr": "MII ethernet interface", - "signals": [ - {"name": "enet_resetn", "direction": "output", "width": "1"}, - {"name": "enet_rx_clk", "direction": "input", "width": "1"}, - {"name": "enet_gtx_clk", "direction": "output", "width": "1"}, - {"name": "enet_rx_d0", "direction": "input", "width": "1"}, - {"name": "enet_rx_d1", "direction": "input", "width": "1"}, - {"name": "enet_rx_d2", "direction": "input", "width": "1"}, - {"name": "enet_rx_d3", "direction": "input", "width": "1"}, - {"name": "enet_rx_dv", "direction": "input", "width": "1"}, - # {"name": "enet_rx_err", "direction": "output", "width": "1"}, - {"name": "enet_tx_d0", "direction": "output", "width": "1"}, - {"name": "enet_tx_d1", "direction": "output", "width": "1"}, - {"name": "enet_tx_d2", "direction": "output", "width": "1"}, - {"name": "enet_tx_d3", "direction": "output", "width": "1"}, - {"name": "enet_tx_en", "direction": "output", "width": "1"}, - # {"name": "enet_tx_err", "direction": "output", "width": "1"}, - ], - }, - ] - - # - # Wires - # - attributes_dict["wires"] = [ - { - "name": "clk_en_rst", - "descr": "Clock, clock enable and reset", - "signals": [ - {"name": "clk"}, - {"name": "cke", "width": "1"}, - {"name": "arst", "width": "1"}, - ], - }, - { - "name": "rs232_int", - "descr": "iob-system uart interface", - "signals": [ - {"name": "rxd"}, - {"name": "txd"}, - {"name": "rs232_rts", "width": "1"}, - {"name": "high", "width": "1"}, - ], - }, - { - "name": "axi", - "descr": "AXI interface to connect SoC to memory", - "interface": { - "type": "axi", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - { - "name": "reset_sync_clk_rst", - "descr": "Reset synchronizer inputs", - "signals": [ - {"name": "clk"}, - { - "name": "rst_int" if params["use_extmem"] else "resetn_inv", - "width": "1", - }, - ], - }, - { - "name": "reset_sync_arst_out", - "descr": "Reset synchronizer output", - "signals": [ - {"name": "arst"}, - ], - }, - { - "name": "clk", - "descr": "Clock signal", - "signals": [ - {"name": "clk"}, - ], - }, - ] - - if params["use_extmem"]: - attributes_dict["wires"] += [ - # DDR3 ctrl - { - "name": "ddr3_ctr_clk_rst", - "descr": "DDR3 controller clock and areset inputs", - "signals": [ - {"name": "clk"}, - {"name": "resetn"}, - ], - }, - { - "name": "ddr3_ctr_general", - "descr": "DDR3 controller general signals", - "signals": [ - {"name": "rzqin"}, - {"name": "pll_locked"}, - {"name": "init_done"}, - ], - }, - ] - if not params["use_extmem"]: - attributes_dict["wires"] += [ - { - "name": "memory_axi", - "descr": "AXI bus to connect interconnect and memory", - "interface": { - "type": "axi", - "wire_prefix": "mem_", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - ] - if params["use_ethernet"]: - attributes_dict["wires"] += [ - # eth clock - { - "name": "rxclk_buf_io", - "descr": "rxclkbuf io", - "signals": [ - {"name": "enet_rx_clk"}, - {"name": "eth_clk", "width": "1"}, - ], - }, - { - "name": "ddio_out_clkbuf_io", - "descr": "DDIO clock buffer io", - "signals": [ - { - "name": "enet_resetn_inv", - "width": "1", - }, # TODO: Connect and invert enet_resetn - {"name": "low", "width": "1"}, - {"name": "high"}, - {"name": "eth_clk"}, - {"name": "enet_gtx_clk"}, - ], - }, - ] - # - # Blocks - # - attributes_dict["blocks"] = [ - { - "core_name": "iob_system_mwrap", - "instance_name": "iob_system_mwrap", - "instance_description": "IOb-SoC memory wrapper", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst", - "rs232_m": "rs232_int", - "axi_m": "axi", - }, - "dest_dir": "hardware/common_src", - "iob_system_params": params, - }, - { - "core_name": "iob_reset_sync", - "instance_name": "rst_sync", - "instance_description": "Reset synchronizer", - "connect": { - "clk_rst_s": "reset_sync_clk_rst", - "arst_o": "reset_sync_arst_out", - }, - }, - ] - if params["use_extmem"]: - # DDR3 controller - attributes_dict["blocks"] += [ - { - "core_name": "altera_alt_ddr3", - "instance_name": "ddr3_ctrl", - "instance_description": "DDR3 controller", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_rst_i": "ddr3_ctr_clk_rst", - "general": "ddr3_ctr_general", - "ddr3": "ddr3", - "s0_axi_s": "axi", - }, - }, - ] - if not params["use_extmem"]: - attributes_dict["blocks"] += [ - { - "core_name": "axi_interconnect_wrapper", - "name": "fpga_axi_interconnect_wrapper", - "instance_name": "axi_interconnect", - "instance_description": "Interconnect instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_i": "clk", - "rst_i": "reset_sync_arst_out", - "s0_axi_s": "axi", - "m0_axi_m": "memory_axi", - }, - "num_slaves": 1, - }, - { - "core_name": "axi_ram", - "instance_name": "ddr_model_mem", - "instance_description": "DDR model memory", - "parameters": { - "ID_WIDTH": "AXI_ID_W", - "ADDR_WIDTH": "AXI_ADDR_W", - "DATA_WIDTH": "AXI_DATA_W", - "READ_ON_WRITE": "0", - }, - "connect": { - "clk_i": "clk", - "rst_i": "reset_sync_arst_out", - "axi_s": "memory_axi", - }, - }, - ] - if params["init_mem"]: - attributes_dict["blocks"][-1]["parameters"].update( - { - "FILE": f'"{params["name"]}_firmware"', - } - ) - if params["use_ethernet"]: - # Eth clock - attributes_dict["blocks"] += [ - { - "core_name": "altera_clk_buf_altclkctrl", - "instance_name": "rxclk_buf", - "instance_description": "RX clock buffer", - "connect": { - "io": "rxclk_buf_io", - }, - }, - { - "core_name": "altera_ddio_out_clkbuf", - "instance_name": "ddio_out_clkbuf_inst", - "instance_description": "DDIO out clock buffer", - "connect": { - "io": "ddio_out_clkbuf_io", - }, - }, - ] - - # - # Snippets - # - attributes_dict["snippets"] = [ - { - "verilog_code": """ - // General connections - assign high = 1'b1; - assign cke = 1'b1; -""", - }, - ] - if params["use_extmem"]: - attributes_dict["snippets"] += [ - { - "verilog_code": """ - assign rst_int = ~resetn_i | ~pll_locked | ~init_done; -""", - }, - ] - else: # Not use_extmem - attributes_dict["snippets"] += [ - { - "verilog_code": """ - assign resetn_inv = ~resetn_i; -""", - }, - ] - if params["use_ethernet"]: - attributes_dict["snippets"] += [ - { - "verilog_code": """ - // Ethernet connections - assign low = 1'b0; -""", - }, - ] - - return attributes_dict - - -# TODO: Add slave ports to alt_ddr3.qsys, based on number of extmem connections -# -# def modify_alt_ddr3_qsys(qsys_path, num_extmem_connections): -# with open(qsys_path, "r") as f: -# lines = f.readlines() -# new_lines = [] -# -# for line in lines: -# new_lines.append(line) -# if "element clk_0" in line: -# for i in range(1, num_extmem_connections): -# new_lines.insert( -# -1, -# f""" -# element axi_bridge_{i} -# {{ -# datum _sortIndex -# {{ -# value = "{i+2}"; -# type = "int"; -# }} -# }} -# \n""", -# ) -# elif 'interface name="clk"' in line: -# for i in range(1, num_extmem_connections): -# new_lines.insert( -# -1, -# f""" -# -# \n""", -# ) -# elif 'module name="clk_0"' in line: -# for i in range(1, num_extmem_connections): -# new_lines.insert( -# -1, -# f""" -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# -# \n""", -# ) -# elif 'end="axi_bridge_0.clk"' in line: -# for i in range(1, num_extmem_connections): -# new_lines.insert( -# -1, -# f""" -# -# -# -# -# -# -# \n""", -# ) -# elif 'name="qsys_mm.clockCrossingAdapter"' in line: -# for i in range(1, num_extmem_connections): -# new_lines.insert( -# -1, -# f""" -# -# \n""", -# ) -# -# with open(qsys_path, "w") as f: -# f.writelines(new_lines) diff --git a/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.sdc b/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.sdc deleted file mode 100644 index 94cc2920e..000000000 --- a/lib/hardware/iob_system/hardware/fpga/quartus/cyclonev_gt_dk/cyclonev_gt_dk.sdc +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -create_clock -name "clk" -period 20.0 [get_ports {clk_i}] -#create_clock -period 40 [get_ports {enet_rx_clk_i}] diff --git a/lib/hardware/iob_system/hardware/fpga/quartus/quartus.sdc b/lib/hardware/iob_system/hardware/fpga/quartus/quartus.sdc deleted file mode 100644 index a88bc8a67..000000000 --- a/lib/hardware/iob_system/hardware/fpga/quartus/quartus.sdc +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -derive_pll_clocks - -derive_clock_uncertainty - diff --git a/lib/hardware/iob_system/hardware/fpga/quartus/quartus_postmap.tcl b/lib/hardware/iob_system/hardware/fpga/quartus/quartus_postmap.tcl deleted file mode 100644 index ca6f22139..000000000 --- a/lib/hardware/iob_system/hardware/fpga/quartus/quartus_postmap.tcl +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -if { $USE_EXTMEM == "1" } { - source "db/ip/alt_ddr3/submodules/alt_ddr3_mem_if_ddr3_emif_0_p0_parameters.tcl" - - source "db/ip/alt_ddr3/submodules/alt_ddr3_mem_if_ddr3_emif_0_p0_pin_assignments.tcl" -} diff --git a/lib/hardware/iob_system/hardware/fpga/src/fpga.sdc b/lib/hardware/iob_system/hardware/fpga/src/fpga.sdc deleted file mode 100644 index 7ad1cb2b5..000000000 --- a/lib/hardware/iob_system/hardware/fpga/src/fpga.sdc +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -#no project specific constraints diff --git a/lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.py b/lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.py deleted file mode 100644 index 842097076..000000000 --- a/lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.py +++ /dev/null @@ -1,420 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - # user-passed parameters - params = py_params_dict["iob_system_params"] - - attributes_dict = { - "version": "0.1", - # - # Configuration - # - "confs": [ - { - "name": "AXI_ID_W", - "descr": "AXI ID bus width", - "type": "F", - "val": "4", - "min": "1", - "max": "32", - }, - { - "name": "AXI_LEN_W", - "descr": "AXI burst length width", - "type": "F", - "val": "8", - "min": "1", - "max": "8", - }, - { - "name": "AXI_ADDR_W", - "descr": "AXI address bus width", - "type": "F", - "val": "`DDR_ADDR_W" if params["use_extmem"] else "20", - "min": "1", - "max": "32", - }, - { - "name": "AXI_DATA_W", - "descr": "AXI data bus width", - "type": "F", - "val": "`DDR_DATA_W", - "min": "1", - "max": "32", - }, - ], - } - # - # Ports - # - attributes_dict["ports"] = [ - { - "name": "clk_rst_i", - "descr": "Clock and reset", - "signals": [ - {"name": "c0_sys_clk_clk_p", "direction": "input", "width": "1"}, - {"name": "c0_sys_clk_clk_n", "direction": "input", "width": "1"}, - {"name": "areset", "direction": "input", "width": "1"}, - ], - }, - { - "name": "rs232", - "descr": "Serial port", - "signals": [ - {"name": "txd", "direction": "output", "width": "1"}, - {"name": "rxd", "direction": "input", "width": "1"}, - ], - }, - ] - if params["use_extmem"]: - attributes_dict["ports"] += [ - { - "name": "ddr4_pins", - "descr": "External DDR4 memory interface", - "signals": [ - {"name": "c0_ddr4_act_n", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_adr", "direction": "output", "width": "17"}, - {"name": "c0_ddr4_ba", "direction": "output", "width": "2"}, - {"name": "c0_ddr4_bg", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_cke", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_odt", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_cs_n", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_ck_t", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_ck_c", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_reset_n", "direction": "output", "width": "1"}, - {"name": "c0_ddr4_dm_dbi_n", "direction": "inout", "width": "4"}, - {"name": "c0_ddr4_dq", "direction": "inout", "width": "32"}, - {"name": "c0_ddr4_dqs_c", "direction": "inout", "width": "4"}, - {"name": "c0_ddr4_dqs_t", "direction": "inout", "width": "4"}, - ], - }, - ] - if params["use_ethernet"]: - attributes_dict["ports"] += [ - { - "name": "mii", - "descr": "MII ethernet interface", - "signals": [ - {"name": "enet_resetn", "direction": "output", "width": "1"}, - {"name": "enet_rx_clk", "direction": "input", "width": "1"}, - {"name": "enet_gtx_clk", "direction": "output", "width": "1"}, - {"name": "enet_rx_d0", "direction": "input", "width": "1"}, - {"name": "enet_rx_d1", "direction": "input", "width": "1"}, - {"name": "enet_rx_d2", "direction": "input", "width": "1"}, - {"name": "enet_rx_d3", "direction": "input", "width": "1"}, - {"name": "enet_rx_dv", "direction": "input", "width": "1"}, - # {"name": "enet_rx_err", "direction": "output", "width": "1"}, - {"name": "enet_tx_d0", "direction": "output", "width": "1"}, - {"name": "enet_tx_d1", "direction": "output", "width": "1"}, - {"name": "enet_tx_d2", "direction": "output", "width": "1"}, - {"name": "enet_tx_d3", "direction": "output", "width": "1"}, - {"name": "enet_tx_en", "direction": "output", "width": "1"}, - # {"name": "enet_tx_err", "direction": "output", "width": "1"}, - ], - }, - ] - - # - # Wires - # - attributes_dict["wires"] = [ - { - "name": "clk_en_rst", - "descr": "Clock, clock enable and reset", - "interface": { - "type": "clk_en_rst", - }, - }, - { - "name": "rs232_int", - "descr": "iob-system uart interface", - "signals": [ - {"name": "rxd"}, - {"name": "txd"}, - {"name": "rs232_rts", "width": "1"}, - {"name": "high", "width": "1"}, - ], - }, - { - "name": "axi", - "descr": "AXI interface to connect SoC to memory", - "interface": { - "type": "axi", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - { - "name": "intercon_clk_rst", - "descr": "AXI interconnect clock and reset inputs", - "signals": [ - { - "name": "ddr4_axi_clk" if params["use_extmem"] else "clk", - "width": 1, - }, - {"name": "intercon_rst", "width": "1"}, - ], - }, - { - "name": "intercon_s0_clk_rst", - "descr": "Interconnect slave 0 clock reset interface", - "signals": [ - {"name": "clk"}, - {"name": "intercon_s0_arstn", "width": "1"}, - ], - }, - { - "name": "intercon_m0_clk_rst", - "descr": "Interconnect master 0 clock and reset", - "signals": [ - {"name": "ddr4_axi_clk" if params["use_extmem"] else "clk"}, - {"name": "intercon_m0_arstn", "width": "1"}, - ], - }, - { - "name": "memory_axi", - "descr": "AXI bus to connect interconnect and memory", - "interface": { - "type": "axi", - "wire_prefix": "mem_", - "ID_W": "AXI_ID_W", - "LEN_W": "AXI_LEN_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1 if params["use_extmem"] else 2, - }, - }, - ] - if params["use_extmem"]: - attributes_dict["wires"] += [ - { - "name": "ddr4_ui_clk_out", - "descr": "DDR4 user interface clock output", - "signals": [ - {"name": "clk"}, - ], - }, - { - "name": "ddr4_axi_clk_rst", - "descr": "ddr4 axi clock and resets", - "signals": [ - {"name": "ddr4_axi_clk"}, - {"name": "intercon_rst"}, - {"name": "intercon_m0_arstn"}, - ], - }, - ] - if not params["use_extmem"]: - attributes_dict["wires"] += [ - { - "name": "clk_wizard_out", - "descr": "Connect clock wizard outputs to iob-system clock and reset", - "signals": [ - {"name": "clk"}, - {"name": "intercon_rst"}, - ], - }, - { - "name": "axi_ram_clk", - "descr": "AXI RAM clock input", - "signals": [ - {"name": "clk"}, - ], - }, - { - "name": "axi_ram_rst", - "descr": "AXI RAM reset input", - "signals": [ - {"name": "axi_ram_rst", "width": "1"}, - ], - }, - ] - if params["use_ethernet"]: - attributes_dict["wires"] += [ - # eth clock - { - "name": "rxclk_buf_io", - "descr": "IBUFG io", - "signals": [ - {"name": "enet_rx_clk"}, - {"name": "eth_clk", "width": "1"}, - ], - }, - { - "name": "oddre1_io", - "descr": "ODDRE1 io", - "signals": [ - {"name": "enet_gtx_clk"}, - {"name": "eth_clk"}, - {"name": "high"}, - {"name": "low", "width": "1"}, - { - "name": "enet_resetn_inv", - "width": "1", - }, - ], - }, - ] - - # - # Blocks - # - attributes_dict["blocks"] = [ - { - # IOb-SoC Memory Wrapper - "core_name": "iob_system_mwrap", - "instance_name": "iob_system_mwrap", - "instance_description": "IOb-SoC instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst", - "rs232_m": "rs232_int", - "axi_m": "axi", - }, - "dest_dir": "hardware/common_src", - "iob_system_params": params, - }, - { - "core_name": "xilinx_axi_interconnect", - "instance_name": "axi_async_bridge", - "instance_description": "Interconnect instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_rst_s": "intercon_clk_rst", - "m0_clk_rst": "intercon_m0_clk_rst", - "m0_axi_m": "memory_axi", - "s0_clk_rst": "intercon_s0_clk_rst", - "s0_axi_s": "axi", - }, - "num_slaves": 1, - }, - ] - if params["use_extmem"]: - # DDR4 controller - attributes_dict["blocks"] += [ - { - "core_name": "xilinx_ddr4_ctrl", - "instance_name": "ddr4_ctrl", - "instance_description": "DDR4 controller instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_rst_i": "clk_rst_i", - "ui_clk_o": "ddr4_ui_clk_out", - "axi_clk_rst": "ddr4_axi_clk_rst", - "axi_s": "memory_axi", - "ddr4": "ddr4_pins", - }, - }, - ] - if not params["use_extmem"]: - # Clock wizard - attributes_dict["blocks"] += [ - { - "core_name": "xilinx_clock_wizard", - "instance_name": "clk_250_to_100_MHz", - "instance_description": "PLL to generate system clock", - "parameters": { - "OUTPUT_PER": 10, - "INPUT_PER": 4, - }, - "connect": { - "clk_rst_i": "clk_rst_i", - "clk_rst_o": "clk_wizard_out", - }, - }, - { - "core_name": "axi_ram", - "instance_name": "ddr_model_mem", - "instance_description": "DDR model memory", - "parameters": { - "ID_WIDTH": "AXI_ID_W", - "ADDR_WIDTH": "AXI_ADDR_W", - "DATA_WIDTH": "AXI_DATA_W", - "READ_ON_WRITE": "1", - }, - "connect": { - "clk_i": "axi_ram_clk", - "rst_i": "axi_ram_rst", - "axi_s": "memory_axi", - }, - }, - ] - if params["init_mem"]: - attributes_dict["blocks"][-1]["parameters"].update( - { - "FILE": f'"{params["name"]}_firmware"', - } - ) - if params["use_ethernet"]: - # Eth clock - attributes_dict["blocks"] += [ - { - "core_name": "xilinx_ibufg", - "instance_name": "rxclk_buf", - "connect": { - "io": "rxclk_buf_io", - }, - }, - { - "core_name": "xilinx_oddre1", - "instance_name": "oddre1_inst", - "connect": { - "io": "oddre1_io", - }, - }, - ] - - # - # Snippets - # - attributes_dict["snippets"] = [ - { - "verilog_code": """ - // General connections - assign high = 1'b1; - assign cke = 1'b1; - assign arst = ~intercon_s0_arstn; -""", - }, - ] - if params["use_ethernet"]: - attributes_dict["snippets"] += [ - { - "verilog_code": """ - // Ethernet connections - assign low = 1'b0; - assign enet_resetn_inv = ~enet_resetn; -""", - }, - ] - if not params["use_extmem"]: - attributes_dict["snippets"] += [ - { - "verilog_code": """ - // Memory connections - assign axi_ram_rst = ~intercon_m0_arstn; -""", - }, - ] - - return attributes_dict diff --git a/lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.sdc b/lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.sdc deleted file mode 100644 index a2783eceb..000000000 --- a/lib/hardware/iob_system/hardware/fpga/vivado/aes_ku040_db_g/aes_ku040_db_g.sdc +++ /dev/null @@ -1,128 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -## System Clock -create_clock -name "clk" -period 4.0 [get_ports {c0_sys_clk_clk_p_i}] - -# LVDS Programmable Clock Generator (CDCM61002) -#set_property PACKAGE_PIN M5 [get_ports LVDS_CLK0_N] -#set_property PACKAGE_PIN M6 [get_ports LVDS_CLK0_P] -#set_property PACKAGE_PIN P5 [get_ports LVDS_CLK1_N] -#set_property PACKAGE_PIN P6 [get_ports LVDS_CLK1_P] - -#set_property IOSTANDARD LVDS [get_ports LVDS_CLK0_N] -#set_property IOSTANDARD LVDS [get_ports LVDS_CLK0_P] -#set_property IOSTANDARD LVDS [get_ports LVDS_CLK1_N] -#set_property IOSTANDARD LVDS [get_ports LVDS_CLK1_P] - -##DDR clocks -set_property PACKAGE_PIN H22 [get_ports {c0_sys_clk_clk_p_i}] -set_property PACKAGE_PIN H23 [get_ports {c0_sys_clk_clk_n_i}] -set_property IOSTANDARD DIFF_SSTL12 [get_ports {c0_sys_clk_clk_p_i}] -set_property IOSTANDARD DIFF_SSTL12 [get_ports {c0_sys_clk_clk_n_i}] - -set_property CONFIG_VOLTAGE 2.5 [current_design] - -#derive_pll_clocks -#derive_clock_uncertainty - -set_property CFGBVS VCCO [current_design] - -## USB-UART Interface -set_property PACKAGE_PIN D20 [get_ports {txd_o}] -set_property IOSTANDARD LVCMOS18 [get_ports {txd_o}] -set_property PACKAGE_PIN C19 [get_ports {rxd_i}] -set_property IOSTANDARD LVCMOS18 [get_ports {rxd_i}] - -###### User LEDs -#set_property PACKAGE_PIN D16 [get_ports {led[6]}] -#set_property IOSTANDARD LVCMOS18 [get_ports {led[6]}] - -#set_property PACKAGE_PIN G16 [get_ports {led[5]}] -#set_property IOSTANDARD LVCMOS18 [get_ports {led[5]}] - -#set_property PACKAGE_PIN H16 [get_ports {led[4]}] -#set_property IOSTANDARD LVCMOS18 [get_ports {led[4]}] - -#set_property PACKAGE_PIN E18 [get_ports {led[3]}] -#set_property IOSTANDARD LVCMOS18 [get_ports {led[3]}] - -#set_property PACKAGE_PIN E17 [get_ports {led[2]}] -#set_property IOSTANDARD LVCMOS18 [get_ports {led[2]}] - -#set_property PACKAGE_PIN E16 [get_ports {led[1]}] -#set_property IOSTANDARD LVCMOS18 [get_ports {led[1]}] - -#set_property PACKAGE_PIN H18 [get_ports {led[0]}] -#set_property IOSTANDARD LVCMOS18 [get_ports {led[0]}] - -#set_property PACKAGE_PIN H17 [get_ports {trap}] -#set_property IOSTANDARD LVCMOS18 [get_ports {trap}] - -####### User PUSH Switches -#set_property PACKAGE_PIN N24 [get_ports {areset_i}] -#set_property IOSTANDARD LVCMOS12 [get_ports {areset_i}] -set_property PACKAGE_PIN K20 [get_ports {areset_i}] -set_property IOSTANDARD LVCMOS12 [get_ports {areset_i}] - -#set_property PACKAGE_PIN K18 [get_ports {gpio_push_sw_tri_i[0]}] -#set_property IOSTANDARD LVCMOS12 [get_ports {gpio_push_sw_tri_i[0]}] - -#set_property PACKAGE_PIN L18 [get_ports {gpio_push_sw_tri_i[1]}] -#set_property IOSTANDARD LVCMOS12 [get_ports {gpio_push_sw_tri_i[1]}] - -#set_property PACKAGE_PIN K21 [get_ports {gpio_push_sw_tri_i[2]}] -#set_property IOSTANDARD LVCMOS12 [get_ports {gpio_push_sw_tri_i[2]}] - -#set_property PACKAGE_PIN K20 [get_ports {gpio_push_sw_tri_i[3]}] -#set_property IOSTANDARD LVCMOS12 [get_ports {gpio_push_sw_tri_i[3]}] - -####### Ethernet 100 MHz -#create_clock -name enet_clk -period 40 [get_ports {enet_rx_clk_i}] - -## Ethernet #1 Interface (J1) -#set_property PACKAGE_PIN D9 [get_ports enet_resetn_o] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_resetn_o] - -#set_property PACKAGE_PIN A10 [get_ports enet_rx_d0_i] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_rx_d0_i] - -#set_property PACKAGE_PIN B10 [get_ports enet_rx_d1_i] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_rx_d1_i] - -#set_property PACKAGE_PIN B11 [get_ports enet_rx_d2_i] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_rx_d2_i] - -#set_property PACKAGE_PIN C11 [get_ports enet_rx_d3_i] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_rx_d3_i] - -#set_property PACKAGE_PIN D11 [get_ports enet_rx_dv_i] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_rx_dv_i] - -#set_property PACKAGE_PIN E11 [get_ports enet_rx_clk_i] -##set_property IOSTANDARD LVCMOS18 [get_ports enet_rx_clk_i] - -#set_property PACKAGE_PIN H8 [get_ports enet_tx_d0_o] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_tx_d0_o] - -#set_property PACKAGE_PIN H9 [get_ports enet_tx_d1_o] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_tx_d1_o] - -#set_property PACKAGE_PIN J9 [get_ports enet_tx_d2_o] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_tx_d2_o] - -##set_property PACKAGE_PIN J10 [get_ports enet_tx_d3_o] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_tx_d3_o] - -#set_property PACKAGE_PIN G9 [get_ports enet_tx_en_o] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_tx_en_o] - -#set_property PACKAGE_PIN G10 [get_ports enet_gtx_clK_o] -#set_property IOSTANDARD LVCMOS18 [get_ports enet_gtx_clK_o] - -#set_property IOB TRUE [get_ports enet_tx_d0_o] -#set_property IOB TRUE [get_ports enet_tx_d1_o] -#set_property IOB TRUE [get_ports enet_tx_d2_o] -#set_property IOB TRUE [get_ports enet_tx_d3_o] -#set_property IOB TRUE [get_ports enet_tx_en_o] diff --git a/lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.py b/lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.py deleted file mode 100644 index 1d16914d6..000000000 --- a/lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.py +++ /dev/null @@ -1,180 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - # user-passed parameters - params = py_params_dict["iob_system_params"] - - attributes_dict = { - "version": "0.1", - # - # Configuration - # - "confs": [ - { - "name": "AXI_ID_W", - "descr": "AXI ID bus width", - "type": "F", - "val": "4", - "min": "1", - "max": "32", - }, - { - "name": "AXI_LEN_W", - "descr": "AXI burst length width", - "type": "F", - "val": "8", - "min": "1", - "max": "8", - }, - { - "name": "AXI_ADDR_W", - "descr": "AXI address bus width", - "type": "F", - "val": "`DDR_ADDR_W" if params["use_extmem"] else "20", - "min": "1", - "max": "32", - }, - { - "name": "AXI_DATA_W", - "descr": "AXI data bus width", - "type": "F", - "val": "`DDR_DATA_W", - "min": "1", - "max": "32", - }, - ], - } - - # - # Ports - # - attributes_dict["ports"] = [ - { - "name": "clk_rst_i", - "descr": "Clock and reset", - "signals": [ - {"name": "clk", "direction": "input", "width": "1"}, - {"name": "arst", "direction": "input", "width": "1"}, - ], - }, - { - "name": "rs232", - "descr": "Serial port", - "signals": [ - {"name": "txd", "direction": "output", "width": "1"}, - {"name": "rxd", "direction": "input", "width": "1"}, - ], - }, - ] - - # - # Wires - # - attributes_dict["wires"] = [ - { - "name": "rs232_int", - "descr": "iob-system uart interface", - "signals": [ - {"name": "rxd"}, - {"name": "txd"}, - {"name": "rs232_rts", "width": "1"}, - {"name": "high", "width": "1"}, - ], - }, - { - "name": "axi", - "descr": "AXI interface to connect SoC to memory", - "interface": { - "type": "axi", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - { - "name": "memory_axi", - "descr": "AXI bus to connect interconnect and memory", - "interface": { - "type": "axi", - "wire_prefix": "mem_", - "ID_W": "AXI_ID_W", - "LEN_W": "AXI_LEN_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LOCK_W": 1 if params["use_extmem"] else 2, - }, - }, - ] - - # - # Blocks - # - attributes_dict["blocks"] = [ - { - # IOb-SoC Memory Wrapper - "core_name": "iob_system_mwrap", - "instance_name": "iob_system_mwrap", - "instance_description": "IOb-SoC instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst", - "rs232_m": "rs232_int", - "axi_m": "axi", - }, - "dest_dir": "hardware/common_src", - "iob_system_params": params, - }, - { - "core_name": "xilinx_axi_interconnect", - "instance_name": "axi_async_bridge", - "instance_description": "Interconnect instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_rst_s": "intercon_clk_rst", - "m0_clk_rst": "intercon_m0_clk_rst", - "m0_axi_m": "memory_axi", - "s0_clk_rst": "intercon_s0_clk_rst", - "s0_axi_s": "axi", - }, - "num_slaves": 1, - }, - { - "core_name": "axi_ram", - "instance_name": "ddr_model_mem", - "instance_description": "DDR model memory", - "parameters": { - "ID_WIDTH": "AXI_ID_W", - "ADDR_WIDTH": "AXI_ADDR_W", - "DATA_WIDTH": "AXI_DATA_W", - "READ_ON_WRITE": "1", - }, - "connect": { - "clk_i": "clk", - "rst_i": "arst", - "axi_s": "memory_axi", - }, - }, - ] - - if params["init_mem"]: - attributes_dict["blocks"][-1]["parameters"].update( - { - "FILE": f'"{params["name"]}_firmware"', - } - ) - - return attributes_dict diff --git a/lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.sdc b/lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.sdc deleted file mode 100644 index eb211c9cf..000000000 --- a/lib/hardware/iob_system/hardware/fpga/vivado/basys3/basys3.sdc +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -## System Clock -set_property PACKAGE_PIN W5 [get_ports clk_i] -set_property IOSTANDARD LVCMOS33 [get_ports clk_i] -create_clock -period 10.000 [get_ports clk_i] - -# System Reset -set_property PACKAGE_PIN T17 [get_ports arst_i] -set_property IOSTANDARD LVCMOS33 [get_ports arst_i] - - -## USB-UART Interface -set_property PACKAGE_PIN B18 [get_ports rxd_i] -set_property IOSTANDARD LVCMOS33 [get_ports rxd_i] -set_property PACKAGE_PIN A18 [get_ports txd_o] -set_property IOSTANDARD LVCMOS33 [get_ports txd_o] diff --git a/lib/hardware/iob_system/hardware/fpga/vivado/vivado.sdc b/lib/hardware/iob_system/hardware/fpga/vivado/vivado.sdc deleted file mode 100644 index c3dc03967..000000000 --- a/lib/hardware/iob_system/hardware/fpga/vivado/vivado.sdc +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -## Synchronizers -#set_property ASYNC_REG TRUE [get_cells -hier {*iob_r_data_o*[*]}] -#set_property ASYNC_REG TRUE [get_cells -hier {*iob_rn_data_o*[*]}] - -## Clock groups -#set_clock_groups -asynchronous -group {c0_sys_clk_clk_p} -group {enet_clk} diff --git a/lib/hardware/iob_system/hardware/fpga/vivado/vivado_premap.tcl b/lib/hardware/iob_system/hardware/fpga/vivado/vivado_premap.tcl deleted file mode 100644 index 3d7086b81..000000000 --- a/lib/hardware/iob_system/hardware/fpga/vivado/vivado_premap.tcl +++ /dev/null @@ -1,95 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -if {$N_INTERCONNECT_SLAVES eq ""} { - set N_INTERCONNECT_SLAVES 1 ; # Default value when not provided -} - -proc generate_slave_config_lines {num_slaves} { - for {set i 0} {$i < $num_slaves} {incr i} { - set slave_number [format "%02d" $i] - set_property "CONFIG.S${slave_number}_AXI_IS_ACLK_ASYNC" 1 [get_ips axi_interconnect_0] - set_property "CONFIG.S${slave_number}_AXI_READ_FIFO_DEPTH" 32 [get_ips axi_interconnect_0] - set_property "CONFIG.S${slave_number}_AXI_WRITE_FIFO_DEPTH" 32 [get_ips axi_interconnect_0] - } -} - -read_verilog vivado/$BOARD/xilinx_axi_interconnect.v - -if { ![file isdirectory "./ip"]} { - file mkdir ./ip -} - -#async interconnect MIG<->Cache -if { [file isdirectory "./ip/axi_interconnect_0"] } { - read_ip ./ip/axi_interconnect_0/axi_interconnect_0.xci - report_property [get_files ./ip/axi_interconnect_0/axi_interconnect_0.xci] -} else { - - create_ip -name axi_interconnect -vendor xilinx.com -library ip -version 1.7 -module_name axi_interconnect_0 -dir ./ip -force - - set_property CONFIG.NUM_SLAVE_PORTS $N_INTERCONNECT_SLAVES [get_ips axi_interconnect_0] - set_property CONFIG.AXI_ADDR_WIDTH 30 [get_ips axi_interconnect_0] - set_property CONFIG.ACLK_PERIOD 5000 [get_ips axi_interconnect_0] - set_property CONFIG.INTERCONNECT_DATA_WIDTH 32 [get_ips axi_interconnect_0] - set_property CONFIG.M00_AXI_IS_ACLK_ASYNC 1 [get_ips axi_interconnect_0] - set_property CONFIG.M00_AXI_WRITE_FIFO_DEPTH 32 [get_ips axi_interconnect_0] - set_property CONFIG.M00_AXI_READ_FIFO_DEPTH 32 [get_ips axi_interconnect_0] - - generate_slave_config_lines $N_INTERCONNECT_SLAVES - - generate_target all [get_files ./ip/axi_interconnect_0/axi_interconnect_0.xci] - - report_property [get_ips axi_interconnect_0] - report_property [get_files ./ip/axi_interconnect_0/axi_interconnect_0.xci] - exec sed -i s/100/5/g ip/axi_interconnect_0/axi_interconnect_0_ooc.xdc - synth_ip [get_files ./ip/axi_interconnect_0/axi_interconnect_0.xci] - -} - -if { $USE_EXTMEM > 0 } { - - read_verilog vivado/$BOARD/xilinx_ddr4_ctrl.v - - - if { [file isdirectory "./ip/ddr4_0"] } { - read_ip ./ip/ddr4_0/ddr4_0.xci - report_property [get_files ./ip/ddr4_0/ddr4_0.xci] - } else { - - create_ip -name ddr4 -vendor xilinx.com -library ip -version 2.2 -module_name ddr4_0 -dir ./ip -force - - set_property -dict \ - [list \ - CONFIG.C0.DDR4_TimePeriod {1250} \ - CONFIG.C0.DDR4_InputClockPeriod {4000} \ - CONFIG.C0.DDR4_CLKOUT0_DIVIDE {5} \ - CONFIG.C0.DDR4_MemoryPart {EDY4016AABG-DR-F} \ - CONFIG.C0.DDR4_DataWidth {32} \ - CONFIG.C0.DDR4_AxiSelection {true} \ - CONFIG.C0.DDR4_CasLatency {11} \ - CONFIG.C0.DDR4_CasWriteLatency {11} \ - CONFIG.C0.DDR4_AxiDataWidth {32} \ - CONFIG.C0.DDR4_AxiAddressWidth {30} \ - CONFIG.ADDN_UI_CLKOUT1_FREQ_HZ {100} \ - CONFIG.C0.BANK_GROUP_WIDTH {1}] [get_ips ddr4_0] - - generate_target all [get_files ./ip/ddr4_0/ddr4_0.xci] - - report_property [get_ips ddr4_0] - report_property [get_files ./ip/ddr4_0/ddr4_0.xci] - - synth_ip [get_files ./ip/ddr4_0/ddr4_0.xci] - } - - read_xdc vivado/$BOARD/ddr.xdc - -} else { - read_verilog vivado/$BOARD/xilinx_clock_wizard.v - read_verilog vivado/$BOARD/clock_wizard.v - read_verilog vivado/$BOARD/iob_reset_sync.v - read_verilog vivado/$BOARD/iob_r.v - read_verilog vivado/$BOARD/axi_ram.v - -} diff --git a/lib/hardware/iob_system/hardware/modules/iob_system_cache_system/iob_system_cache_system.py b/lib/hardware/iob_system/hardware/modules/iob_system_cache_system/iob_system_cache_system.py deleted file mode 100644 index 149e93ad4..000000000 --- a/lib/hardware/iob_system/hardware/modules/iob_system_cache_system/iob_system_cache_system.py +++ /dev/null @@ -1,310 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - ADDR_W = py_params_dict["addr_w"] if "addr_w" in py_params_dict else 32 - DATA_W = py_params_dict["data_w"] if "data_w" in py_params_dict else 32 - MEM_ADDR_W = py_params_dict["mem_addr_w"] if "mem_addr_w" in py_params_dict else 32 - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "FIRM_ADDR_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Width of firmware", - }, - { - "name": "DDR_DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Width of data bus in ddr interface", - }, - { - "name": "DDR_ADDR_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Width of address bus in ddr interface", - }, - { - "name": "AXI_ID_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Width of id signal in axi interface", - }, - { - "name": "AXI_LEN_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Width of len signal in axi interface", - }, - { - "name": "AXI_DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Width of data bus in axi interface", - }, - { - "name": "AXI_ADDR_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Width of address bus in axi interface", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "i_bus_s", - "interface": { - "type": "iob", - "subtype": "slave", - "port_prefix": "i_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W, - }, - "descr": "Instruction bus", - }, - { - "name": "d_bus_s", - "interface": { - "type": "iob", - "subtype": "slave", - "port_prefix": "d_", - "DATA_W": DATA_W, - "ADDR_W": ADDR_W, - }, - "descr": "Data bus", - }, - { - "name": "axi_m", - "interface": { - "type": "axi", - "subtype": "master", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - "descr": "AXI master interface for external memory", - }, - ], - } - attributes_dict["wires"] = [ - { - "name": "never_reset", - "descr": "Reset signal for common components (always low)", - "signals": [ - {"name": "always_low", "width": 1}, - ], - }, - { - "name": "icache", - "interface": { - "type": "iob", - "wire_prefix": "icache_be_", - "DATA_W": DATA_W, - "ADDR_W": MEM_ADDR_W, - }, - "descr": "iob-system external memory instruction cache interface", - }, - { - "name": "dcache", - "interface": { - "type": "iob", - "wire_prefix": "dcache_be_", - "DATA_W": DATA_W, - "ADDR_W": MEM_ADDR_W, - }, - "descr": "iob-system external memory data cache interface", - }, - { - "name": "l2cache", - "interface": { - "type": "iob", - "wire_prefix": "l2cache_", - "DATA_W": DATA_W, - "ADDR_W": MEM_ADDR_W, - }, - "descr": "iob-system external memory l2 cache interface", - }, - ] - attributes_dict["blocks"] = [ - { - "core_name": "iob_merge", - "name": "iob_i_d_into_l2_merge", - "instance_name": "iob_i_d_into_l2_merge", - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "reset_i": "never_reset", - "input_0": "dcache", - "input_1": "icache", - "output_m": "l2cache", - }, - "num_inputs": 2, - "addr_w": MEM_ADDR_W, - }, - { - "core_name": "iob_cache", - "instance_name": "iob_cache_inst", - "instantiate": False, - }, - ] - attributes_dict["snippets"] = [ - { - "verilog_code": f""" - assign always_low = 1'b0; - - // Instruction cache instance - iob_cache_iob #( - .FE_ADDR_W (FIRM_ADDR_W), - .BE_ADDR_W ({MEM_ADDR_W}), - .NWAYS_W (1), //Number of ways - .NLINES_W (7), //Cache Line Offset (number of lines) - .WORD_OFFSET_W(3), //Word Offset (number of words per line) - .WTBUF_DEPTH_W(5), //FIFO's depth -- 5 minimum for BRAM implementation - .USE_CTRL (0), //Cache-Control can't be accessed - .USE_CTRL_CNT (0) //Remove counters - ) icache ( - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i(arst_i), - - // Front-end interface - .iob_valid_i (i_iob_valid_i), - .iob_addr_i (i_iob_addr_i[FIRM_ADDR_W-1:2]), - .iob_wdata_i (i_iob_wdata_i), - .iob_wstrb_i (i_iob_wstrb_i), - .iob_rdata_o (i_iob_rdata_o), - .iob_rvalid_o(i_iob_rvalid_o), - .iob_ready_o (i_iob_ready_o), - //Control IO - .invalidate_i(1'b0), - .invalidate_o(), - .wtb_empty_i (1'b1), - .wtb_empty_o (), - // Back-end interface - .be_valid_o (icache_be_iob_valid), - .be_addr_o (icache_be_iob_addr), - .be_wdata_o (icache_be_iob_wdata), - .be_wstrb_o (icache_be_iob_wstrb), - .be_rdata_i (icache_be_iob_rdata), - .be_rvalid_i (icache_be_iob_rvalid), - .be_ready_i (icache_be_iob_ready) - ); - - //mem control signals - wire l2_wtb_empty; - wire invalidate; - reg invalidate_reg; - //Necessary logic to avoid invalidating L2 while it's being accessed by a request - always @(posedge clk_i, posedge arst_i) - if (arst_i) invalidate_reg <= 1'b0; - else if (invalidate) invalidate_reg <= 1'b1; - else if (~l2cache_iob_valid) invalidate_reg <= 1'b0; - else invalidate_reg <= invalidate_reg; - - // - // DATA CACHE - // - - // IOb ready and rvalid signals - - // Data cache instance - iob_cache_iob #( - .FE_ADDR_W ({MEM_ADDR_W}), - .BE_ADDR_W ({MEM_ADDR_W}), - .NWAYS_W (1), //Number of ways - .NLINES_W (7), //Cache Line Offset (number of lines) - .WORD_OFFSET_W(3), //Word Offset (number of words per line) - .WTBUF_DEPTH_W(5), //FIFO's depth -- 5 minimum for BRAM implementation - .USE_CTRL (1), //Either 1 to enable cache-control or 0 to disable - .USE_CTRL_CNT (0) //do not change (it's implementation depends on the previous) - ) dcache ( - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i(arst_i), - - // Front-end interface - .iob_valid_i (d_iob_valid_i), - .iob_addr_i (d_iob_addr_i[1+{MEM_ADDR_W}-1:2]), - .iob_wdata_i (d_iob_wdata_i), - .iob_wstrb_i (d_iob_wstrb_i), - .iob_rdata_o (d_iob_rdata_o), - .iob_rvalid_o(d_iob_rvalid_o), - .iob_ready_o (d_iob_ready_o), - //Control IO - .invalidate_i(1'b0), - .invalidate_o(invalidate), - .wtb_empty_i (l2_wtb_empty), - .wtb_empty_o (), - // Back-end interface - .be_valid_o (dcache_be_iob_valid), - .be_addr_o (dcache_be_iob_addr), - .be_wdata_o (dcache_be_iob_wdata), - .be_wstrb_o (dcache_be_iob_wstrb), - .be_rdata_i (dcache_be_iob_rdata), - .be_rvalid_i (dcache_be_iob_rvalid), - .be_ready_i (dcache_be_iob_ready) - ); - - // L2 cache instance - iob_cache_axi #( - .AXI_ID_W (AXI_ID_W), - .AXI_LEN_W (AXI_LEN_W), - .FE_ADDR_W ({MEM_ADDR_W}), - .BE_ADDR_W (DDR_ADDR_W), - .BE_DATA_W (DDR_DATA_W), - .NWAYS_W (2), //Number of Ways - .NLINES_W (7), //Cache Line Offset (number of lines) - .WORD_OFFSET_W(3), //Word Offset (number of words per line) - .WTBUF_DEPTH_W(5), //FIFO's depth -- 5 minimum for BRAM implementation - .USE_CTRL (0), //Cache-Control can't be accessed - .USE_CTRL_CNT (0) //Remove counters - ) l2cache ( - // Native interface - .iob_valid_i (l2cache_iob_valid), - .iob_addr_i (l2cache_iob_addr[{MEM_ADDR_W}-1:2]), - .iob_wdata_i (l2cache_iob_wdata), - .iob_wstrb_i (l2cache_iob_wstrb), - .iob_rdata_o (l2cache_iob_rdata), - .iob_rvalid_o(l2cache_iob_rvalid), - .iob_ready_o (l2cache_iob_ready), - //Control IO - .invalidate_i(invalidate_reg & ~l2cache_iob_valid), - .invalidate_o(), - .wtb_empty_i (1'b1), - .wtb_empty_o (l2_wtb_empty), - // AXI interface - `include "iob_system_cache_system_axi_m_m_portmap.vs" - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i (arst_i) - ); -""", - }, - ] - - return attributes_dict diff --git a/lib/hardware/iob_system/hardware/modules/iob_system_mwrap/iob_system_mwrap.py b/lib/hardware/iob_system/hardware/modules/iob_system_mwrap/iob_system_mwrap.py deleted file mode 100644 index b48513e58..000000000 --- a/lib/hardware/iob_system/hardware/modules/iob_system_mwrap/iob_system_mwrap.py +++ /dev/null @@ -1,89 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -import copy - -import iob_system - - -def setup(py_params_dict): - params = py_params_dict["iob_system_params"] - - iob_system_attr = iob_system.setup(params) - - attributes_dict = { - "name": params["name"] + "_mwrap", - "version": "0.1", - "confs": [ - { - "name": "BOOT_HEXFILE", - "descr": "Bootloader file name", - "type": "P", - "val": f'"{params["name"]}_bootrom"', - "min": "NA", - "max": "NA", - }, - ] - + iob_system_attr["confs"], - } - - # Declare memory wrapper ports and wires automatically based on iob-system ports. - mwrap_wires = [] - mwrap_ports = [] - for port in iob_system_attr["ports"]: - if port["name"] == "rom_bus": - wire = copy.deepcopy(port) - if "interface" in wire and "port_prefix" in wire["interface"]: - wire["interface"]["wire_prefix"] = wire["interface"]["port_prefix"] - wire["interface"].pop("port_prefix") - if "signals" in wire: - for sig in wire["signals"]: - sig.pop("direction") - mwrap_wires.append(wire) - else: - mwrap_ports.append(port) - - attributes_dict["ports"] = mwrap_ports - - attributes_dict["wires"] = mwrap_wires + [ - { - "name": "clk", - "descr": "Clock signal", - "signals": [ - {"name": "clk"}, - ], - }, - ] - attributes_dict["blocks"] = [ - # ROM - { - "core_name": "iob_rom_sp", - "instance_name": "boot_rom", - "instance_description": "Boot ROM", - "parameters": { - "ADDR_W": params["bootrom_addr_w"] - 2, - "DATA_W": params["data_w"], - "HEXFILE": '{BOOT_HEXFILE, ".hex"}', - }, - "connect": { - "clk": "clk", - "rom_if": "rom_bus", - }, - }, - # IOb-SoC - { - "core_name": "iob_system", - "instance_name": "iob_system", - "instance_description": "IOb-SoC core", - "parameters": { - i["name"]: i["name"] - for i in iob_system_attr["confs"] - if i["type"] in ["P", "F"] - }, - "connect": {i["name"]: i["name"] for i in iob_system_attr["ports"]}, - **params, - }, - ] - - return attributes_dict diff --git a/lib/hardware/iob_system/hardware/modules/iob_system_sim_wrapper/iob_system_sim_wrapper.py b/lib/hardware/iob_system/hardware/modules/iob_system_sim_wrapper/iob_system_sim_wrapper.py deleted file mode 100644 index 9d62d9dc2..000000000 --- a/lib/hardware/iob_system/hardware/modules/iob_system_sim_wrapper/iob_system_sim_wrapper.py +++ /dev/null @@ -1,322 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - params = py_params_dict["iob_system_params"] - - attributes_dict = { - "name": params["name"] + "_sim_wrapper", - "version": "0.1", - "confs": [ - { - "name": "AXI_ID_W", - "descr": "AXI ID bus width", - "type": "F", - "val": "4", - "min": "1", - "max": "32", - }, - { - "name": "AXI_LEN_W", - "descr": "AXI burst length width", - "type": "F", - "val": "8", - "min": "1", - "max": "8", - }, - { - "name": "AXI_ADDR_W", - "descr": "AXI address bus width", - "type": "F", - "val": "`DDR_ADDR_W", - "min": "1", - "max": "32", - }, - { - "name": "AXI_DATA_W", - "descr": "AXI data bus width", - "type": "F", - "val": "`DDR_DATA_W", - "min": "1", - "max": "32", - }, - ], - } - # - # Ports - # - attributes_dict["ports"] = [ - { - "name": "clk_en_rst_s", - "descr": "Clock, clock enable and reset", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - }, - { - "name": "uart_s", - "descr": "Testbench uart csrs interface", - "interface": { - "type": "iob", - "subtype": "slave", - "port_prefix": "uart_", - "ADDR_W": 3, - }, - }, - ] - if params["use_ethernet"]: - attributes_dict["ports"] += [ - { - "name": "ethernet_s", - "descr": "Testbench ethernet csrs interface", - "interface": { - "type": "iob", - "subtype": "slave", - "port_prefix": "ethernet_", - }, - }, - ] - - # - # Wires - # - attributes_dict["wires"] = [ - { - "name": "rs232", - "descr": "rs232 bus", - "interface": { - "type": "rs232", - }, - }, - { - "name": "rs232_invert", - "descr": "Invert order of rs232 signals", - "signals": [ - {"name": "rs232_txd"}, - {"name": "rs232_rxd"}, - {"name": "rs232_cts"}, - {"name": "rs232_rts"}, - ], - }, - { - "name": "axi", - "descr": "AXI bus to connect SoC to interconnect", - "interface": { - "type": "axi", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - "LOCK_W": "AXI_LEN_W", - }, - }, - { - "name": "clk", - "descr": "Clock signal", - "signals": [ - {"name": "clk"}, - ], - }, - { - "name": "rst", - "descr": "Reset signal", - "signals": [ - {"name": "arst"}, - ], - }, - { - "name": "memory_axi", - "descr": "AXI bus to connect interconnect and memory", - "interface": { - "type": "axi", - "wire_prefix": "mem_", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - "LOCK_W": "1", - }, - }, - ] - if params["use_ethernet"]: - attributes_dict["wires"] += [ - { - "name": "eth_axi", - "descr": "Ethernet AXI bus", - "interface": { - "type": "axi", - "wire_prefix": "eth_", - }, - }, - { - "name": "eth_mii_invert", - "descr": "Invert order of signals in ethernet MII bus", - "signals": [ - {"name": "eth_MTxClk", "width": "1"}, - {"name": "MRxDv"}, - {"name": "MRxD"}, - {"name": "MRxErr"}, - {"name": "eth_MRxClk", "width": "1"}, - {"name": "MTxEn"}, - {"name": "MTxD"}, - {"name": "MTxErr"}, - {"name": "eth_MColl", "width": "1"}, - {"name": "eth_MCrS", "width": "1"}, - {"name": "eth_MDC", "width": "1"}, - {"name": "eth_MDIO", "width": "1"}, - ], - }, - { - "name": "eth_int", - "descr": "Ethernet interrupt", - "signals": [ - {"name": "eth_interrupt"}, - ], - }, - ] - # - # Blocks - # - attributes_dict["blocks"] = [ - { - "core_name": "iob_system_mwrap", - "instance_name": "iob_system_mwrap", - "instance_description": "IOb-SoC memory wrapper", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "rs232_m": "rs232", - "axi_m": "axi", - }, - "dest_dir": "hardware/common_src", - "iob_system_params": params, - }, - { - "core_name": "iob_uart", - "name": "iob_uart_iob", - "instance_name": "uart_tb", - "instance_description": "Testbench uart core", - "csr_if": "iob", - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "cbus_s": "uart_s", - "rs232_m": "rs232_invert", - }, - }, - { - "core_name": "axi_interconnect_wrapper", - "name": "sim_axi_interconnect_wrapper", - "instance_name": "axi_interconnect", - "instance_description": "Interconnect instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_i": "clk", - "rst_i": "rst", - "s0_axi_s": ( - "axi", - "axi_awlock[0]", - "axi_arlock[0]", - ), - "m0_axi_m": "memory_axi", - }, - "num_slaves": 1, - }, - { - "core_name": "axi_ram", - "instance_name": "ddr_model_mem", - "instance_description": "Internal/DDR model memory", - "parameters": { - "ID_WIDTH": "AXI_ID_W", - "ADDR_WIDTH": "AXI_ADDR_W", - "DATA_WIDTH": "AXI_DATA_W", - }, - "connect": { - "clk_i": "clk", - "rst_i": "rst", - "axi_s": ( - "memory_axi", - "{1'b0, mem_axi_arlock}", - "{1'b0, mem_axi_awlock}", - ), - }, - }, - ] - if params["init_mem"]: - attributes_dict["blocks"][-1]["parameters"].update( - { - "FILE": f'"{params["name"]}_firmware"', - } - ) - if params["use_ethernet"]: - attributes_dict["blocks"] += [ - { - "core_name": "iob_eth", - "instance_name": "eth_tb", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_LEN_W": "AXI_LEN_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - }, - "connect": { - "clk_en_rst": "clk_en_rst_s", - "iob": "ethernet", - "axi": "eth_axi", - "mii": "eth_mii_invert", - "interrupt": "eth_int", - }, - }, - ] - # - # Snippets - # - attributes_dict["snippets"] = [] - if params["use_ethernet"]: - attributes_dict["snippets"] += [ - { - "verilog_code": """ - //ethernet clock: 4x slower than system clock - reg [1:0] eth_cnt = 2'b0; - reg eth_clk; - - always @(posedge clk_i) begin - eth_cnt <= eth_cnt + 1'b1; - eth_clk <= eth_cnt[1]; - end - - // Set ethernet AXI inputs to low - assign eth_axi_awready_i = 1'b0; - assign eth_axi_wready_i = 1'b0; - assign eth_axi_bid_i = {AXI_ID_W{1'b0}}; - assign eth_axi_bresp_i = 2'b0; - assign eth_axi_bvalid_i = 1'b0; - assign eth_axi_arready_i = 1'b0; - assign eth_axi_rid_i = {AXI_ID_W{1'b0}}; - assign eth_axi_rdata_i = {AXI_DATA_W{1'b0}}; - assign eth_axi_rresp_i = 2'b0; - assign eth_axi_rlast_i = 1'b0; - assign eth_axi_rvalid_i = 1'b0; - - // Connect ethernet MII signals - assign eth_MTxClk = eth_clk; - assign eth_MRxClk = eth_clk; - assign eth_MColl = 1'b0; - assign eth_MCrS = 1'b0; - -""", - }, - ] - - return attributes_dict diff --git a/lib/hardware/iob_system/hardware/simulation/sim_build.mk b/lib/hardware/iob_system/hardware/simulation/sim_build.mk deleted file mode 100644 index 0549141a1..000000000 --- a/lib/hardware/iob_system/hardware/simulation/sim_build.mk +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -include auto_sim_build.mk - -# Add iob-system software as a build dependency -HEX+=iob_system_bootrom.hex iob_system_firmware.hex - -ROOT_DIR :=../.. -include $(ROOT_DIR)/software/sw_build.mk - -VTOP:=iob_system_tb - -# SOURCES -ifeq ($(SIMULATOR),verilator) - -VSRC+=./src/iob_tasks.cpp - -ifeq ($(USE_ETHERNET),1) -VSRC+=./src/iob_eth_csrs_emb_verilator.c ./src/iob_eth_driver_tb.cpp -endif - -# get header files (needed for iob_system_tb.cpp) -VHDR+=iob_uart_csrs.h -iob_uart_csrs.h: ../../software/src/iob_uart_csrs.h - cp $< $@ - -# verilator top module -VTOP:=iob_system_sim_wrapper - -endif - -CONSOLE_CMD ?=rm -f soc2cnsl cnsl2soc; ../../scripts/console.py -L - -GRAB_TIMEOUT ?= 3600 diff --git a/lib/hardware/iob_system/hardware/simulation/src/.empty b/lib/hardware/iob_system/hardware/simulation/src/.empty deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.cpp b/lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.cpp deleted file mode 100644 index a43733422..000000000 --- a/lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -#include -#include -#include - -#include "Viob_system_sim_wrapper.h" -#include "bsp.h" -#include "iob_system_conf.h" -#include "iob_uart_csrs.h" - -#include "iob_tasks.h" -#ifdef IOB_SYSTEM_USE_ETHERNET -#include "iob_eth_driver_tb.h" -#endif - -#if (VM_TRACE == 1) // If verilator was invoked with --trace -#if (VM_TRACE_FST == 1) // If verilator was invoked with --trace-fst -#include -#else -#include -#endif -#endif - -extern vluint64_t main_time; -extern timer_settings_t task_timer_settings; - -void cpu_inituart(iob_native_t *uart_if); - -Viob_system_sim_wrapper *dut = new Viob_system_sim_wrapper; - -void call_eval() { dut->eval(); } - -#if (VM_TRACE == 1) -#if (VM_TRACE_FST == 1) -VerilatedFstC *tfp = new VerilatedFstC; // Create tracing object -#else -VerilatedVcdC *tfp = new VerilatedVcdC; // Create tracing object -#endif - -void call_dump(vluint64_t time) { tfp->dump(time); } -#endif - -double sc_time_stamp() { // Called by $time in Verilog - return main_time; -} - -// -// Main program -// -int main(int argc, char **argv, char **env) { - unsigned int i; - - Verilated::commandArgs(argc, argv); - task_timer_settings.clk = &dut->clk_i; - task_timer_settings.eval = call_eval; -#if (VM_TRACE == 1) - task_timer_settings.dump = call_dump; -#endif - - iob_native_t uart_if = { - &dut->uart_iob_valid_i, &dut->uart_iob_addr_i, UCHAR, - &dut->uart_iob_wdata_i, &dut->uart_iob_wstrb_i, &dut->uart_iob_rdata_o, - &dut->uart_iob_rvalid_o, &dut->uart_iob_ready_o}; - -#ifdef IOB_SYSTEM_USE_ETHERNET - iob_native_t eth_if = {&dut->ethernet_iob_valid_i, - &dut->ethernet_iob_addr_i, - USINT, - &dut->ethernet_iob_wdata_i, - &dut->ethernet_iob_wstrb_i, - &dut->ethernet_iob_rdata_o, - &dut->ethernet_iob_rvalid_o, - &dut->ethernet_iob_ready_o}; -#endif - -#if (VM_TRACE == 1) - Verilated::traceEverOn(true); // Enable tracing - dut->trace(tfp, 1); - tfp->open("uut.vcd"); -#endif - - dut->clk_i = 0; - dut->cke_i = 1; - - // Reset sequence - dut->arst_i = 0; - for (i = 0; i < 100; i++) - Timer(CLK_PERIOD); - dut->arst_i = 1; - for (i = 0; i < 100; i++) - Timer(CLK_PERIOD); - dut->arst_i = 0; - - *(uart_if.iob_valid) = 0; - *(uart_if.iob_wstrb) = 0; - cpu_inituart(&uart_if); - - FILE *soc2cnsl_fd; - FILE *cnsl2soc_fd; - char cpu_char = 0; - char rxread_reg = 0, txread_reg = 0; - int able2write = 0, able2read = 0; - - while ((cnsl2soc_fd = fopen("./cnsl2soc", "rb")) == NULL) - ; - fclose(cnsl2soc_fd); - soc2cnsl_fd = fopen("./soc2cnsl", "wb"); - -#ifdef IOB_SYSTEM_USE_ETHERNET - eth_setup(ð_if); -#endif - - while (1) { - while (!rxread_reg && !txread_reg) { - rxread_reg = (char)iob_read(IOB_UART_RXREADY_ADDR, &uart_if); - txread_reg = (char)iob_read(IOB_UART_TXREADY_ADDR, &uart_if); - } - if (rxread_reg) { - cpu_char = (char)iob_read(IOB_UART_RXDATA_ADDR, &uart_if); - fwrite(&cpu_char, sizeof(char), 1, soc2cnsl_fd); - fflush(soc2cnsl_fd); - rxread_reg = 0; - } - if (txread_reg) { - if ((cnsl2soc_fd = fopen("./cnsl2soc", "rb")) == NULL) { - fclose(soc2cnsl_fd); - break; - } - able2write = fread(&cpu_char, sizeof(char), 1, cnsl2soc_fd); - if (able2write > 0) { - iob_write(IOB_UART_TXDATA_ADDR, cpu_char, IOB_UART_TXDATA_W / 8, - &uart_if); - fclose(cnsl2soc_fd); - cnsl2soc_fd = fopen("./cnsl2soc", "w"); - } - fclose(cnsl2soc_fd); - txread_reg = 0; - } - -#ifdef IOB_SYSTEM_USE_ETHERNET - eth_relay_frames(ð_if); -#endif - } - - dut->final(); - -#if (VM_TRACE == 1) - tfp->dump(main_time); // Dump last values - tfp->close(); // Close tracing file - VL_PRINTF("Generated vcd file\n"); - delete tfp; -#endif - - delete dut; - dut = NULL; - - exit(0); -} - -void cpu_inituart(iob_native_t *uart_if) { - // pulse reset uart - iob_write(IOB_UART_SOFTRESET_ADDR, 1, IOB_UART_SOFTRESET_W / 8, uart_if); - iob_write(IOB_UART_SOFTRESET_ADDR, 0, IOB_UART_SOFTRESET_W / 8, uart_if); - // config uart div factor - iob_write(IOB_UART_DIV_ADDR, int(FREQ / BAUD), IOB_UART_DIV_W / 8, uart_if); - // enable uart for receiving - iob_write(IOB_UART_RXEN_ADDR, 1, IOB_UART_RXEN_W / 8, uart_if); - iob_write(IOB_UART_TXEN_ADDR, 1, IOB_UART_TXEN_W / 8, uart_if); -} diff --git a/lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.v b/lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.v deleted file mode 100644 index a58eb6036..000000000 --- a/lib/hardware/iob_system/hardware/simulation/src/iob_system_tb.v +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`include "bsp.vh" -`include "iob_system_conf.vh" -`include "iob_uart_conf.vh" -`include "iob_uart_csrs_def.vh" - -//Peripherals _csrs_def.vh file includes. -`include "iob_uart_csrs_def.vh" - -module iob_system_tb; - - parameter realtime CLK_PER = 1s / `FREQ; - - localparam ADDR_W = `IOB_SYSTEM_ADDR_W; - localparam DATA_W = `IOB_SYSTEM_DATA_W; - - //clock - reg clk = 1; - initial clk = 0; - always #(CLK_PER / 2) clk = ~clk; - - //reset - reg arst = 0; - - //received by getchar - reg rxread_reg; - reg txread_reg; - reg [7:0] cpu_char; - integer soc2cnsl_fd = 0, cnsl2soc_fd = 0; - - - //IOb-SoC uart - reg iob_valid_i; - reg [`IOB_UART_CSRS_ADDR_W-1:0] iob_addr_i; - reg [ `IOB_SYSTEM_DATA_W-1:0] iob_wdata_i; - reg [ 3:0] iob_wstrb_i; - wire [ `IOB_SYSTEM_DATA_W-1:0] iob_rdata_o; - wire iob_ready_o; - wire iob_rvalid_o; - - //iterator - integer i = 0, n = 0; - integer error, n_byte = 0; - - ///////////////////////////////////////////// - // TEST PROCEDURE - // - initial begin - -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - //init cpu bus signals - iob_valid_i = 0; - iob_wstrb_i = 0; - - //reset system - arst = ~`IOB_SYSTEM_RST_POL; - #100 arst = `IOB_SYSTEM_RST_POL; - #1_000 arst = ~`IOB_SYSTEM_RST_POL; - #100; - @(posedge clk) #1; - - // configure uart - cpu_inituart(); - - cpu_char = 0; - rxread_reg = 0; - txread_reg = 0; - - - cnsl2soc_fd = $fopen("cnsl2soc", "r"); - while (!cnsl2soc_fd) begin - $display("Could not open \"cnsl2soc\""); - cnsl2soc_fd = $fopen("cnsl2soc", "r"); - end - $fclose(cnsl2soc_fd); - soc2cnsl_fd = $fopen("soc2cnsl", "w"); - - while (1) begin - while (!rxread_reg && !txread_reg) begin - iob_read(`IOB_UART_RXREADY_ADDR, rxread_reg, `IOB_UART_RXREADY_W); - iob_read(`IOB_UART_TXREADY_ADDR, txread_reg, `IOB_UART_TXREADY_W); - end - if (rxread_reg) begin - iob_read(`IOB_UART_RXDATA_ADDR, cpu_char, `IOB_UART_RXDATA_W); - $fwriteh(soc2cnsl_fd, "%c", cpu_char); - $fflush(soc2cnsl_fd); - rxread_reg = 0; - end - if (txread_reg) begin - cnsl2soc_fd = $fopen("cnsl2soc", "r"); - if (!cnsl2soc_fd) begin - //wait 1 ms and try again - #1_000_000 cnsl2soc_fd = $fopen("cnsl2soc", "r"); - if (!cnsl2soc_fd) begin - $fclose(soc2cnsl_fd); - $finish(); - end - end - n = $fscanf(cnsl2soc_fd, "%c", cpu_char); - if (n > 0) begin - iob_write(`IOB_UART_TXDATA_ADDR, cpu_char, `IOB_UART_TXDATA_W); - $fclose(cnsl2soc_fd); - cnsl2soc_fd = $fopen("./cnsl2soc", "w"); - end - $fclose(cnsl2soc_fd); - txread_reg = 0; - end - end - end - -`ifdef IOB_SYSTEM_USE_ETHERNET - //IOb-SoC ethernet - wire ethernet_iob_valid; - wire [`IOB_ETH_CSRS_ADDR_W-1:0] ethernet_iob_addr; - wire [ `IOB_SYSTEM_DATA_W-1:0] ethernet_iob_wdata; - wire [ 3:0] ethernet_iob_wstrb; - wire [ `IOB_SYSTEM_DATA_W-1:0] ethernet_iob_rdata; - wire ethernet_iob_ready; - wire ethernet_iob_rvalid; - - - iob_eth_driver_tb eth_driver ( - .clk_i (clk), - .iob_valid_o (ethernet_iob_valid), - .iob_addr_o (ethernet_iob_addr), - .iob_wdata_o (ethernet_iob_wdata), - .iob_wstrb_o (ethernet_iob_wstrb), - .iob_rdata_i (ethernet_iob_rdata), - .iob_ready_i (ethernet_iob_ready), - .iob_rvalid_i(ethernet_iob_rvalid) - ); -`endif - - - iob_system_sim_wrapper iob_system_sim_wrapper ( - .clk_i (clk), - .cke_i (1'b1), - .arst_i(arst), - -`ifdef IOB_SYSTEM_USE_ETHERNET - .ethernet_iob_valid_i (ethernet_valid), - .ethernet_iob_addr_i (ethernet_addr), - .ethernet_iob_wdata_i (ethernet_wdata), - .ethernet_iob_wstrb_i (ethernet_wstrb), - .ethernet_iob_rdata_o (ethernet_rdata), - .ethernet_iob_ready_o (ethernet_ready), - .ethernet_iob_rvalid_o(ethernet_rvalid), -`endif - - .uart_iob_valid_i (iob_valid_i), - .uart_iob_addr_i (iob_addr_i), - .uart_iob_wdata_i (iob_wdata_i), - .uart_iob_wstrb_i (iob_wstrb_i), - .uart_iob_rdata_o (iob_rdata_o), - .uart_iob_ready_o (iob_ready_o), - .uart_iob_rvalid_o(iob_rvalid_o) - ); - - task cpu_inituart; - begin - //pulse reset uart - iob_write(`IOB_UART_SOFTRESET_ADDR, 1, `IOB_UART_SOFTRESET_W); - iob_write(`IOB_UART_SOFTRESET_ADDR, 0, `IOB_UART_SOFTRESET_W); - //config uart div factor - iob_write(`IOB_UART_DIV_ADDR, `FREQ / `BAUD, `IOB_UART_DIV_W); - //enable uart for receiving - iob_write(`IOB_UART_RXEN_ADDR, 1, `IOB_UART_RXEN_W); - iob_write(`IOB_UART_TXEN_ADDR, 1, `IOB_UART_TXEN_W); - end - endtask - - `include "iob_tasks.vs" - -endmodule diff --git a/lib/hardware/iob_system/hardware/syn/genus/syn_build.tcl b/lib/hardware/iob_system/hardware/syn/genus/syn_build.tcl deleted file mode 100644 index 30b959bf5..000000000 --- a/lib/hardware/iob_system/hardware/syn/genus/syn_build.tcl +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -set_db hdl_error_on_latch false \ No newline at end of file diff --git a/lib/hardware/iob_system/hardware/syn/src/bsp.vh b/lib/hardware/iob_system/hardware/syn/src/bsp.vh deleted file mode 100644 index e8b97e4a0..000000000 --- a/lib/hardware/iob_system/hardware/syn/src/bsp.vh +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - - -//empty file for now diff --git a/lib/hardware/iob_system/iob_system.py b/lib/hardware/iob_system/iob_system.py deleted file mode 100755 index 021532c7d..000000000 --- a/lib/hardware/iob_system/iob_system.py +++ /dev/null @@ -1,505 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -import sys -import os - -# Add iob-system scripts folder to python path -sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "scripts")) - -from iob_system_utils import update_params, iob_system_scripts - - -def setup(py_params_dict): - params = { - "name": "iob_system", - "init_mem": True, - "use_extmem": False, - "use_ethernet": False, - "addr_w": 32, - "data_w": 32, - "mem_addr_w": 24, - "bootrom_addr_w": 12, - "fw_addr": 0, - "fw_addr_w": 15, - } - - update_params(params, py_params_dict) - - attributes_dict = { - "name": params["name"], - "version": "0.7", - "is_system": True, - "board_list": ["cyclonev_gt_dk", "aes_ku040_db_g"], - "confs": [ - # macros - { # Needed for testbench - "name": "ADDR_W", - "descr": "Address bus width", - "type": "M", - "val": params["addr_w"], - "min": "1", - "max": "32", - }, - { # Needed for testbench - "name": "DATA_W", - "descr": "Data bus width", - "type": "M", - "val": params["data_w"], - "min": "1", - "max": "32", - }, - { # Needed for makefile and software - "name": "INIT_MEM", - "descr": "Enable MUL and DIV CPU instructions", - "type": "M", - "val": params["init_mem"], - "min": "0", - "max": "1", - }, - { # Needed for makefile and software - "name": "USE_EXTMEM", - "descr": "Enable MUL and DIV CPU instructions", - "type": "M", - "val": params["use_extmem"], - "min": "0", - "max": "1", - }, - { # Needed for software - "name": "MEM_ADDR_W", - "descr": "Memory bus address width", - "type": "M", - "val": params["mem_addr_w"], - "min": "0", - "max": "32", - }, - { # Needed for software - "name": "FW_ADDR", - "descr": "Firmware address", - "type": "M", - "val": params["fw_addr"], - "min": "0", - "max": "32", - }, - { # Needed for software - "name": "FW_ADDR_W", - "descr": "Firmware address", - "type": "M", - "val": params["fw_addr_w"], - "min": "0", - "max": "32", - }, - { # Needed for testbench - "name": "RST_POL", - "descr": "Reset polarity.", - "type": "M", - "val": "1", - "min": "0", - "max": "1", - }, - { # Needed for software and makefiles - "name": "BOOTROM_ADDR_W", - "descr": "Bootloader ROM address width (byte addressable). Includes a pre-bootloader that uses the first 128 bytes. Bootloader starts at address 0x80 of this ROM.", - "type": "M", - "val": params["bootrom_addr_w"], - "min": "1", - "max": "32", - }, - # mandatory parameters (do not change them!) - { - "name": "AXI_ID_W", - "descr": "AXI ID bus width", - "type": "F", - "val": "0", - "min": "1", - "max": "32", - }, - { - "name": "AXI_ADDR_W", - "descr": "AXI address bus width", - "type": "F", - "val": params["mem_addr_w"], - "min": "1", - "max": "32", - }, - { - "name": "AXI_DATA_W", - "descr": "AXI data bus width", - "type": "F", - "val": params["data_w"], - "min": "1", - "max": "32", - }, - { - "name": "AXI_LEN_W", - "descr": "AXI burst length width", - "type": "F", - "val": "4", - "min": "1", - "max": "4", - }, - ], - } - attributes_dict["ports"] = [ - { - "name": "clk_en_rst_s", - "descr": "Clock, clock enable and reset", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - }, - { - "name": "rom_bus", - "descr": "Ports for connection with ROM memory", - "signals": [ - { - "name": "boot_rom_valid", - "direction": "output", - "width": "1", - }, - { - "name": "boot_rom_addr", - "direction": "output", - "width": params["bootrom_addr_w"] - 2, - }, - { - "name": "boot_rom_rdata", - "direction": "input", - "width": params["data_w"], - }, - ], - }, - { - "name": "axi_m", - "descr": "AXI master interface for memory", - "interface": { - "type": "axi", - "subtype": "master", - "ID_W": "AXI_ID_W", - "ADDR_W": "AXI_ADDR_W", - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - "LOCK_W": "AXI_LEN_W", - }, - }, - # Peripheral IO ports - { - "name": "rs232_m", - "descr": "iob-system uart interface", - "interface": { - "type": "rs232", - }, - }, - # NOTE: Add ports for peripherals here - ] - - attributes_dict["wires"] = [ - { - "name": "clk", - "descr": "Clock signal", - "signals": [ - {"name": "clk"}, - ], - }, - { - "name": "rst", - "descr": "Reset signal", - "signals": [ - {"name": "arst"}, - ], - }, - { - "name": "split_reset", - "descr": "Reset signal for iob_split components", - "signals": [ - {"name": "arst"}, - ], - }, - { - "name": "cpu_ibus", - "descr": "CPU instruction bus", - "interface": { - "type": "axi", - "wire_prefix": "cpu_i_", - "ID_W": "AXI_ID_W", - "ADDR_W": params["addr_w"], - "DATA_W": params["data_w"], - "LEN_W": "AXI_LEN_W", - "LOCK_W": "1", - }, - }, - { - "name": "cpu_dbus", - "descr": "CPU data bus", - "interface": { - "type": "axi", - "wire_prefix": "cpu_d_", - "ID_W": "AXI_ID_W", - "ADDR_W": params["addr_w"], - "DATA_W": params["data_w"], - "LEN_W": "AXI_LEN_W", - "LOCK_W": "1", - }, - }, - { - "name": "interrupts", - "descr": "System interrupts", - "signals": [ - {"name": "interrupts", "width": 32}, - ], - }, - { - "name": "bootrom_cbus", - "descr": "iob-system boot controller data interface", - "interface": { - "type": "axi", - "wire_prefix": "bootrom_", - "ID_W": "AXI_ID_W", - "ADDR_W": params["addr_w"] - 2, - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - "LOCK_W": "1", - }, - }, - { - "name": "axi_periphs_cbus", - "descr": "AXI bus for peripheral CSRs", - "interface": { - "type": "axi", - "wire_prefix": "periphs_", - "ID_W": "AXI_ID_W", - "ADDR_W": params["addr_w"] - 1, - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - { - "name": "iob_periphs_cbus", - "descr": "AXI-Lite bus for peripheral CSRs", - "interface": { - "type": "iob", - "wire_prefix": "periphs_", - "ID_W": "AXI_ID_W", - "ADDR_W": params["addr_w"] - 1, - "DATA_W": "AXI_DATA_W", - "LEN_W": "AXI_LEN_W", - }, - }, - # Peripheral cbus wires added automatically - # NOTE: Add other peripheral wires here - ] - attributes_dict["blocks"] = [ - { - "core_name": "iob_vexriscv", - "instance_name": "cpu", - "instance_description": "RISC-V CPU instance", - "parameters": { - "AXI_ID_W": "1", - "AXI_ADDR_W": params["addr_w"], - "AXI_DATA_W": params["data_w"], - "AXI_LEN_W": "AXI_LEN_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "rst_i": "rst", - "i_bus_m": ( - "cpu_ibus", - "cpu_i_axi_arid[0]", - "cpu_i_axi_rid[0]", - "cpu_i_axi_awid[0]", - "cpu_i_axi_bid[0]", - ), - "d_bus_m": ( - "cpu_dbus", - "cpu_d_axi_arid[0]", - "cpu_d_axi_rid[0]", - "cpu_d_axi_awid[0]", - "cpu_d_axi_bid[0]", - ), - "plic_interrupts_i": "interrupts", - "plic_cbus_s": ( - "plic_cbus", - "plic_cbus_iob_addr[22-1:0]", - ), - "clint_cbus_s": ( - "clint_cbus", - "clint_cbus_iob_addr[16-1:0]", - ), - }, - }, - { - "core_name": "axi_interconnect_wrapper", - "name": "soc_axi_interconnect_wrapper", - "instance_name": "axi_interconnect", - "instance_description": "Interconnect instance", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_ADDR_W": params["addr_w"], - "AXI_DATA_W": "AXI_DATA_W", - "MEM_ADDR_W": "AXI_ADDR_W", - }, - "connect": { - "clk_i": "clk", - "rst_i": "rst", - "s0_axi_s": "cpu_ibus", - "s1_axi_s": "cpu_dbus", - "mem_axi_m": ( - "axi_m", - "axi_arlock_o[0]", - "axi_awlock_o[0]", - ), - "bootrom_axi_m": "bootrom_cbus", - "peripherals_axi_m": ( - "axi_periphs_cbus", - "periphs_axi_awlock[0]", - "periphs_axi_arlock[0]", - ), - }, - "num_slaves": 2, - "masters": { - "mem": params["addr_w"] - 2, - "bootrom": params["addr_w"] - 2, - "peripherals": params["addr_w"] - 1, - }, - }, - { - "core_name": "iob_bootrom", - "instance_name": "bootrom", - "instance_description": "Boot ROM peripheral", - "parameters": { - "AXI_ID_W": "AXI_ID_W", - "AXI_ADDR_W": "AXI_ADDR_W", - "AXI_DATA_W": "AXI_DATA_W", - "AXI_LEN_W": "AXI_LEN_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "cbus_s": ( - "bootrom_cbus", - "bootrom_axi_araddr[13-1:0]", - "bootrom_axi_arid[0]", - "bootrom_axi_rid[0]", - "{1'b0, bootrom_axi_arlock}", - "bootrom_axi_awaddr[13-1:0]", - "bootrom_axi_awid[0]", - "bootrom_axi_bid[0]", - "{1'b0, bootrom_axi_awlock}", - ), - "ext_rom_bus": "rom_bus", - }, - "bootrom_addr_w": params["bootrom_addr_w"], - "soc_name": params["name"], - }, - { - "core_name": "axi2iob", - "instance_name": "periphs_axi2iob", - "instance_description": "Convert AXI to AXI lite for CLINT", - "parameters": { - "AXI_ID_WIDTH": "AXI_ID_W", - "ADDR_WIDTH": params["addr_w"] - 1, - "DATA_WIDTH": "AXI_DATA_W", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "axi_s": ( - "axi_periphs_cbus", - "periphs_axi_arlock[0]", - "periphs_axi_awlock[0]", - ), - "iob_m": "iob_periphs_cbus", - }, - }, - { - "core_name": "iob_split", - "name": "iob_pbus_split", - "instance_name": "iob_pbus_split", - "instance_description": "Split between peripherals", - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "reset_i": "split_reset", - "input_s": "iob_periphs_cbus", - # Peripherals cbus connections added automatically - }, - "num_outputs": 0, # Num outputs configured automatically - "addr_w": params["addr_w"] - 1, - }, - # Peripherals - { - "core_name": "iob_uart", - "instance_name": "UART0", - "instance_description": "UART peripheral", - "peripheral_addr_w": 3, - "parameters": {}, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - # Cbus connected automatically - "rs232_m": "rs232_m", - }, - }, - { - "core_name": "iob_timer", - "instance_name": "TIMER0", - "instance_description": "Timer peripheral", - "peripheral_addr_w": 4, - "parameters": {}, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - # Cbus connected automatically - }, - }, - # NOTE: Instantiate other peripherals here, using the 'peripheral_addr_w' flag - # - # Modules that need to be setup, but are not instantiated directly inside - # 'iob_system' Verilog module - # Testbench - { - "core_name": "iob_tasks", - "instance_name": "iob_tasks_inst", - "instantiate": False, - "dest_dir": "hardware/simulation/src", - }, - # Simulation wrapper - { - "core_name": "iob_system_sim_wrapper", - "instance_name": "iob_system_sim_wrapper", - "instantiate": False, - "dest_dir": "hardware/simulation/src", - "iob_system_params": params, - }, - # FPGA wrappers - { - "core_name": "aes_ku040_db_g", - "instance_name": "aes_ku040_db_g", - "instantiate": False, - "dest_dir": "hardware/fpga/vivado/aes_ku040_db_g", - "iob_system_params": params, - }, - { - "core_name": "cyclonev_gt_dk", - "instance_name": "cyclonev_gt_dk", - "instantiate": False, - "dest_dir": "hardware/fpga/quartus/cyclonev_gt_dk", - "iob_system_params": params, - }, - ] - attributes_dict["sw_modules"] = [ - # Software modules - { - "core_name": "printf", - "instance_name": "printf_inst", - }, - ] - attributes_dict["snippets"] = [ - { - "verilog_code": """ - //assign interrupts = {{30{1'b0}}, uart_interrupt_o, 1'b0}; - assign interrupts = {{30{1'b0}}, 1'b0, 1'b0}; -""" - } - ] - - iob_system_scripts(attributes_dict, params, py_params_dict) - - return attributes_dict diff --git a/lib/hardware/iob_system/scripts/iob_system_utils.py b/lib/hardware/iob_system/scripts/iob_system_utils.py deleted file mode 100644 index 85de01473..000000000 --- a/lib/hardware/iob_system/scripts/iob_system_utils.py +++ /dev/null @@ -1,308 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -import os -import sys - -# -# Functions for iob_system.py -# - - -def update_params(params, py_params): - """Update given `params` dictionary with values from `py_params` dictionary. - Paramters will be updated to have the same type as the default value. - :param dict params: dictionary to update. Contains default values and their types. - :param dict py_params: dictionary to use as source. Contains new values. - Their types will be converted to match the corresponding default type. - """ - for name, default_val in params.items(): - if name not in py_params: - continue - if type(default_val) is bool and py_params[name] == "0": - params[name] = False - else: - params[name] = type(default_val)(py_params[name]) - - -def iob_system_scripts(attributes_dict, params, py_params): - """IOb-SoC automatic setup scripts. - :param dict attributes_dict: iob_system attributes - :param dict params: iob_system python parameters - :param dict py_params: iob_system argument python parameters - """ - handle_system_overrides(attributes_dict, py_params) - set_build_dir(attributes_dict, py_params) - peripherals = get_iob_system_peripherals_list(attributes_dict) - connect_peripherals_cbus(attributes_dict, peripherals, params) - generate_makefile_segments(attributes_dict, peripherals, params, py_params) - generate_peripheral_base_addresses(attributes_dict, peripherals, params, py_params) - - -# -# Local functions -# - - -def handle_system_overrides(attributes_dict, py_params): - """Override/append attributes given in `system_attributes` python parameter (usually by child core). - :param dict attributes_dict: iob_system attributes - :param dict py_params: Dictionary containing `system_attributes` python parameter - """ - child_attributes = py_params.get("system_attributes") - if not child_attributes: - return - - for child_attribute_name, child_value in child_attributes.items(): - # Don't override child-specific attributes - if child_attribute_name in ["original_name", "setup_dir", "parent"]: - continue - - # Override other attributes of type string - if type(child_value) is str: - attributes_dict[child_attribute_name] = child_value - continue - - # Override or append elements from list attributes - assert ( - type(child_value) is list - ), f"Invalid type for attribute '{child_attribute_name}': {type(child_value)}" - - # Select identifier attribute. Used to compare if should override each element. - identifier = "name" - if child_attribute_name in ["blocks", "sw_modules"]: - identifier = "instance_name" - elif child_attribute_name in ["board_list", "ignore_snippets"]: - # Elements in list do not have identifier, so just append them to parent list - for child_obj in child_value: - attributes_dict[child_attribute_name].append(child_obj) - continue - - # Process each object from list - for child_obj in child_value: - # Find object and override it - for idx, obj in enumerate(attributes_dict[child_attribute_name]): - if obj[identifier] == child_obj[identifier]: - # print(f"DEBUG: Overriding {child_obj[identifier]}", file=sys.stderr) - attributes_dict[child_attribute_name][idx] = child_obj - break - else: - # Didn't override, so append it to list - attributes_dict[child_attribute_name].append(child_obj) - - -def set_build_dir(attributes_dict, py_params): - """If build_dir not given in py_params, set a default one. - :param dict attributes_dict: iob_system attributes - :param dict py_params: iob_system argument python parameters - """ - if "build_dir" in py_params and py_params["build_dir"]: - build_dir = py_params["build_dir"] - else: - build_dir = f"../{attributes_dict['name']}_V{attributes_dict['version']}" - attributes_dict["build_dir"] = build_dir - - -def get_iob_system_peripherals_list(attributes_dict): - """Parses blocks list in iob_system attributes, for blocks with the `peripheral_addr_w` attribute set. - Also removes `peripheral_addr_w` attribute from each block after adding it to the peripherals list. - """ - peripherals = [] - for block in attributes_dict["blocks"]: - if "peripheral_addr_w" in block and block["peripheral_addr_w"]: - peripherals.append({"ref": block, "addr_w": block["peripheral_addr_w"]}) - block.pop("peripheral_addr_w") - return peripherals - - -def connect_peripherals_cbus(attributes_dict, peripherals, params): - """Update given attributes_dict to connect peripherals cbus to system's pbus_split. - :param dict attributes_dict: iob_system attributes - :param list peripherals: list of peripheral blocks - :param dict params: iob_system python parameters - """ - # Find pbus_split - pbus_split = None - for block in attributes_dict["blocks"]: - if block["instance_name"] == "iob_pbus_split": - pbus_split = block - - # Number of peripherals = peripherals + CLINT + PLIC - num_peripherals = len(peripherals) + 2 - peripheral_addr_w = params["addr_w"] - 1 - (num_peripherals - 1).bit_length() - - # Configure number of connections to pbus_split - pbus_split["num_outputs"] = num_peripherals - - for idx, peripheral in enumerate(peripherals): - peripheral_name = peripheral["ref"]["instance_name"].lower() - # Add peripheral cbus wire - attributes_dict["wires"].append( - { - "name": f"{peripheral_name}_cbus", - "descr": f"{peripheral_name} Control/Status Registers bus", - "interface": { - "type": "iob", - "wire_prefix": f"{peripheral_name}_cbus_", - "ADDR_W": peripheral_addr_w, - }, - }, - ) - # Connect cbus to pbus_split - pbus_split["connect"][f"output_{idx}_m"] = f"{peripheral_name}_cbus" - # Connect cbus to peripheral - peripheral["ref"]["connect"]["cbus_s"] = ( - f"{peripheral_name}_cbus", - f"{peripheral_name}_cbus_iob_addr[{peripheral['addr_w']}-1:0]", - ) - - # Add CLINT and PLIC wires (they are not in peripherals list) - attributes_dict["wires"] += [ - { - "name": "clint_cbus", - "descr": "CLINT Control/Status Registers bus", - "interface": { - "type": "iob", - "wire_prefix": "clint_cbus_", - "ADDR_W": peripheral_addr_w, - }, - }, - { - "name": "plic_cbus", - "descr": "PLIC Control/Status Registers bus", - "interface": { - "type": "iob", - "wire_prefix": "plic_cbus_", - "ADDR_W": peripheral_addr_w, - }, - }, - ] - - # Connect CLINT and PLIC cbus to last outputs of pbus_split - pbus_split["connect"][f"output_{num_peripherals-2}_m"] = "clint_cbus" - pbus_split["connect"][f"output_{num_peripherals-1}_m"] = "plic_cbus" - - -def generate_peripheral_base_addresses( - attributes_dict, peripherals_list, params, py_params -): - """Create C header file containing peripheral base addresses. - :param dict attributes_dict: iob_system attributes - :param list peripherals_list: list of peripheral blocks - :param dict params: iob_system python parameters - :param dict py_params: iob_system argument python parameters - """ - out_file = os.path.join( - attributes_dict["build_dir"], "software", f"{attributes_dict['name']}_periphs.h" - ) - # Don't override output file - if os.path.isfile(out_file): - return - - # Include CLINT and PLIC in peripherals list - complete_peripherals_list = peripherals_list + [ - {"ref": {"instance_name": "CLINT0"}}, - {"ref": {"instance_name": "PLIC0"}}, - ] - n_slaves_w = (len(complete_peripherals_list) - 1).bit_length() - - # Don't create files for other targets (like clean) - if "py2hwsw_target" not in py_params or py_params["py2hwsw_target"] != "setup": - return - - os.makedirs(os.path.dirname(out_file), exist_ok=True) - with open(out_file, "w") as f: - for idx, instance in enumerate(complete_peripherals_list): - instance_name = instance["ref"]["instance_name"] - f.write( - f"#define {instance_name}_BASE (PBUS_BASE + ({idx}<<(P_BIT-{n_slaves_w})))\n" - ) - - -def generate_makefile_segments(attributes_dict, peripherals, params, py_params): - """Generate automatic makefile segments for iob_system. - :param dict attributes_dict: iob_system attributes - :param list peripherals: list of peripheral blocks - :param dict params: iob_system python parameters - :param dict py_params: iob_system argument python parameters - """ - name = attributes_dict["name"] - build_dir = attributes_dict["build_dir"] - - # Don't create files for other targets (like clean) - if "py2hwsw_target" not in py_params or py_params["py2hwsw_target"] != "setup": - return - - # - # Create auto_sw_build.mk - # - os.makedirs(f"{build_dir}/software", exist_ok=True) - with open(f"{build_dir}/software/auto_sw_build.mk", "w") as file: - file.write("#This file was auto generated by iob_system_utils.py\n") - # Create a list with every peripheral name, except clint, and plic - file.write( - "PERIPHERALS ?=" - + " ".join(peripheral["ref"]["core_name"] for peripheral in peripherals) - + "\n", - ) - if params["use_ethernet"]: - # Set custom ethernet CONSOLE_CMD - file.write( - 'CONSOLE_CMD ?=rm -f soc2cnsl cnsl2soc; $(IOB_CONSOLE_PYTHON_ENV) $(PYTHON_DIR)/console_ethernet.py -L -c $(PYTHON_DIR)/console.py -m "$(RMAC_ADDR)" -i "$(ETH_IF)"\n', - ) - file.write( - """\ - UTARGETS+=iob_eth_rmac.h - EMUL_HDR+=iob_eth_rmac.h - iob_eth_rmac.h: - echo "#define ETH_RMAC_ADDR 0x$(RMAC_ADDR)" > $@\n -""", - ) - - # - # Create auto_fpga_build.mk - # - os.makedirs(f"{build_dir}/hardware/fpga", exist_ok=True) - with open(f"{build_dir}/hardware/fpga/auto_fpga_build.mk", "w") as file: - file.write("#This file was auto generated by iob_system_utils.py\n") - - # Set N_INTERCONNECT_SLAVES variable - # TODO: Count axi interfaces automatically for peripherals with DMA - file.write("N_INTERCONNECT_SLAVES:=1\n") - # Set USE_EXTMEM variable - file.write(f"USE_EXTMEM:={int(params['use_extmem'])}\n") - # Set INIT_MEM variable - file.write(f"INIT_MEM:={int(params['init_mem'])}\n") - if params["use_ethernet"]: - # Set custom ethernet CONSOLE_CMD - file.write( - 'CONSOLE_CMD=$(IOB_CONSOLE_PYTHON_ENV) $(PYTHON_DIR)/console_ethernet.py -s /dev/usb-uart -c $(PYTHON_DIR)/console.py -m "$(RMAC_ADDR)" -i "$(ETH_IF)"\n', - ) - - # - # Create auto_sim_build.mk - # - os.makedirs(f"{build_dir}/hardware/simulation", exist_ok=True) - with open(f"{build_dir}/hardware/simulation/auto_sim_build.mk", "w") as file: - file.write("#This file was auto generated by iob_system_utils.py\n") - if params["use_ethernet"]: - file.write("USE_ETHERNET=1\n") - # Set custom ethernet CONSOLE_CMD - file.write( - 'ETH2FILE_SCRIPT="$(PYTHON_DIR)/eth2file.py"\n' - 'CONSOLE_CMD=$(IOB_CONSOLE_PYTHON_ENV) $(PYTHON_DIR)/console_ethernet.py -L -c $(PYTHON_DIR)/console.py -e $(ETH2FILE_SCRIPT) -m "$(RMAC_ADDR)" -i "$(ETH_IF)" -t 60\n', - ) - - # - # Create auto_iob_system_boot.lds and auto_iob_system_firmware.lds - # - os.makedirs(f"{build_dir}/software", exist_ok=True) - with open(f"{build_dir}/software/auto_{name}_boot.lds", "w") as file: - file.write("/* This file was auto generated by iob_system_utils.py */\n") - file.write( - f". = {hex((1 << params['mem_addr_w']) - (1 << params['bootrom_addr_w']))};\n" - ) - with open(f"{build_dir}/software/auto_{name}_firmware.lds", "w") as file: - file.write("/* This file was auto generated by iob_system_utils.py */\n") - file.write(f". = {params['fw_addr']};\n") diff --git a/lib/hardware/iob_system/software/src/iob_system_firmware.S b/lib/hardware/iob_system/software/src/iob_system_firmware.S deleted file mode 100644 index c7438f46c..000000000 --- a/lib/hardware/iob_system/software/src/iob_system_firmware.S +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -#include "iob_system_conf.h" -#include "iob_system_periphs.h" -#include "iob_system_system.h" - -.section .init -.globl main - - //set stack pointer - lui sp, %hi(1 << IOB_SYSTEM_FW_ADDR_W) - addi sp, sp, %lo(1 << IOB_SYSTEM_FW_ADDR_W) - - //call main - jal ra, main - - // Jump to the pre-bootloader - li x5, 0x80000000 - jalr x0, 0(x5) diff --git a/lib/hardware/iob_system/software/src/iob_system_firmware.c b/lib/hardware/iob_system/software/src/iob_system_firmware.c deleted file mode 100644 index 222a2765e..000000000 --- a/lib/hardware/iob_system/software/src/iob_system_firmware.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "bsp.h" -#include "iob_system_conf.h" -#include "iob_system_periphs.h" -#include "iob_system_system.h" -#include "iob_timer.h" -#include "iob_uart.h" -#include "printf.h" -#include - -char *send_string = "Sending this string as a file to console.\n" - "The file is then requested back from console.\n" - "The sent file is compared to the received file to confirm " - "correct file transfer via UART using console.\n" - "Generating the file in the firmware creates an uniform " - "file transfer between pc-emul, simulation and fpga without" - " adding extra targets for file generation.\n"; - -int main() { - char pass_string[] = "Test passed!"; - char fail_string[] = "Test failed!"; - - // init timer - timer_init(TIMER0_BASE); - - // init uart - uart_init(UART0_BASE, FREQ / BAUD); - printf_init(&uart_putc); - - // test puts - uart_puts("\n\n\nHello world!\n\n\n"); - - // test printf with floats - printf("Value of Pi = %f\n\n", 3.1415); - - // test file send - char *sendfile = malloc(1000); - int send_file_size = 0; - send_file_size = strlen(strcpy(sendfile, send_string)); - uart_sendfile("Sendfile.txt", send_file_size, sendfile); - - // test file receive - char *recvfile = malloc(10000); - int file_size = 0; - file_size = uart_recvfile("Sendfile.txt", recvfile); - - // compare files - if (strcmp(sendfile, recvfile)) { - printf("FAILURE: Send and received file differ!\n"); - } else { - printf("SUCCESS: Send and received file match!\n"); - } - - free(sendfile); - free(recvfile); - - uart_sendfile("test.log", strlen(pass_string), pass_string); - - // read current timer count, compute elapsed time - unsigned long long elapsed = timer_get_count(); - unsigned int elapsedu = elapsed / (FREQ / 1000000); - - printf("\nExecution time: %d clock cycles\n", (unsigned int)elapsed); - printf("\nExecution time: %dus @%dMHz\n\n", elapsedu, FREQ / 1000000); - - uart_finish(); -} diff --git a/lib/hardware/iob_system/software/src/iob_system_firmware.lds b/lib/hardware/iob_system/software/src/iob_system_firmware.lds deleted file mode 100644 index c798abfc5..000000000 --- a/lib/hardware/iob_system/software/src/iob_system_firmware.lds +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -SECTIONS { - /* Program code */ - INCLUDE auto_iob_system_firmware.lds - .init : { *(.init) } - .text : { *(.text) } - - .memory : { - /* Read-only data */ - *(.rodata); - - /* Initialized data */ - *(.data); - - /* Uninitialized data */ - *(.bss); - - /* Any symbols not explicitly mentioned in previous sections are included in this section */ - *(*); - } - - /* Stack */ - stack_start = .; - .stack : { *(.stack) } - stack_end = .; - - . = ALIGN(4); - _heap_start = .; - end = .; -} diff --git a/lib/hardware/iob_system/software/src/iob_system_system.h b/lib/hardware/iob_system/software/src/iob_system_system.h deleted file mode 100644 index 798a87749..000000000 --- a/lib/hardware/iob_system/software/src/iob_system_system.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#define B_BIT 30 // Bootrom selection bit -#define P_BIT 31 // Peripheral bus selection bit - -// peripheral bus base -#define PBUS_BASE (1 << P_BIT) - -#define BOOTROM_BASE (1 << B_BIT) diff --git a/lib/hardware/iob_system/software/sw_build.mk b/lib/hardware/iob_system/software/sw_build.mk deleted file mode 100644 index e3f12fb7a..000000000 --- a/lib/hardware/iob_system/software/sw_build.mk +++ /dev/null @@ -1,85 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -######################################### -# Embedded targets # -######################################### -ROOT_DIR ?=.. - -include $(ROOT_DIR)/software/auto_sw_build.mk - -# Local embedded makefile settings for custom bootloader and firmware targets. - -#Function to obtain parameter named $(1) in verilog header file located in $(2) -#Usage: $(call GET_MACRO,,) -GET_MACRO = $(shell grep "define $(1)" $(2) | rev | cut -d" " -f1 | rev) - -#Function to obtain parameter named $(1) from iob_system_conf.vh -GET_IOB_SYSTEM_CONF_MACRO = $(call GET_MACRO,IOB_SYSTEM_$(1),../src/iob_system_conf.vh) - -iob_system_bootrom.hex: ../../software/iob_system_preboot.bin ../../software/iob_system_boot.bin - ../../scripts/makehex.py $^ 00000080 $(call GET_IOB_SYSTEM_CONF_MACRO,BOOTROM_ADDR_W) > $@ - -iob_system_firmware.hex: iob_system_firmware.bin - ../../scripts/makehex.py $< $(call GET_IOB_SYSTEM_CONF_MACRO,MEM_ADDR_W) > $@ - ../../scripts/hex_split.py iob_system_firmware . - -iob_system_firmware.bin: ../../software/iob_system_firmware.bin - cp $< $@ - -../../software/%.bin: - make -C ../../ fw-build - -UTARGETS+=build_iob_system_software - -TEMPLATE_LDS=src/$@.lds - -IOB_SYSTEM_INCLUDES=-I. -Isrc -Iinclude - -IOB_SYSTEM_LFLAGS=-Wl,-Bstatic,-T,$(TEMPLATE_LDS),--strip-debug - -# FIRMWARE SOURCES -IOB_SYSTEM_FW_SRC=src/iob_system_firmware.S -IOB_SYSTEM_FW_SRC+=src/iob_system_firmware.c -IOB_SYSTEM_FW_SRC+=src/printf.c -# PERIPHERAL SOURCES -IOB_SYSTEM_FW_SRC+=$(addprefix src/,$(addsuffix .c,$(PERIPHERALS))) -IOB_SYSTEM_FW_SRC+=$(addprefix src/,$(addsuffix _csrs_emb.c,$(PERIPHERALS))) - -# BOOTLOADER SOURCES -IOB_SYSTEM_BOOT_SRC+=src/iob_system_boot.S -IOB_SYSTEM_BOOT_SRC+=src/iob_system_boot.c -IOB_SYSTEM_BOOT_SRC+=src/iob_uart.c -IOB_SYSTEM_BOOT_SRC+=src/iob_uart_csrs_emb.c - -# PREBOOT SOURCES -IOB_SYSTEM_PREBOOT_SRC=src/iob_system_preboot.S - -build_iob_system_software: iob_system_firmware iob_system_boot iob_system_preboot - -iob_system_firmware: - make $@.elf INCLUDES="$(IOB_SYSTEM_INCLUDES)" LFLAGS="$(IOB_SYSTEM_LFLAGS) -Wl,-Map,$@.map" SRC="$(IOB_SYSTEM_FW_SRC)" TEMPLATE_LDS="$(TEMPLATE_LDS)" - -iob_system_boot: - make $@.elf INCLUDES="$(IOB_SYSTEM_INCLUDES)" LFLAGS="$(IOB_SYSTEM_LFLAGS) -Wl,-Map,$@.map" SRC="$(IOB_SYSTEM_BOOT_SRC)" TEMPLATE_LDS="$(TEMPLATE_LDS)" - -iob_system_preboot: - make $@.elf INCLUDES="$(IOB_SYSTEM_INCLUDES)" LFLAGS="$(IOB_SYSTEM_LFLAGS) -Wl,-Map,$@.map" SRC="$(IOB_SYSTEM_PREBOOT_SRC)" TEMPLATE_LDS="$(TEMPLATE_LDS)" - - -.PHONY: build_iob_system_software iob_system_firmware iob_system_boot - -######################################### -# PC emulation targets # -######################################### -# Local pc-emul makefile settings for custom pc emulation targets. - -# SOURCES -EMUL_SRC+=src/iob_system_firmware.c -EMUL_SRC+=src/printf.c - -# PERIPHERAL SOURCES -EMUL_SRC+=$(addprefix src/,$(addsuffix .c,$(PERIPHERALS))) -EMUL_SRC+=$(addprefix src/,$(addsuffix _csrs_pc_emul.c,$(PERIPHERALS))) - diff --git a/lib/hardware/iob_system/submodules/BOOTROM/iob_bootrom.py b/lib/hardware/iob_system/submodules/BOOTROM/iob_bootrom.py deleted file mode 100644 index 6cecf449e..000000000 --- a/lib/hardware/iob_system/submodules/BOOTROM/iob_bootrom.py +++ /dev/null @@ -1,251 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -import os -import sys -import shutil - - -def setup(py_params_dict): - VERSION = "0.1" - BOOTROM_ADDR_W = ( - py_params_dict["bootrom_addr_w"] if "bootrom_addr_w" in py_params_dict else 12 - ) - - attributes_dict = { - "version": VERSION, - "confs": [ - { - "name": "DATA_W", - "descr": "Data bus width", - "type": "F", - "val": "32", - "min": "0", - "max": "32", - }, - { - "name": "ADDR_W", - "descr": "Address bus width", - "type": "F", - "val": BOOTROM_ADDR_W - 2, - "min": "0", - "max": "32", - }, - { - "name": "AXI_ID_W", - "descr": "AXI ID bus width", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - }, - { - "name": "AXI_ADDR_W", - "descr": "AXI address bus width", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - }, - { - "name": "AXI_DATA_W", - "descr": "AXI data bus width", - "type": "P", - "val": "0", - "min": "1", - "max": "32", - }, - { - "name": "AXI_LEN_W", - "descr": "AXI burst length width", - "type": "P", - "val": "0", - "min": "1", - "max": "4", - }, - ], - # - # Ports - # - "ports": [ - { - "name": "clk_en_rst_s", - "descr": "Clock and reset", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - }, - { - "name": "cbus_s", - "descr": "Front-end control interface", - "interface": { - "type": "axi", - "subtype": "slave", - "port_prefix": "cbus_", - # BOOTROM_ADDR_W + 1 for remaining csrs ("VERSION" csr) - "ADDR_W": BOOTROM_ADDR_W + 1, - "DATA_W": "DATA_W", - }, - }, - { - "name": "ext_rom_bus", - "descr": "External ROM signals", - "signals": [ - { - "name": "ext_rom_en", - "direction": "output", - "width": "1", - }, - { - "name": "ext_rom_addr", - "direction": "output", - "width": BOOTROM_ADDR_W - 2, - }, - { - "name": "ext_rom_rdata", - "direction": "input", - "width": "DATA_W", - }, - ], - }, - ], - # - # Wires - # - "wires": [ - { - "name": "csrs_iob", - "descr": "Internal iob interface", - "interface": { - "type": "iob", - "wire_prefix": "csrs_", - # BOOTROM_ADDR_W + 1 for remaining csrs ("VERSION" csr) - "ADDR_W": BOOTROM_ADDR_W + 1, - "DATA_W": "DATA_W", - }, - }, - { - "name": "rom", - "descr": "'rom' register interface", - "signals": [ - {"name": "rom_rdata_rd", "width": "DATA_W"}, - {"name": "rom_rvalid_rd", "width": 1}, - {"name": "rom_ren_rd", "width": 1}, - {"name": "rom_rready_rd", "width": 1}, - ], - }, - { - "name": "rom_rvalid_data_i", - "descr": "Register input", - "signals": [ - {"name": "rom_ren_rd"}, - ], - }, - { - "name": "rom_rvalid_data_o", - "descr": "Register output", - "signals": [ - {"name": "rom_rvalid_rd"}, - ], - }, - ], - # - # Blocks - # - "blocks": [ - { - "core_name": "csrs", - "instance_name": "csrs_inst", - "version": VERSION, - "csrs": [ - { - "name": "rom", - "descr": "ROM access.", - "regs": [ - { - "name": "rom", - "descr": "Bootloader ROM (read).", - "type": "R", - "n_bits": "DATA_W", - "rst_val": 0, - "addr": -1, - "log2n_items": BOOTROM_ADDR_W - 2, - "autoreg": False, - }, - ], - } - ], - "csr_if": "axi", - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "cbus_s", - "csrs_iob_o": "csrs_iob", - # Register interfaces - "rom": "rom", - }, - }, - { - "core_name": "iob_reg", - "instance_name": "rom_rvalid_r", - "instance_description": "ROM rvalid register", - "parameters": { - "DATA_W": 1, - "RST_VAL": "1'b0", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "rom_rvalid_data_i", - "data_o": "rom_rvalid_data_o", - }, - }, - ], - # - # Snippets - # - "snippets": [ - { - "verilog_code": f""" - assign ext_rom_en_o = rom_ren_rd; - assign ext_rom_addr_o = csrs_iob_addr[{BOOTROM_ADDR_W}-1:2]; - assign rom_rdata_rd = ext_rom_rdata_i; - assign rom_rready_rd = 1'b1; // ROM is always ready -""", - }, - ], - } - - copy_sw_srcs_with_rename(py_params_dict) - - return attributes_dict - - -def copy_sw_srcs_with_rename(py_params): - """Copy software sources, and rename them based on correct SoC name.""" - SOC_NAME = py_params.get("soc_name", "iob_system") - - # Don't create files for other targets (like clean) - if py_params.get("py2hwsw_target") != "setup": - return - - SRC_DIR = os.path.join(os.path.dirname(__file__), "software_templates/src") - DEST_DIR = os.path.join(py_params.get("build_dir"), "software/src") - os.makedirs(DEST_DIR, exist_ok=True) - - for filename in os.listdir(SRC_DIR): - new_filename = filename.replace("iob_system", SOC_NAME) - src = os.path.join(SRC_DIR, filename) - dst = os.path.join(DEST_DIR, new_filename) - - # Read file, replace strings with SoC name, and write new file - with open(src, "r") as file: - lines = file.readlines() - for idx in range(len(lines)): - lines[idx] = ( - lines[idx] - .replace("iob_system", SOC_NAME) - .replace("iob_system".upper(), SOC_NAME.upper()) - ) - with open(dst, "w") as file: - file.writelines(lines) diff --git a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.S b/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.S deleted file mode 100644 index 4f1c25486..000000000 --- a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.S +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -#include "iob_system_conf.h" -#include "iob_system_periphs.h" -#include "iob_system_system.h" - -.section .init -.globl main - - //set stack pointer - lui sp, %hi(1 << IOB_SYSTEM_MEM_ADDR_W) - addi sp, sp, %lo(1 << IOB_SYSTEM_MEM_ADDR_W) - - //call main - jal ra, main - - // Jump to the firmware - li x5, IOB_SYSTEM_FW_ADDR - jalr x0, 0(x5) diff --git a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.c b/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.c deleted file mode 100644 index f7283e4bf..000000000 --- a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "bsp.h" -#include "iob_system_conf.h" -#include "iob_system_periphs.h" -#include "iob_system_system.h" -#include "iob_uart.h" - -#define PROGNAME "IOb-Bootloader" - -int main() { - - // init uart - uart_init(UART0_BASE, FREQ / BAUD); - - // connect with console - do { - if (IOB_UART_GET_TXREADY()) - uart_putc((char)ENQ); - } while (!IOB_UART_GET_RXREADY()); - - // welcome message - uart_puts(PROGNAME); - uart_puts(": connected!\n"); - -#ifdef IOB_SYSTEM_USE_EXTMEM - uart_puts(PROGNAME); - uart_puts(": DDR in use and program runs from DDR\n"); -#endif - - // address to copy firmware to - char *prog_start_addr = (char *)IOB_SYSTEM_FW_ADDR; - - while (uart_getc() != ACK) { - uart_puts(PROGNAME); - uart_puts(": Waiting for Console ACK.\n"); - } - -#ifndef IOB_SYSTEM_INIT_MEM - // receive firmware from host - int file_size = 0; - char r_fw[] = "iob_system_firmware.bin"; - file_size = uart_recvfile(r_fw, prog_start_addr); - uart_puts(PROGNAME); - uart_puts(": Loading firmware...\n"); - - // sending firmware back for debug - char s_fw[] = "s_fw.bin"; - - if (file_size) - uart_sendfile(s_fw, file_size, prog_start_addr); - else { - uart_puts(PROGNAME); - uart_puts(": ERROR loading firmware\n"); - } -#endif - - // run firmware - uart_puts(PROGNAME); - uart_puts(": Restart CPU to run user program...\n"); - uart_txwait(); -} diff --git a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.lds b/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.lds deleted file mode 100644 index 5a33546ec..000000000 --- a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_boot.lds +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -SECTIONS { - /* Program code */ - INCLUDE auto_iob_system_boot.lds - .init : { *(.init) } - .text : { *(.text) } - - .memory : { - /* Read-only data */ - *(.rodata); - - /* Initialized data */ - *(.data); - - /* Uninitialized data */ - *(.bss); - - /* Any symbols not explicitly mentioned in previous sections are included in this section */ - *(*); - } - - /* Stack */ - stack_start = .; - .stack : { *(.stack) } - stack_end = .; - - . = ALIGN(4); - _heap_start = .; - end = .; -} diff --git a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.S b/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.S deleted file mode 100644 index 144c2c30e..000000000 --- a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.S +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -#include "iob_system_conf.h" -#include "iob_system_system.h" - -// Can't include iob_bootrom_swreg.h because the assembler doesn't recognize stdint.h, -// so define the constants here instead (these are address offsets). -#define IOB_BOOTROM_ROM_ADDR 0x80 - -#define LENGTH ((1 << IOB_SYSTEM_BOOTROM_ADDR_W) - IOB_BOOTROM_ROM_ADDR) -#define BOOTROM (BOOTROM_BASE + IOB_BOOTROM_ROM_ADDR) -#define BOOTLDR_ADDR ((1 << IOB_SYSTEM_MEM_ADDR_W) - (1 << IOB_SYSTEM_BOOTROM_ADDR_W)) - -.section .init -.globl _start - -_start: - li x1, LENGTH - li x2, BOOTROM - li x3, BOOTLDR_ADDR - -copy_loop: - lw x4, 0(x2) - sw x4, 0(x3) - addi x2, x2, 4 - addi x3, x3, 4 - addi x1, x1, -4 - bne x1, x0, copy_loop - - // Jump to the bootloader - li x5, BOOTLDR_ADDR - jalr x0, 0(x5) diff --git a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.lds b/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.lds deleted file mode 100644 index 709b6c57b..000000000 --- a/lib/hardware/iob_system/submodules/BOOTROM/software_templates/src/iob_system_preboot.lds +++ /dev/null @@ -1,16 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -SECTIONS { - .memory : { - . = 0x000000; - *(.init); - *(.text); - *(*); - . = ALIGN(4); - _heap_start = .; - } -} diff --git a/lib/hardware/iob_system/submodules/VEXRISCV b/lib/hardware/iob_system/submodules/VEXRISCV deleted file mode 160000 index 606637108..000000000 --- a/lib/hardware/iob_system/submodules/VEXRISCV +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6066371084ce19ce40b00a699681b5fc761bfff2 diff --git a/lib/hardware/iob_timer/hardware/simulation/src/timer_tb.v b/lib/hardware/iob_timer/hardware/simulation/src/timer_tb.v deleted file mode 100644 index 34ac61e33..000000000 --- a/lib/hardware/iob_timer/hardware/simulation/src/timer_tb.v +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`include "iob_timer_csrs_def.vh" - -module timer_tb; - - localparam PER = 10; - localparam DATA_W = 32; - - integer fd; - - reg clk; - initial clk = 0; - always #(PER / 2) clk = ~clk; - - reg rst; - - reg TIMER_ENABLE; - reg TIMER_SAMPLE; - wire [2*DATA_W-1:0] TIMER_VALUE; - - initial begin -`ifdef VCD - $dumpfile("timer.vcd"); - $dumpvars(); -`endif - TIMER_ENABLE = 0; - TIMER_SAMPLE = 0; - - rst = 1; - // deassert hard reset - @(posedge clk) #1 rst = 0; - @(posedge clk) #1 TIMER_ENABLE = 1; - @(posedge clk) #1 TIMER_SAMPLE = 1; - @(posedge clk) #1 TIMER_SAMPLE = 0; - - //uncomment to fail the test - //@(posedge clk) #1; - - $write("Current time: %d; Timer value %d\n", $time(), TIMER_VALUE); - #(1000 * PER) @(posedge clk) #1 TIMER_SAMPLE = 1; - @(posedge clk) #1 TIMER_SAMPLE = 0; - $write("Current time: %d; Timer value %d\n", $time(), TIMER_VALUE); - - if (TIMER_VALUE == 1003) begin - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - - end else begin - $display("Test failed: expecting timer value 1003 but got %d", TIMER_VALUE); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test failed: expecting timer value 1003 but got %d", TIMER_VALUE); - $fclose(fd); - end - - $finish(); - end - - //instantiate timer core - timer_core timer0 ( - .en_i (TIMER_ENABLE), - .rstrb_i(TIMER_SAMPLE), - .time_o (TIMER_VALUE), - .clk_i (clk), - .cke_i (1'b1), - .arst_i (rst), - .rst_i (rst) - ); - -endmodule diff --git a/lib/hardware/iob_timer/hardware/src/timer_core.v b/lib/hardware/iob_timer/hardware/src/timer_core.v deleted file mode 100644 index 190acc010..000000000 --- a/lib/hardware/iob_timer/hardware/src/timer_core.v +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`include "iob_timer_csrs_def.vh" - -module timer_core #( - parameter DATA_W = 32 -) ( - input clk_i, - input cke_i, - input arst_i, - input en_i, - input rst_i, - input rstrb_i, - output [`IOB_TIMER_DATA_LOW_W+`IOB_TIMER_DATA_HIGH_W-1:0] time_o -); - - wire [2*DATA_W-1:0] time_counter; - - iob_counter #( - .DATA_W (2 * DATA_W), - .RST_VAL(0) - ) time_counter_cnt ( - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i(arst_i), - .rst_i (rst_i), - .en_i (en_i), - .data_o(time_counter) - ); - - //time counter register - iob_reg_re #( - .DATA_W (2 * DATA_W), - .RST_VAL(0) - ) time_counter_reg ( - .clk_i (clk_i), - .cke_i (cke_i), - .arst_i(arst_i), - .rst_i (rst_i), - .en_i (rstrb_i), - .data_i(time_counter), - .data_o(time_o) - ); - -endmodule diff --git a/lib/hardware/iob_timer/hardware/timer_core.py b/lib/hardware/iob_timer/hardware/timer_core.py deleted file mode 100644 index f7f854b32..000000000 --- a/lib/hardware/iob_timer/hardware/timer_core.py +++ /dev/null @@ -1,42 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock and reset", - }, - { - "name": "reg_interface", - "descr": "", - "signals": [ - {"name": "en", "width": "1", "direction": "input"}, - {"name": "rst", "width": "1", "direction": "input"}, - {"name": "rstrb", "width": "1", "direction": "input"}, - {"name": "time", "width": "64", "direction": "output"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/iob_timer/iob_timer.py b/lib/hardware/iob_timer/iob_timer.py deleted file mode 100755 index 4d10d1f15..000000000 --- a/lib/hardware/iob_timer/iob_timer.py +++ /dev/null @@ -1,213 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "4", # Same as `IOB_TIMER_CSRS_ADDR_W - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "WDATA_W", - "type": "P", - "val": "1", - "min": "NA", - "max": "8", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "cbus_s", - "interface": { - "type": "iob", - "subtype": "slave", - "ADDR_W": "4", # Same as `IOB_TIMER_CSRS_ADDR_W - "DATA_W": "DATA_W", - }, - "descr": "CPU native interface", - }, - ], - "wires": [ - { - "name": "csrs_iob", - "descr": "Internal CSRs IOb interface", - "interface": { - "type": "iob", - "wire_prefix": "csrs_", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - # Register wires - { - "name": "reset", - "descr": "", - "signals": [ - {"name": "reset_wr", "width": 1}, - ], - }, - { - "name": "enable", - "descr": "", - "signals": [ - {"name": "enable_wr", "width": 1}, - ], - }, - { - "name": "sample", - "descr": "", - "signals": [ - {"name": "sample_wr", "width": 1}, - ], - }, - { - "name": "data_low", - "descr": "", - "signals": [ - {"name": "data_low_rd", "width": 32}, - ], - }, - { - "name": "data_high", - "descr": "", - "signals": [ - {"name": "data_high_rd", "width": 32}, - ], - }, - # Internal wires - { - "name": "time_now", - "descr": "", - "signals": [ - {"name": "time_now", "width": 64}, - ], - }, - # Timer core - { - "name": "timer_core_reg_interface", - "descr": "", - "signals": [ - {"name": "enable_wr"}, - {"name": "reset_wr"}, - {"name": "sample_wr"}, - {"name": "time_now"}, - ], - }, - ], - "blocks": [ - { - "core_name": "csrs", - "instance_name": "csrs_inst", - "instance_description": "Control/Status Registers", - "csrs": [ - { - "name": "timer", - "descr": "TIMER software accessible registers.", - "regs": [ - { - "name": "reset", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Timer soft reset", - }, - { - "name": "enable", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Timer enable", - }, - { - "name": "sample", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Sample time counter value into a readable register", - }, - { - "name": "data_low", - "type": "R", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "High part of the timer value, which has twice the width of the data word width", - }, - { - "name": "data_high", - "type": "R", - "n_bits": 32, - "rst_val": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Low part of the timer value, which has twice the width of the data word width", - }, - ], - }, - ], - "csr_if": "iob", - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "cbus_s", - "csrs_iob_o": "csrs_iob", - # Register interfaces - "reset": "reset", - "enable": "enable", - "sample": "sample", - "data_low": "data_low", - "data_high": "data_high", - }, - }, - { - "core_name": "timer_core", - "instance_name": "timer_core_inst", - "instance_description": "Timer core driver", - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "reg_interface": "timer_core_reg_interface", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign data_low_rd = time_now[DATA_W-1:0]; - assign data_high_rd = time_now[2*DATA_W-1:DATA_W]; -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/iob_timer/software/example_firmware.c b/lib/hardware/iob_timer/software/example_firmware.c deleted file mode 100644 index 37d0d7121..000000000 --- a/lib/hardware/iob_timer/software/example_firmware.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "bsp.h" -#include "iob_timer.h" -#include "iob_uart.h" -#include "periphs.h" -#include "printf.h" -#include "system.h" - -int main() { - unsigned long long elapsed; - unsigned int elapsedu; - - // init timer and uart - timer_init(TIMER0_BASE); - uart_init(UART_BASE, FREQ / BAUD); - - printf("\nHello world!\n"); - - // read current timer count, compute elapsed time - elapsed = timer_get_count(); - elapsedu = elapsed / (FREQ / 1000000); - - printf("\nExecution time: %d clock cycles\n", (unsigned int)elapsed); - printf("\nExecution time: %dus @%dMHz\n\n", elapsedu, FREQ / 1000000); - - uart_finish(); -} diff --git a/lib/hardware/iob_timer/software/linux/README.md b/lib/hardware/iob_timer/software/linux/README.md deleted file mode 100644 index 756586ab9..000000000 --- a/lib/hardware/iob_timer/software/linux/README.md +++ /dev/null @@ -1,24 +0,0 @@ - - -# IOb Timer Linux Kernel Drivers -- Structure: - - `drivers/`: directory with linux kernel module drivers for iob_timer - - `iob_timer_main.c`: driver source - - `[iob_timer.h]` and `[iob_timer_sysfs.h]`: header files generated by: - ```bash - python3 .path/to/iob-linux/scripts/drivers.py iob_timer -o [output_dir] - ``` - - `driver.mk`: makefile segment with `iob_timer-obj:` target for driver - compilation - - `user/`: directory with user application example that uses iob_timer - drivers - - `iob_timer_user.c`: example user application that uses iob_timer - drivers - - `Makefile`: user application compilation targets - - `iob_timer.dts`: device tree template with iob_timer node - - manually add the `timer` node to the system device tree so the - iob_timer is recognized by the linux kernel diff --git a/lib/hardware/iob_timer/software/linux/drivers/driver.mk b/lib/hardware/iob_timer/software/linux/drivers/driver.mk deleted file mode 100644 index f4419cf85..000000000 --- a/lib/hardware/iob_timer/software/linux/drivers/driver.mk +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -iob_timer-objs := iob_timer_main.o iob_class/iob_class_utils.o diff --git a/lib/hardware/iob_timer/software/linux/drivers/iob_timer_main.c b/lib/hardware/iob_timer/software/linux/drivers/iob_timer_main.c deleted file mode 100644 index 2071fb0d2..000000000 --- a/lib/hardware/iob_timer/software/linux/drivers/iob_timer_main.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* iob_timer_main.c: driver for iob_timer - * using device platform. No hardcoded hardware address: - * 1. load driver: insmod iob_timer.ko - * 2. run user app: ./user/user - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iob_class/iob_class_utils.h" -#include "iob_timer.h" - -static int iob_timer_probe(struct platform_device *); -static int iob_timer_remove(struct platform_device *); - -static ssize_t iob_timer_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t iob_timer_write(struct file *, const char __user *, size_t, - loff_t *); -static loff_t iob_timer_llseek(struct file *, loff_t, int); -static int iob_timer_open(struct inode *, struct file *); -static int iob_timer_release(struct inode *, struct file *); - -static struct iob_data iob_timer_data = {0}; -DEFINE_MUTEX(iob_timer_mutex); - -#include "iob_timer_sysfs.h" - -static const struct file_operations iob_timer_fops = { - .owner = THIS_MODULE, - .write = iob_timer_write, - .read = iob_timer_read, - .llseek = iob_timer_llseek, - .open = iob_timer_open, - .release = iob_timer_release, -}; - -static const struct of_device_id of_iob_timer_match[] = { - {.compatible = "iobundle,timer0"}, - {}, -}; - -static struct platform_driver iob_timer_driver = { - .driver = - { - .name = "iob_timer", - .owner = THIS_MODULE, - .of_match_table = of_iob_timer_match, - }, - .probe = iob_timer_probe, - .remove = iob_timer_remove, -}; - -// -// Module init and exit functions -// -static int iob_timer_probe(struct platform_device *pdev) { - struct resource *res; - int result = 0; - - if (iob_timer_data.device != NULL) { - pr_err("[Driver] %s: No more devices allowed!\n", IOB_TIMER_DRIVER_NAME); - - return -ENODEV; - } - - pr_info("[Driver] %s: probing.\n", IOB_TIMER_DRIVER_NAME); - - // Get the I/O region base address - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - pr_err("[Driver]: Failed to get I/O resource!\n"); - result = -ENODEV; - goto r_get_resource; - } - - // Request and map the I/O region - iob_timer_data.regbase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(iob_timer_data.regbase)) { - result = PTR_ERR(iob_timer_data.regbase); - goto r_ioremmap; - } - iob_timer_data.regsize = resource_size(res); - - // Alocate char device - result = - alloc_chrdev_region(&iob_timer_data.devnum, 0, 1, IOB_TIMER_DRIVER_NAME); - if (result) { - pr_err("%s: Failed to allocate device number!\n", IOB_TIMER_DRIVER_NAME); - goto r_alloc_region; - } - - cdev_init(&iob_timer_data.cdev, &iob_timer_fops); - - result = cdev_add(&iob_timer_data.cdev, iob_timer_data.devnum, 1); - if (result) { - pr_err("%s: Char device registration failed!\n", IOB_TIMER_DRIVER_NAME); - goto r_cdev_add; - } - - // Create device class // todo: make a dummy driver just to create and own the - // class: https://stackoverflow.com/a/16365027/8228163 - if ((iob_timer_data.class = - class_create(THIS_MODULE, IOB_TIMER_DRIVER_CLASS)) == NULL) { - printk("Device class can not be created!\n"); - goto r_class; - } - - // Create device file - iob_timer_data.device = - device_create(iob_timer_data.class, NULL, iob_timer_data.devnum, NULL, - IOB_TIMER_DRIVER_NAME); - if (iob_timer_data.device == NULL) { - printk("Can not create device file!\n"); - goto r_device; - } - - result = iob_timer_create_device_attr_files(iob_timer_data.device); - if (result) { - pr_err("Cannot create device attribute file......\n"); - goto r_dev_file; - } - - dev_info(&pdev->dev, "initialized.\n"); - goto r_ok; - -r_dev_file: - iob_timer_remove_device_attr_files(&iob_timer_data); -r_device: - class_destroy(iob_timer_data.class); -r_class: - cdev_del(&iob_timer_data.cdev); -r_cdev_add: - unregister_chrdev_region(iob_timer_data.devnum, 1); -r_alloc_region: - // iounmap is managed by devm -r_ioremmap: -r_get_resource: -r_ok: - - return result; -} - -static int iob_timer_remove(struct platform_device *pdev) { - iob_timer_remove_device_attr_files(&iob_timer_data); - class_destroy(iob_timer_data.class); - cdev_del(&iob_timer_data.cdev); - unregister_chrdev_region(iob_timer_data.devnum, 1); - // Note: no need for iounmap, since we are using devm_ioremap_resource() - - dev_info(&pdev->dev, "exiting.\n"); - - return 0; -} - -static int __init test_counter_init(void) { - pr_info("[Driver] %s: initializing.\n", IOB_TIMER_DRIVER_NAME); - - return platform_driver_register(&iob_timer_driver); -} - -static void __exit test_counter_exit(void) { - pr_info("[Driver] %s: exiting.\n", IOB_TIMER_DRIVER_NAME); - platform_driver_unregister(&iob_timer_driver); -} - -// -// File operations -// - -static int iob_timer_open(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_timer device opened\n"); - - if (!mutex_trylock(&iob_timer_mutex)) { - pr_info("Another process is accessing the device\n"); - - return -EBUSY; - } - - return 0; -} - -static int iob_timer_release(struct inode *inode, struct file *file) { - pr_info("[Driver] iob_timer device closed\n"); - - mutex_unlock(&iob_timer_mutex); - - return 0; -} - -static ssize_t iob_timer_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) { - int size = 0; - u32 value = 0; - - /* read value from register */ - switch (*ppos) { - case IOB_TIMER_DATA_LOW_ADDR: - value = iob_data_read_reg(iob_timer_data.regbase, IOB_TIMER_DATA_LOW_ADDR, - IOB_TIMER_DATA_LOW_W); - size = (IOB_TIMER_DATA_LOW_W >> 3); // bit to bytes - pr_info("[Driver] Read data low!\n"); - break; - case IOB_TIMER_DATA_HIGH_ADDR: - value = iob_data_read_reg(iob_timer_data.regbase, IOB_TIMER_DATA_HIGH_ADDR, - IOB_TIMER_DATA_HIGH_W); - size = (IOB_TIMER_DATA_HIGH_W >> 3); // bit to bytes - pr_info("[Driver] Read data high!\n"); - break; - case IOB_TIMER_VERSION_ADDR: - value = iob_data_read_reg(iob_timer_data.regbase, IOB_TIMER_VERSION_ADDR, - IOB_TIMER_VERSION_W); - size = (IOB_TIMER_VERSION_W >> 3); // bit to bytes - pr_info("[Driver] Read version!\n"); - break; - default: - // invalid address - no bytes read - return 0; - } - - // Read min between count and REG_SIZE - if (size > count) - size = count; - - if (copy_to_user(buf, &value, size)) - return -EFAULT; - - return count; -} - -static ssize_t iob_timer_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) { - int size = 0; - u32 value = 0; - - switch (*ppos) { - case IOB_TIMER_RESET_ADDR: - size = (IOB_TIMER_RESET_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_timer_data.regbase, value, IOB_TIMER_RESET_ADDR, - IOB_TIMER_RESET_W); - pr_info("[Driver] Reset iob_timer: 0x%x\n", value); - break; - case IOB_TIMER_ENABLE_ADDR: - size = (IOB_TIMER_ENABLE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_timer_data.regbase, value, IOB_TIMER_ENABLE_ADDR, - IOB_TIMER_ENABLE_W); - pr_info("[Driver] Enable iob_timer: 0x%x\n", value); - break; - case IOB_TIMER_SAMPLE_ADDR: // sample counter - size = (IOB_TIMER_SAMPLE_W >> 3); // bit to bytes - if (read_user_data(buf, size, &value)) - return -EFAULT; - iob_data_write_reg(iob_timer_data.regbase, value, IOB_TIMER_SAMPLE_ADDR, - IOB_TIMER_SAMPLE_W); - pr_info("[Driver] Sample iob_timer: 0x%x\n", value); - break; - default: - pr_info("[Driver] Invalid write address 0x%x\n", (unsigned int)*ppos); - // invalid address - no bytes written - return 0; - } - - return count; -} - -/* Custom lseek function - * check: lseek(2) man page for whence modes - */ -static loff_t iob_timer_llseek(struct file *filp, loff_t offset, int whence) { - loff_t new_pos = -1; - - switch (whence) { - case SEEK_SET: - new_pos = offset; - break; - case SEEK_CUR: - new_pos = filp->f_pos + offset; - break; - case SEEK_END: - new_pos = (1 << IOB_TIMER_CSRS_ADDR_W) + offset; - break; - default: - return -EINVAL; - } - - // Check for valid bounds - if (new_pos < 0 || new_pos > iob_timer_data.regsize) { - return -EINVAL; - } - - // Update file position - filp->f_pos = new_pos; - - return new_pos; -} - -module_init(test_counter_init); -module_exit(test_counter_exit); - -MODULE_LICENSE("Dual MIT/GPL"); -MODULE_AUTHOR("IObundle"); -MODULE_DESCRIPTION("IOb-Timer Drivers"); -MODULE_VERSION("0.10"); diff --git a/lib/hardware/iob_timer/software/linux/iob_timer.dts b/lib/hardware/iob_timer/software/linux/iob_timer.dts deleted file mode 100644 index b43c8c4e6..000000000 --- a/lib/hardware/iob_timer/software/linux/iob_timer.dts +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* Copyright (c) 2024 IObundle */ - -/dts-v1/; - -/ { - #address-cells = <1>; - #size-cells = <1>; - model = "IOb-SoC, VexRiscv"; - compatible = "IOb-SoC, VexRiscv"; - // CPU - // Memory - // Choosen - soc { - #address-cells = <1>; - #size-cells = <1>; - compatible = "iobundle,iob-soc", "simple-bus"; - ranges; - - // Other SOC peripherals go here - - // Add this Node to device tree - TIMER0: timer@/*TIMER0_ADDR_MACRO*/ { - compatible = "iobundle,timer0"; - reg = <0x/*TIMER0_ADDR_MACRO*/ 0x100>; - }; - - }; -}; diff --git a/lib/hardware/iob_timer/software/linux/user/Makefile b/lib/hardware/iob_timer/software/linux/user/Makefile deleted file mode 100644 index 3e9654961..000000000 --- a/lib/hardware/iob_timer/software/linux/user/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -SRC = $(wildcard *.c) -HDR += iob_timer.h -FLAGS = -Wall -Werror -O2 -FLAGS += -static -FLAGS += -march=rv32imac -FLAGS += -mabi=ilp32 -BIN = iob_timer_user -CC = riscv64-unknown-linux-gnu-gcc - -all: $(BIN) - -$(BIN): $(SRC) $(HDR) - $(CC) $(FLAGS) $(INCLUDE) -o $(BIN) $(SRC) - -LIB_DIR = ../../../../../../ -IOB_LINUX_DIR ?= ../../../lib/iob-linux -$(HDR): - cd lib && \ - .$(IOB_LINUX_DIR)/scripts/drivers.py iob_timer -o `realpath $(CURDIR)` - -clean: - rm -rf $(BIN) $(HDR) diff --git a/lib/hardware/iob_timer/software/linux/user/iob_timer_user.c b/lib/hardware/iob_timer/software/linux/user/iob_timer_user.c deleted file mode 100644 index a136347fa..000000000 --- a/lib/hardware/iob_timer/software/linux/user/iob_timer_user.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include -#include -#include - -#include "iob_timer.h" - -int sysfs_read_file(const char *filename, uint32_t *read_value) { - // Open file for read - FILE *file = fopen(filename, "r"); - if (file == NULL) { - perror("[User] Failed to open the file"); - return -1; - } - - // Read uint32_t value from file in ASCII - ssize_t ret = fscanf(file, "%u", read_value); - if (ret == -1) { - perror("[User] Failed to read from file"); - fclose(file); - return -1; - } - - fclose(file); - - return ret; -} - -int sysfs_write_file(const char *filename, uint32_t write_value) { - // Open file for write - FILE *file = fopen(filename, "w"); - if (file == NULL) { - perror("[User] Failed to open the file"); - return -1; - } - - // Write uint32_t value to file in ASCII - ssize_t ret = fprintf(file, "%u", write_value); - if (ret == -1) { - perror("[User] Failed to write to file"); - fclose(file); - return -1; - } - - fclose(file); - - return ret; -} - -int timer_reset() { - if (sysfs_write_file(IOB_TIMER_SYSFILE_RESET, 1) == -1) { - return -1; - } - if (sysfs_write_file(IOB_TIMER_SYSFILE_RESET, 0) == -1) { - return -1; - } - - return 0; -} - -int timer_init() { - if (timer_reset()) { - return -1; - } - - if (sysfs_write_file(IOB_TIMER_SYSFILE_ENABLE, 1) == -1) { - return -1; - } - - return 0; -} - -int timer_print_version() { - uint32_t ret = -1; - uint32_t version = 0; - - ret = sysfs_read_file(IOB_TIMER_SYSFILE_VERSION, &version); - if (ret == -1) { - return ret; - } - - printf("[User] Version: 0x%x\n", version); - return 0; -} - -int timer_get_count(uint64_t *count) { - uint32_t ret = -1; - uint32_t data = 0; - - // Sample timer counter - if (sysfs_write_file(IOB_TIMER_SYSFILE_SAMPLE, 1) == -1) { - return -1; - } - if (sysfs_write_file(IOB_TIMER_SYSFILE_SAMPLE, 0) == -1) { - return -1; - } - - // Read sampled timer counter - ret = sysfs_read_file(IOB_TIMER_SYSFILE_DATA_HIGH, &data); - if (ret == -1) { - return -1; - } - *count = ((uint64_t)data) << IOB_TIMER_DATA_LOW_W; - ret = sysfs_read_file(IOB_TIMER_SYSFILE_DATA_LOW, &data); - if (ret == -1) { - return -1; - } - (*count) = (*count) | (uint64_t)data; - - return 0; -} - -int main(int argc, char *argv[]) { - printf("[User] IOb-Timer application\n"); - - if (timer_init()) { - perror("[User] Failed to initialize timer"); - - return EXIT_FAILURE; - } - - if (timer_print_version()) { - perror("[User] Failed to print version"); - - return EXIT_FAILURE; - } - - // read current timer count - uint64_t elapsed = 0; - if (timer_get_count(&elapsed)) { - perror("[User] Failed to get count"); - } - printf("\nExecution time: %lu clock cycles\n", elapsed); - - return EXIT_SUCCESS; -} diff --git a/lib/hardware/iob_timer/software/src/iob_timer.c b/lib/hardware/iob_timer/software/src/iob_timer.c deleted file mode 100644 index c73c8d73b..000000000 --- a/lib/hardware/iob_timer/software/src/iob_timer.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob_timer.h" - -// Base Address -static uint32_t base; - -void timer_reset() { - IOB_TIMER_SET_RESET(1); - IOB_TIMER_SET_RESET(0); -} - -void timer_init(uint32_t base_address) { - base = base_address; - IOB_TIMER_INIT_BASEADDR(base_address); - timer_reset(); - IOB_TIMER_SET_ENABLE(1); -} - -uint64_t timer_get_count() { - // sample timer counter - IOB_TIMER_SET_SAMPLE(1); - IOB_TIMER_SET_SAMPLE(0); - - uint64_t count = (uint64_t)IOB_TIMER_GET_DATA_HIGH(); - count <<= IOB_TIMER_DATA_LOW_W; - count |= (uint64_t)IOB_TIMER_GET_DATA_LOW(); - - return count; -} diff --git a/lib/hardware/iob_timer/software/src/iob_timer.h b/lib/hardware/iob_timer/software/src/iob_timer.h deleted file mode 100644 index 8d01cbc1a..000000000 --- a/lib/hardware/iob_timer/software/src/iob_timer.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#pragma once -#include "iob_timer_csrs.h" - -// Functions -void timer_reset(); -void timer_init(uint32_t base_address); -uint64_t timer_get_count(); diff --git a/lib/hardware/iob_timer/software/src/iob_timer_csrs_pc_emul.c b/lib/hardware/iob_timer/software/src/iob_timer_csrs_pc_emul.c deleted file mode 100644 index 42cb2eefd..000000000 --- a/lib/hardware/iob_timer/software/src/iob_timer_csrs_pc_emul.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* PC Emulation of TIMER peripheral */ - -#include -#include - -#include "bsp.h" -#include "iob_timer_csrs.h" - -/* convert clock values from PC CLOCK FREQ to EMBEDDED FREQ */ -#define PC_TO_FREQ_FACTOR ((1.0 * FREQ) / CLOCKS_PER_SEC) - -static clock_t start, end, time_counter, counter_reg; -static int timer_enable; - -static int base; -void IOB_TIMER_INIT_BASEADDR(uint32_t addr) { - base = addr; - return; -} - -void IOB_TIMER_SET_RESET(uint8_t value) { - // use only reg width - int rst_int = (value & 0x01); - if (rst_int) { - start = end = 0; - time_counter = 0; - timer_enable = 0; - } - return; -} - -void IOB_TIMER_SET_ENABLE(uint8_t value) { - // use only reg width - int en_int = (value & 0x01); - // manage transitions - // 0 -> 1 - if (timer_enable == 0 && en_int == 1) { - // start counting time - start = clock(); - } else if (timer_enable == 1 && en_int == 0) { - // accumulate enable interval - end = clock(); - timer_enable += (end - start); - start = end = 0; // reset aux clock values - } - // store enable en_int - timer_enable = en_int; - return; -} - -void IOB_TIMER_SET_SAMPLE(uint8_t value) { - // use only reg width - int sample_int = (value & 0x01); - if (sample_int) { - counter_reg = time_counter; - if (start != 0) - counter_reg += (clock() - start); - } - return; -} - -uint32_t IOB_TIMER_GET_DATA_HIGH() { - /* convert clock from PC CLOCKS_PER_CYCLE to FREQ */ - double counter_freq = (1.0 * counter_reg) * PC_TO_FREQ_FACTOR; - return ((int)(((unsigned long long)counter_freq) >> 32)); -} - -uint32_t IOB_TIMER_GET_DATA_LOW() { - /* convert clock from PC CLOCKS_PER_CYCLE to FREQ */ - double counter_freq = (1.0 * counter_reg) * PC_TO_FREQ_FACTOR; - return ((int)(((unsigned long long)counter_freq) & 0xFFFFFFFF)); -} diff --git a/lib/hardware/iob_uart/LICENSE b/lib/hardware/iob_uart/LICENSE deleted file mode 100644 index 10bfda633..000000000 --- a/lib/hardware/iob_uart/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Jose T. de Sousa - -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/lib/hardware/iob_uart/Makefile b/lib/hardware/iob_uart/Makefile deleted file mode 100644 index 304ae251e..000000000 --- a/lib/hardware/iob_uart/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -CORE := iob_uart - -LIB_DIR=../../../../lib -DISABLE_LINT:=1 -export DISABLE_LINT -export LIB_DIR -BUILD_DIR := build - -include $(LIB_DIR)/setup.mk - -all: test - -setup: - nix-shell --run 'py2hwsw $(CORE)' - -test: clean setup - nix-shell --run "make -C ../iob_uart_* sim-run" - -.PHONY: all setup test - - diff --git a/lib/hardware/iob_uart/README.md b/lib/hardware/iob_uart/README.md deleted file mode 100644 index 6808b0d1c..000000000 --- a/lib/hardware/iob_uart/README.md +++ /dev/null @@ -1,107 +0,0 @@ - - -# README # - -# iob-uart - -## What is this repository for? ## - -The IObundle UART is a RISC-V-based Peripheral written in Verilog, which users -can download for free, modify, simulate and implement in FPGA or ASIC. It is -written in Verilog and includes a C software driver. The IObundle UART is a -very compact IP that works at high clock rates if needed. It supports -full-duplex operation and a configurable baud rate. The IObundle UART has a -fixed configuration for the Start and Stop bits. More flexible licensable -commercial versions are available upon request. - -## Simulate - -Install the latest stable version of the open-source Verilog simulator Icarus Verilog. - -To simulate type: -``` -make setup -make -C iob_uart_V0.70/ sim-run -``` - -To clean the simulation generated files: -``` -make -C iob_uart_V0.70/ sim-clean -``` - -## Testing - -### Simulation test - -To run a series of simulation tests, type: - -``` -make setup -make -C iob_uart_V0.70/ sim-test -``` - -The above command produces a test log file called `test.log` in the `iob_uart_V0.70/hardware/simulation/` -directory. The `test.log` file is compared with the `Test passed!` string; if they differ, the test fails; otherwise, it -passes. - -To clean the files produced when testing all simulators, type: - -``` -make -C iob_uart_V0.70/ sim-clean -``` - -## Integrate in SoC ## - -* Check out [IOb-SoC](https://github.com/IObundle/iob-soc) - -## Usage - -This peripheral has a set of driver functions, declared in the `software/src/iob-uart.h` file. -These functions allow the primary system to control this peripheral to receive and send UART messages. -The `iob_soc_firmware.c` file of the [IOb-SoC](https://github.com/IObundle/iob-soc) provides an example on how to use these functions. - -To instantiate the peripheral, add a dictionary describing the peripheral in the `peripherals` list of the `blocks` dictionary in the setup Python module of the system. - -The `iob_soc_setup.py` script of the [IOb-SoC](https://github.com/IObundle/iob-soc) system, uses the following dictionary to instantiate a UART peripheral with the instance name `UART0`: -```Python -blocks = \ -[ - # Other blocks here... - - {'name':'peripherals', 'descr':'peripheral modules', 'blocks': [ - {'name':'UART0', 'type':'UART', 'descr':'Default UART interface', 'params':{}}, - - # Other peripheral instances here... - ]}, -] -``` - - -## Generate the documentation ## - -To generate the documentation for the IP core type: -``` -make setup -make -C iob_uart_V0.70/ doc-build [DOC=] -``` - -Currently you can generate two document types: the Product Brief (DOC=pb) -or the User Guide (DOC=ug). To create a new document type get inspired by -the Makefiles in the document type directories document/pb and document/ug. Data -from the FPGA compile tools is imported automatically into the docs. - -To clean the documentation generated files type: -``` -make -C iob_uart_V0.70/ doc-clean [DOC=] -``` - - -## Clean all generated files ## -To clean all generated files, the command is simply -``` -make clean -``` diff --git a/lib/hardware/iob_uart/config_setup.mk b/lib/hardware/iob_uart/config_setup.mk deleted file mode 100644 index 99aa95259..000000000 --- a/lib/hardware/iob_uart/config_setup.mk +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# (c) 2022-Present IObundle, Lda, all rights reserved -# -# This make segment is used at setup-time by ./Makefile -# and at build-time by iob_cache_/Makefile -# - -# core name -NAME=iob_uart - -# core version -VERSION=0010 - -# root directory -UART_DIR ?= . - -# default configuration -CONFIG ?= base diff --git a/lib/hardware/iob_uart/default.nix b/lib/hardware/iob_uart/default.nix deleted file mode 120000 index 626239e8a..000000000 --- a/lib/hardware/iob_uart/default.nix +++ /dev/null @@ -1 +0,0 @@ -../../../../lib/default.nix \ No newline at end of file diff --git a/lib/hardware/iob_uart/document/figures/bd.odg b/lib/hardware/iob_uart/document/figures/bd.odg deleted file mode 100644 index 654a05efa10fb4e41b42e8348b3fa578043b06c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19297 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%0AMO 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 DJaFZm diff --git a/lib/hardware/iob_uart/document/figures/symb.odg.license b/lib/hardware/iob_uart/document/figures/symb.odg.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_uart/document/figures/symb.odg.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_uart/document/figures/tbbd.odg b/lib/hardware/iob_uart/document/figures/tbbd.odg deleted file mode 100644 index da44625f453b1b2a02a387007ca9b1fbde3c6d30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13969 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 SVgLZ?*N5i!3eEic?Ee7m$Pz68 diff --git a/lib/hardware/iob_uart/document/figures/tbbd.odg.license b/lib/hardware/iob_uart/document/figures/tbbd.odg.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_uart/document/figures/tbbd.odg.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_uart/document/pb/Makefile b/lib/hardware/iob_uart/document/pb/Makefile deleted file mode 100644 index f90e17a45..000000000 --- a/lib/hardware/iob_uart/document/pb/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -UART_DIR:=../.. - -NOCLEAN=-name "pb.pdf" - -figures: - mkdir -p ./figures - cp -r -u lib/document/figures/* ../figures/bd.odg ./figures - -include $(UART_DIR)/document/document.mk diff --git a/lib/hardware/iob_uart/document/pb/pb.pdf b/lib/hardware/iob_uart/document/pb/pb.pdf deleted file mode 100644 index e1776c505053d2be6b5132f3f5846a96c2acd744..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61639 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 z 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%0AQ{O3cpOZ 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 E0X$m;H~;_u diff --git a/lib/hardware/iob_uart/document/pb/pb.pdf.license b/lib/hardware/iob_uart/document/pb/pb.pdf.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_uart/document/pb/pb.pdf.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_uart/document/pb/test.expected b/lib/hardware/iob_uart/document/pb/test.expected deleted file mode 100644 index ef88ce91b..000000000 --- a/lib/hardware/iob_uart/document/pb/test.expected +++ /dev/null @@ -1,24 +0,0 @@ -\relax -\providecommand\hyper@newdestlabel[2]{} -\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument} -\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined -\global\let\oldcontentsline\contentsline -\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}} -\global\let\oldnewlabel\newlabel -\gdef\newlabel#1#2{\newlabelxx{#1}#2} -\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}} -\AtEndDocument{\ifx\hyper@anchor\@undefined -\let\contentsline\oldcontentsline -\let\newlabel\oldnewlabel -\fi} -\fi} -\global\let\hyper@last\relax -\gdef\HyperFirstAtBeginDocument#1{#1} -\providecommand\HyField@AuxAddToFields[1]{} -\providecommand\HyField@AuxAddToCoFields[2]{} -\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces High-Level Block Diagram.}}{1}{figure.1}\protected@file@percent } -\newlabel{fig:bd}{{1}{1}{High-Level Block Diagram}{figure.1}{}} -\@writefile{lot}{\contentsline {table}{\numberline {1}{\ignorespaces AMD Kintex Ultrascale FPGAs.}}{1}{table.1}\protected@file@percent } -\newlabel{tab:fpga_amd}{{1}{1}{AMD Kintex Ultrascale FPGAs}{table.1}{}} -\@writefile{lot}{\contentsline {table}{\numberline {2}{\ignorespaces Intel Cyclone V GT FPGAs.}}{1}{table.2}\protected@file@percent } -\newlabel{tab:fpga_intel}{{2}{1}{Intel Cyclone V GT FPGAs}{table.2}{}} diff --git a/lib/hardware/iob_uart/document/pb/test.expected.license b/lib/hardware/iob_uart/document/pb/test.expected.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_uart/document/pb/test.expected.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_uart/document/tsrc/bd_desc.tex b/lib/hardware/iob_uart/document/tsrc/bd_desc.tex deleted file mode 100644 index ec0300cc7..000000000 --- a/lib/hardware/iob_uart/document/tsrc/bd_desc.tex +++ /dev/null @@ -1,5 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\item Place block diagram description here diff --git a/lib/hardware/iob_uart/document/tsrc/benefits.tex b/lib/hardware/iob_uart/document/tsrc/benefits.tex deleted file mode 100644 index 1430f4cc8..000000000 --- a/lib/hardware/iob_uart/document/tsrc/benefits.tex +++ /dev/null @@ -1,10 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\begin{itemize} - \itemsep-0.5em -\item Compact and easy to integrate hardware and software implementation -\item Can fit many instances in low cost FPGAs and ASICs -\item Low power consumption -\end{itemize} diff --git a/lib/hardware/iob_uart/document/tsrc/deliverables.tex b/lib/hardware/iob_uart/document/tsrc/deliverables.tex deleted file mode 100644 index 5024f4843..000000000 --- a/lib/hardware/iob_uart/document/tsrc/deliverables.tex +++ /dev/null @@ -1,13 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\begin{itemize} - \itemsep-0.5em -\item ASIC or FPGA synthesized netlist or Verilog source code, and respective - synthesis and implementation scripts -\item ASIC or FPGA verification environment by simulation and emulation -\item Bare-metal software driver and example user software -\item User documentation for easy system integration -\item Example integration in IOb-SoC (optional) -\end{itemize} diff --git a/lib/hardware/iob_uart/document/tsrc/features.tex b/lib/hardware/iob_uart/document/tsrc/features.tex deleted file mode 100644 index ed4818834..000000000 --- a/lib/hardware/iob_uart/document/tsrc/features.tex +++ /dev/null @@ -1,22 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\begin{itemize} -\item Supported in IObundle's RISC-V IOb-SoC open-source and free of charge template. -\item IObundle's IOb-SoC native CPU interface. -\item Verilog basic UART implementation. -\item Soft reset and enable functions. -\item Runtime configurable baud rate -\item C software driver at the bare-metal level. -\item Simple Verilog testbench for the IP's {\em nucleus}. -\item System-level Verilog testbench available when simulating the IP embedded in IOb-SoC. -\item Simulation Makefile for the open-source and free of charge Icarus Verilog simulator. -\item FPGA synthesis and implementation scripts for two FPGA families from two FPGA vendors. -\item Automated creation of FPGA netlists -\item Automated production of documentation using the open-source and free Latex framework. -\item IP data automatically extracted from FPGA tool logs to include in documents. -\item Makefile tree for full automation of simulation, FPGA implementation and document production. -\item AXI4 Lite CPU interface (premium option). -\item Parity bits (premium option). -\end{itemize} diff --git a/lib/hardware/iob_uart/document/tsrc/intro.tex b/lib/hardware/iob_uart/document/tsrc/intro.tex deleted file mode 100644 index 2b55c0df1..000000000 --- a/lib/hardware/iob_uart/document/tsrc/intro.tex +++ /dev/null @@ -1,13 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -The IObundle UART is a RISC-V-based Peripheral written in Verilog, which users -can download for free, modify, simulate and implement in FPGA or ASIC. It is -written in Verilog and includes a C software driver. The IObundle UART is a very -compact IP that works at high clock rates if needed. It supports full-duplex -operation and a configurable baud rate. The IObundle UART has a fixed -configuration for the Start and Stop bits. More flexible licensable commercial -versions are available upon request. - - diff --git a/lib/hardware/iob_uart/document/tsrc/results.tex b/lib/hardware/iob_uart/document/tsrc/results.tex deleted file mode 100644 index de0a759fc..000000000 --- a/lib/hardware/iob_uart/document/tsrc/results.tex +++ /dev/null @@ -1,5 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -The results obtained use all the default user configurable parameters, which affect the RAM usage results. diff --git a/lib/hardware/iob_uart/document/tsrc/title.tex b/lib/hardware/iob_uart/document/tsrc/title.tex deleted file mode 100644 index 002148e44..000000000 --- a/lib/hardware/iob_uart/document/tsrc/title.tex +++ /dev/null @@ -1,6 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\title{IOb-UART, a RISC-V UART} -\header{IOb-UART, a RISC-V UART} diff --git a/lib/hardware/iob_uart/document/tsrc/ug_title.tex b/lib/hardware/iob_uart/document/tsrc/ug_title.tex deleted file mode 100644 index da566d98a..000000000 --- a/lib/hardware/iob_uart/document/tsrc/ug_title.tex +++ /dev/null @@ -1,13 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -%replace ipcore-name by the name of your ip core (e.g. IOb-Cache) and description by a brief description (e.g. a Configurable Cache) - -\title{% -\Huge IOB-UART \\ - \vspace*{3cm} -\Large A RISC-V UART -} - -\header{IOB-UART, a RISC-V UART} diff --git a/lib/hardware/iob_uart/document/ug/Makefile b/lib/hardware/iob_uart/document/ug/Makefile deleted file mode 100644 index 7b04ddfa6..000000000 --- a/lib/hardware/iob_uart/document/ug/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -UART_DIR:=../.. -CORE_DIR:=$(UART_DIR) - -NOCLEAN=-name "ug.pdf" -o -name "if.tex" -o -name "td.tex" \ - -o -name "csrs.tex" -o -name "swop.tex" -o -name "inst.tex" \ - -o -name "sim.tex" -o -name "synth.tex" -o -name "custom.tex" \ - -o -name "revhist.tex" - -figures: - mkdir -p ./figures - cp -r -u lib/document/figures/* ../figures/* ./figures - -include $(UART_DIR)/document/document.mk diff --git a/lib/hardware/iob_uart/document/ug/csrs.tex b/lib/hardware/iob_uart/document/ug/csrs.tex deleted file mode 100644 index 7b0d2f4b0..000000000 --- a/lib/hardware/iob_uart/document/ug/csrs.tex +++ /dev/null @@ -1,19 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\begin{table}[H] - \centering - \begin{tabular}{|l|c|c|r|P{0.8cm}|p{5.5cm}|} - \hline - \rowcolor{iob-green} - - {\bf Name} & {\bf R/W} & {\bf Addr} & {\bf Bits} & {\bf Initial Value} & {\bf Description} \\ \hline - - \input{uart_csrs_tab} - - \end{tabular} - \caption{UART software accessible registers.} - \label{tab:sw_tx} -\end{table} - diff --git a/lib/hardware/iob_uart/document/ug/if.tex b/lib/hardware/iob_uart/document/ug/if.tex deleted file mode 100644 index 2c877a54c..000000000 --- a/lib/hardware/iob_uart/document/ug/if.tex +++ /dev/null @@ -1,51 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\begin{table}[H] - \centering - \begin{tabular}{|l|l|r|p{10.5cm}|} - - \hline - \rowcolor{iob-green} - {\bf Name} & {\bf Direction} & {\bf Width} & {\bf Description} \\ \hline \hline - - \input{gen_if_tab} - - \end{tabular} - \caption{General Interface Signals.} - \label{gen_if_tab:is} -\end{table} - -\begin{table}[H] - \centering - \begin{tabular}{|l|l|r|p{8.5cm}|} - - \hline - \rowcolor{iob-green} - {\bf Name} & {\bf Direction} & {\bf Width} & {\bf Description} \\ \hline \hline - - \input{iob_s_if_tab} - - \end{tabular} - \caption{IObundle Interface Signals} - \label{tab:if_iob_s} -\end{table} - -\begin{table}[H] - \centering - \begin{tabular}{|l|l|r|p{9.5cm}|} - - \hline - \rowcolor{iob-green} - {\bf Name} & {\bf Direction} & {\bf Width} & {\bf Description} \\ \hline \hline - - \input{rs232_if_tab} - - \end{tabular} - \caption{RS232 Interface Signals} - \label{tab:if_rs232} -\end{table} - -%TODO -%\input{\TEX/ug/axil_s_if} diff --git a/lib/hardware/iob_uart/document/ug/inst.tex b/lib/hardware/iob_uart/document/ug/inst.tex deleted file mode 100644 index aeec9c19c..000000000 --- a/lib/hardware/iob_uart/document/ug/inst.tex +++ /dev/null @@ -1,14 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -The IOb-UART is a fully synchronous, single clock ({\tt clk}) domain -design with an asynchronous active-high reset signal ({\tt arst}) that drives -all the flip-flops in the design. The reset signal should be de-asserted -synchronously with the clock signal's rising edge. - -The IOb-UART works attached to a CPU core, using the IOb Native Bus -interface. It outputs (pin {\tt txd}) and receives (pin {\tt rxd}) an -RS232-encoded serial data stream. The RS232 protocol also specifies and -handshking signal pair consisting of the signals {\tt cts} and {\tt rts}. - diff --git a/lib/hardware/iob_uart/document/ug/revhist.tex b/lib/hardware/iob_uart/document/ug/revhist.tex deleted file mode 100644 index 956738a4a..000000000 --- a/lib/hardware/iob_uart/document/ug/revhist.tex +++ /dev/null @@ -1,5 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -\today & Initial document version \input{version}. \\ \hline diff --git a/lib/hardware/iob_uart/document/ug/sim.tex b/lib/hardware/iob_uart/document/ug/sim.tex deleted file mode 100644 index 46c9ac6df..000000000 --- a/lib/hardware/iob_uart/document/ug/sim.tex +++ /dev/null @@ -1,8 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -The provided testbench implements a self-loop, where the IOb-UART handshakes -({\tt cts} to {\tt rts}, and sends data to itself ({\tt txd} to {\tt rxd}). The -testbench drives the clock and reset signals, and emulates the CPU actions with -a simple control block. diff --git a/lib/hardware/iob_uart/document/ug/synth.tex b/lib/hardware/iob_uart/document/ug/synth.tex deleted file mode 100644 index 26ca465f3..000000000 --- a/lib/hardware/iob_uart/document/ug/synth.tex +++ /dev/null @@ -1,6 +0,0 @@ -% SPDX-FileCopyrightText: 2024 IObundle -% -% SPDX-License-Identifier: MIT - -This IP core synthesizes for FPGA and ASIC with very low logic resources -consumption. The implementation does not require memory or arithmetic resources. diff --git a/lib/hardware/iob_uart/document/ug/test.expected b/lib/hardware/iob_uart/document/ug/test.expected deleted file mode 100644 index 69bde3742..000000000 --- a/lib/hardware/iob_uart/document/ug/test.expected +++ /dev/null @@ -1,71 +0,0 @@ -\relax -\providecommand\hyper@newdestlabel[2]{} -\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument} -\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined -\global\let\oldcontentsline\contentsline -\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}} -\global\let\oldnewlabel\newlabel -\gdef\newlabel#1#2{\newlabelxx{#1}#2} -\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}} -\AtEndDocument{\ifx\hyper@anchor\@undefined -\let\contentsline\oldcontentsline -\let\newlabel\oldnewlabel -\fi} -\fi} -\global\let\hyper@last\relax -\gdef\HyperFirstAtBeginDocument#1{#1} -\providecommand\HyField@AuxAddToFields[1]{} -\providecommand\HyField@AuxAddToCoFields[2]{} -\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}{section.1}\protected@file@percent } -\newlabel{sec:intro}{{1}{1}{Introduction}{section.1}{}} -\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces IP Core Symbol.\relax }}{1}{figure.caption.5}\protected@file@percent } -\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}} -\newlabel{fig:symbol}{{1}{1}{IP Core Symbol.\relax }{figure.caption.5}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {1.1}Features}{1}{subsection.1.1}\protected@file@percent } -\newlabel{sec:feat}{{1.1}{1}{Features}{subsection.1.1}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {1.2}Benefits}{2}{subsection.1.2}\protected@file@percent } -\newlabel{sec:benef}{{1.2}{2}{Benefits}{subsection.1.2}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {1.3}Deliverables}{2}{subsection.1.3}\protected@file@percent } -\newlabel{sec:deliv}{{1.3}{2}{Deliverables}{subsection.1.3}{}} -\@writefile{toc}{\contentsline {section}{\numberline {2}Description}{2}{section.2}\protected@file@percent } -\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Block Diagram}{2}{subsection.2.1}\protected@file@percent } -\newlabel{sec:bdd}{{2.1}{2}{Block Diagram}{subsection.2.1}{}} -\@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces High-Level Block Diagram.\relax }}{2}{figure.caption.6}\protected@file@percent } -\newlabel{fig:bd}{{2}{2}{High-Level Block Diagram.\relax }{figure.caption.6}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Configuration}{3}{subsection.2.2}\protected@file@percent } -\newlabel{sec:ipconfig}{{2.2}{3}{Configuration}{subsection.2.2}{}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.1}Macros}{3}{subsubsection.2.2.1}\protected@file@percent } -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.2}Parameters}{3}{subsubsection.2.2.2}\protected@file@percent } -\newlabel{sec:cp}{{2.2.2}{3}{Parameters}{subsubsection.2.2.2}{}} -\@writefile{lot}{\contentsline {table}{\numberline {1}{\ignorespaces Synthesis Parameters.\relax }}{3}{table.caption.7}\protected@file@percent } -\newlabel{tab:sp}{{1}{3}{Synthesis Parameters.\relax }{table.caption.7}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Interface Signals}{3}{subsection.2.3}\protected@file@percent } -\newlabel{sec:ifsig}{{2.3}{3}{Interface Signals}{subsection.2.3}{}} -\@writefile{lot}{\contentsline {table}{\numberline {2}{\ignorespaces General Interface Signals.\relax }}{3}{table.caption.8}\protected@file@percent } -\newlabel{gen_if_tab:is}{{2}{3}{General Interface Signals.\relax }{table.caption.8}{}} -\@writefile{lot}{\contentsline {table}{\numberline {3}{\ignorespaces IObundle Interface Signals\relax }}{3}{table.caption.9}\protected@file@percent } -\newlabel{tab:if_iob_s}{{3}{3}{IObundle Interface Signals\relax }{table.caption.9}{}} -\@writefile{lot}{\contentsline {table}{\numberline {4}{\ignorespaces RS232 Interface Signals\relax }}{3}{table.caption.10}\protected@file@percent } -\newlabel{tab:if_rs232}{{4}{3}{RS232 Interface Signals\relax }{table.caption.10}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Software Accessible Registers}{4}{subsection.2.4}\protected@file@percent } -\newlabel{sec:csrs}{{2.4}{4}{Software Accessible Registers}{subsection.2.4}{}} -\@writefile{lot}{\contentsline {table}{\numberline {5}{\ignorespaces UART software accessible registers.\relax }}{4}{table.caption.11}\protected@file@percent } -\newlabel{tab:sw_tx}{{5}{4}{UART software accessible registers.\relax }{table.caption.11}{}} -\@writefile{toc}{\contentsline {section}{\numberline {3}Usage}{4}{section.3}\protected@file@percent } -\newlabel{sec:inst}{{3}{4}{Usage}{section.3}{}} -\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Core Instance and Required Surrounding Blocks\relax }}{4}{figure.caption.12}\protected@file@percent } -\newlabel{fig:inst}{{3}{4}{Core Instance and Required Surrounding Blocks\relax }{figure.caption.12}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}Simulation}{4}{subsection.3.1}\protected@file@percent } -\newlabel{sec:tbbd}{{3.1}{4}{Simulation}{subsection.3.1}{}} -\@writefile{lof}{\contentsline {figure}{\numberline {4}{\ignorespaces Testbench Block Diagram\relax }}{5}{figure.caption.13}\protected@file@percent } -\newlabel{fig:tbbd}{{4}{5}{Testbench Block Diagram\relax }{figure.caption.13}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Synthesis}{5}{subsection.3.2}\protected@file@percent } -\newlabel{sec:synth}{{3.2}{5}{Synthesis}{subsection.3.2}{}} -\@writefile{toc}{\contentsline {section}{\numberline {4}Implementation Results}{5}{section.4}\protected@file@percent } -\newlabel{sec:results}{{4}{5}{Implementation Results}{section.4}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}FPGA}{5}{subsection.4.1}\protected@file@percent } -\@writefile{lot}{\contentsline {table}{\numberline {6}{\ignorespaces AMD Kintex Ultrascale FPGAs.\relax }}{5}{table.caption.14}\protected@file@percent } -\newlabel{tab:fpga_amd}{{6}{5}{AMD Kintex Ultrascale FPGAs.\relax }{table.caption.14}{}} -\@writefile{lot}{\contentsline {table}{\numberline {7}{\ignorespaces Intel Cyclone V GT FPGAs.\relax }}{6}{table.caption.15}\protected@file@percent } -\newlabel{tab:fpga_intel}{{7}{6}{Intel Cyclone V GT FPGAs.\relax }{table.caption.15}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}ASIC}{6}{subsection.4.2}\protected@file@percent } diff --git a/lib/hardware/iob_uart/document/ug/test.expected.license b/lib/hardware/iob_uart/document/ug/test.expected.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_uart/document/ug/test.expected.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_uart/document/ug/ug.pdf b/lib/hardware/iob_uart/document/ug/ug.pdf deleted file mode 100644 index b605f345b7be4e45562978da0eb77663e8ca838a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110255 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 z 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 diff --git a/lib/hardware/iob_uart/document/ug/ug.pdf.license b/lib/hardware/iob_uart/document/ug/ug.pdf.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_uart/document/ug/ug.pdf.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_uart/hardware/simulation/sim_build.mk b/lib/hardware/iob_uart/hardware/simulation/sim_build.mk deleted file mode 100644 index 780b35a26..000000000 --- a/lib/hardware/iob_uart/hardware/simulation/sim_build.mk +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -# -# This file is included in BUILD_DIR/sim/Makefile -# - -NOCLEAN+=-o -name "uart_tb.v" diff --git a/lib/hardware/iob_uart/hardware/simulation/src/iob_uart_tb.v b/lib/hardware/iob_uart/hardware/simulation/src/iob_uart_tb.v deleted file mode 100644 index 41a8648e4..000000000 --- a/lib/hardware/iob_uart/hardware/simulation/src/iob_uart_tb.v +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`include "iob_uart_csrs_def.vh" -`include "iob_uart_conf.vh" - -`define IOB_RESET(CLK, RESET, PRE, DURATION, POST) RESET=~`IOB_UART_RST_POL;\ - #PRE RESET=`IOB_UART_RST_POL; #DURATION RESET=~`IOB_UART_RST_POL; #POST;\ - @(posedge CLK) #1; - -//ASCII codes used -`define STX 2 //start of text -`define ETX 3 //end of text -`define EOT 4 //end of transission -`define ENQ 5 //enquiry -`define ACK 6 //acklowledge -`define FTX 7 //transmit file -`define FRX 8 //receive file - -module iob_uart_tb; - - parameter clk_frequency = 100e6; //100 MHz - parameter baud_rate = 1e6; //high value to speed sim - parameter clk_per = 1e9 / clk_frequency; - - //iterator - integer i, fd; - - // CORE SIGNALS - reg arst = ~`IOB_UART_RST_POL; - reg clk; - - //control interface (backend) - reg rst_soft; - reg wr_en; - reg rd_en; - reg [`IOB_UART_DIV_W-1:0] div; - - reg tx_en; - reg [ 7:0] tx_data; - wire tx_ready; - - reg rx_en; - wire [ 7:0] rx_data; - reg [ 7:0] rcvd_data; - wire rx_ready; - - //rs232 interface (frontend) - wire rts2cts; - wire tx2rx; - - - initial begin -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - clk = 1; - rst_soft = 0; - - rd_en = 0; - wr_en = 0; - - tx_en = 0; - rx_en = 0; - - div = clk_frequency / baud_rate; - - //apply async reset - `IOB_RESET(clk, arst, 100, 1_000, 100); - - // assert tx not ready - if (tx_ready) begin - $display("ERROR: TX is ready initially"); - $finish(); - end - - // assert rx not ready - if (rx_ready) begin - $display("ERROR: RX is ready initially"); - $finish(); - end - - //pulse soft reset - #1 rst_soft = 1; - @(posedge clk) #1 rst_soft = 0; - - - //enable rx - @(posedge clk) #1 rx_en = 1; - - //enable tx - #20000; - @(posedge clk) #1 tx_en = 1; - - - // write data to send - for (i = 0; i < 256; i = i + 1) begin - - //wait for tx ready - do @(posedge clk); while (!tx_ready); - - //write word to send - @(posedge clk) #1 wr_en = 1; - tx_data = i; - @(posedge clk) #1 wr_en = 0; - - //wait for core to receive datarx ready - do @(posedge clk); while (!rx_ready); - - //read received word - @(posedge clk) #1 rd_en = 1; - rcvd_data = rx_data; - @(posedge clk) #1 rd_en = 0; - - - // check received data - if (rcvd_data != i) begin - $display("Test failed: got %x, expected %x", rcvd_data, i); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test failed: got %x, expected %x", rcvd_data, i); - $fclose(fd); - $finish(); - end - - @(posedge clk); - @(posedge clk); - @(posedge clk); - - end // for (i=0; i < 256; i= i+1) - - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - - end - - // - // CLOCK - // - - //system clock - always #(clk_per / 2) clk = ~clk; - - - // Instantiate the Unit Under Test (UUT) - uart_core uut ( - .clk_i (clk), - .arst_i (arst), - .rst_soft_i (rst_soft), - .tx_en_i (tx_en), - .rx_en_i (rx_en), - .tx_ready_o (tx_ready), - .rx_ready_o (rx_ready), - .tx_data_i (tx_data), - .rx_data_o (rx_data), - .data_write_en_i(wr_en), - .data_read_en_i (rd_en), - .bit_duration_i (div), - .rs232_rxd_i (tx2rx), - .rs232_txd_o (tx2rx), - .rs232_cts_i (rts2cts), - .rs232_rts_o (rts2cts) - ); - -endmodule - diff --git a/lib/hardware/iob_uart/hardware/simulation/test.expected b/lib/hardware/iob_uart/hardware/simulation/test.expected deleted file mode 100644 index 883063ec4..000000000 --- a/lib/hardware/iob_uart/hardware/simulation/test.expected +++ /dev/null @@ -1 +0,0 @@ -Test PASSED diff --git a/lib/hardware/iob_uart/hardware/simulation/test.expected.license b/lib/hardware/iob_uart/hardware/simulation/test.expected.license deleted file mode 100644 index 9a5279e71..000000000 --- a/lib/hardware/iob_uart/hardware/simulation/test.expected.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2024 IObundle - -SPDX-License-Identifier: MIT diff --git a/lib/hardware/iob_uart/hardware/src/uart_core.v b/lib/hardware/iob_uart/hardware/src/uart_core.v deleted file mode 100644 index d988ca725..000000000 --- a/lib/hardware/iob_uart/hardware/src/uart_core.v +++ /dev/null @@ -1,385 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`include "iob_uart_csrs_def.vh" -`include "iob_uart_conf.vh" - -module uart_core ( - input clk_i, - input arst_i, - input rst_soft_i, - input tx_en_i, - input rx_en_i, - input [ 7:0] tx_data_i, - output reg [ 7:0] rx_data_o, - output reg tx_ready_o, - output reg rx_ready_o, - input rs232_rxd_i, - output rs232_txd_o, - input rs232_cts_i, - output reg rs232_rts_o, - input data_write_en_i, - input data_read_en_i, - input [`IOB_UART_DIV_W-1:0] bit_duration_i -); - - //////////////////////////////////////////////////////// - // TXtxd - - //BLOCK Serial Transmit Controller & This block serializes the data written to the UART\_TXDATA by the CPU, and sends it to the {\tt txd} ouput. - - //clear to send (cts) synchronizer - reg [1:0] cts_int; - always @(posedge clk_i) cts_int <= {cts_int[0], rs232_cts_i}; - - wire [7:0] tx_data_int; - iob_reg_e #( - .DATA_W (8), - .RST_VAL(8'b0) - ) txdata_reg ( - // clk_en_rst port - .clk_i (clk_i), - .cke_i (1'b1), - .arst_i(arst_i), - .en_i (data_write_en_i), - // data_i port - .data_i(tx_data_i), - // data_o port - .data_o(tx_data_int) - ); - - - // sender - reg [ 1:0] tx_pc; - reg [ 9:0] tx_pattern; //stop(1) + data(8) + start(1) = 10 bits - reg [ 3:0] tx_bitcnt; - reg [15:0] tx_cyclecnt; - // receiver - reg [ 2:0] rx_pc; - reg [15:0] rx_cyclecnt; - reg [ 3:0] rx_bitcnt; - reg [ 7:0] rx_pattern; - - //tx bit - assign rs232_txd_o = tx_pattern[0]; - - - localparam RST_POL = `IOB_UART_RST_POL; - - generate - if (RST_POL == 1) begin : g_rst_pol_1 - //tx program - always @(posedge clk_i, posedge arst_i) - if (arst_i) begin - tx_pc <= 1'b0; - tx_ready_o <= 1'b0; - tx_pattern <= ~10'b0; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b0; - end else if (rst_soft_i) begin - tx_pc <= 1'b0; - tx_ready_o <= 1'b0; - tx_pattern <= ~10'b0; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b0; - end else if (tx_en_i && cts_int[1]) begin - - tx_pc <= tx_pc + 1'b1; //increment pc by default - - case (tx_pc) - - 0: begin //wait for data to send - tx_ready_o <= 1'b1; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b1; - tx_pattern <= ~9'b0; - if (!data_write_en_i) tx_pc <= tx_pc; - else tx_ready_o <= 1'b0; - end - - 1: begin //load tx pattern to send - tx_pattern <= {1'b1, tx_data_int[7:0], 1'b0}; //{stop, data, start}>> - end - - 2: begin //send pattern - tx_pc <= tx_pc; //stay here util pattern sent - tx_cyclecnt <= tx_cyclecnt + 1'b1; //increment cycle counter - if (tx_cyclecnt == bit_duration_i) - if (tx_bitcnt == 4'd9) begin //stop bit sent sent - tx_pc <= 1'b0; //restart program - end else begin //data bit sent - tx_pattern <= tx_pattern >> 1; - tx_bitcnt <= tx_bitcnt + 1'b1; //send next bit - tx_cyclecnt <= 1'b1; - end - end - - default: ; - - endcase - - end else begin - - tx_pc <= 1'b0; - tx_ready_o <= 1'b0; - tx_pattern <= ~10'b0; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b0; - - end - - - //////////////////////////////////////////////////////// - // RX - //////////////////////////////////////////////////////// - - //BLOCK Serial Reiceive Controller & This block deserializes the data received from the pin {\tt rxd} input, and writes it to the UART\_RXDATA register, so the CPU can read it. - - - // receiver program - always @(posedge clk_i, posedge arst_i) begin - if (arst_i) begin - rx_pc <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - rx_ready_o <= 1'b0; - rs232_rts_o <= 1'b0; - end else if (rst_soft_i) begin - rx_pc <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - rx_ready_o <= 1'b0; - rs232_rts_o <= 1'b0; - end else if (rx_en_i) begin - rx_pc <= rx_pc + 1'b1; //increment pc by default - - case (rx_pc) - - 0: begin //sync up - rs232_rts_o <= 1'b1; - rx_ready_o <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - if (!rs232_rxd_i) //line is low, wait until it is high - rx_pc <= rx_pc; - end - - 1: begin //line is high - rx_cyclecnt <= rx_cyclecnt + 1'b1; - if (rx_cyclecnt != bit_duration_i) rx_pc <= rx_pc; - else if (!rs232_rxd_i) //error: line returned to low early - rx_pc <= 1'b0; //go back and resync - end - - 2: begin //wait for start bit - rx_cyclecnt <= 1'b1; - if (rs232_rxd_i) //start bit (low) has not arrived, wait - rx_pc <= rx_pc; - end - - 3: begin //start bit is here - rx_cyclecnt <= rx_cyclecnt + 1'b1; - if (rx_cyclecnt != bit_duration_i / 2) // wait half bit period - rx_pc <= rx_pc; - else if (rs232_rxd_i) //error: line returned to high unexpectedly - rx_pc <= 1'b0; //go back and resync - else rx_cyclecnt <= 1'b1; - end - - 4: begin // receive data - rx_cyclecnt <= rx_cyclecnt + 1'b1; - if (rx_cyclecnt == bit_duration_i) begin - rx_cyclecnt <= 1'b1; - rx_bitcnt <= rx_bitcnt + 1'b1; - rx_pattern <= {rs232_rxd_i, rx_pattern[7:1]}; //sample rx line - if (rx_bitcnt == 4'd8) begin //stop bit is here - rx_pattern <= rx_pattern; //unsample rx line - rx_data_o <= rx_pattern; //unsample rx line - rx_ready_o <= 1'b1; - rx_bitcnt <= 1'b0; - rx_pc <= 2'd2; - end else begin - rx_pc <= rx_pc; //wait for more bits - end - end else begin - rx_pc <= rx_pc; //wait for more cycles - end - end - - default: ; - - endcase - - if (data_read_en_i) begin - rx_ready_o <= 1'b0; - end - end else begin - rx_pc <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - rx_ready_o <= 1'b0; - - end - end // always @ (posedge clk_i, posedge arst_i) - end // block: g_rst_pol_1 - else begin: g_rst_pol_0 - //tx program - always @(posedge clk_i, negedge arst_i) - if (!arst_i) begin - tx_pc <= 1'b0; - tx_ready_o <= 1'b0; - tx_pattern <= ~10'b0; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b0; - end else if (rst_soft_i) begin - tx_pc <= 1'b0; - tx_ready_o <= 1'b0; - tx_pattern <= ~10'b0; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b0; - end else if (tx_en_i && cts_int[1]) begin - - tx_pc <= tx_pc + 1'b1; //increment pc by default - - case (tx_pc) - - 0: begin //wait for data to send - tx_ready_o <= 1'b1; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b1; - tx_pattern <= ~9'b0; - if (!data_write_en_i) tx_pc <= tx_pc; - else tx_ready_o <= 1'b0; - end - - 1: begin //load tx pattern to send - tx_pattern <= {1'b1, tx_data_int[7:0], 1'b0}; //{stop, data, start}>> - end - - 2: begin //send pattern - tx_pc <= tx_pc; //stay here util pattern sent - tx_cyclecnt <= tx_cyclecnt + 1'b1; //increment cycle counter - if (tx_cyclecnt == bit_duration_i) - if (tx_bitcnt == 4'd9) begin //stop bit sent sent - tx_pc <= 1'b0; //restart program - end else begin //data bit sent - tx_pattern <= tx_pattern >> 1; - tx_bitcnt <= tx_bitcnt + 1'b1; //send next bit - tx_cyclecnt <= 1'b1; - end - end - - default: ; - - endcase - - end else begin - - tx_pc <= 1'b0; - tx_ready_o <= 1'b0; - tx_pattern <= ~10'b0; - tx_bitcnt <= 1'b0; - tx_cyclecnt <= 1'b0; - - end - - - //////////////////////////////////////////////////////// - // RX - //////////////////////////////////////////////////////// - - //BLOCK Serial Reiceive Controller & This block deserializes the data received from the pin {\tt rxd} input, and writes it to the UART\_RXDATA register, so the CPU can read it. - - - // receiver program - always @(posedge clk_i, negedge arst_i) begin - if (!arst_i) begin - rx_pc <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - rx_ready_o <= 1'b0; - rs232_rts_o <= 1'b0; - end else if (rst_soft_i) begin - rx_pc <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - rx_ready_o <= 1'b0; - rs232_rts_o <= 1'b0; - end else if (rx_en_i) begin - rx_pc <= rx_pc + 1'b1; //increment pc by default - - case (rx_pc) - - 0: begin //sync up - rs232_rts_o <= 1'b1; - rx_ready_o <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - if (!rs232_rxd_i) //line is low, wait until it is high - rx_pc <= rx_pc; - end - - 1: begin //line is high - rx_cyclecnt <= rx_cyclecnt + 1'b1; - if (rx_cyclecnt != bit_duration_i) rx_pc <= rx_pc; - else if (!rs232_rxd_i) //error: line returned to low early - rx_pc <= 1'b0; //go back and resync - end - - 2: begin //wait for start bit - rx_cyclecnt <= 1'b1; - if (rs232_rxd_i) //start bit (low) has not arrived, wait - rx_pc <= rx_pc; - end - - 3: begin //start bit is here - rx_cyclecnt <= rx_cyclecnt + 1'b1; - if (rx_cyclecnt != bit_duration_i / 2) // wait half bit period - rx_pc <= rx_pc; - else if (rs232_rxd_i) //error: line returned to high unexpectedly - rx_pc <= 1'b0; //go back and resync - else rx_cyclecnt <= 1'b1; - end - - 4: begin // receive data - rx_cyclecnt <= rx_cyclecnt + 1'b1; - if (rx_cyclecnt == bit_duration_i) begin - rx_cyclecnt <= 1'b1; - rx_bitcnt <= rx_bitcnt + 1'b1; - rx_pattern <= {rs232_rxd_i, rx_pattern[7:1]}; //sample rx line - if (rx_bitcnt == 4'd8) begin //stop bit is here - rx_pattern <= rx_pattern; //unsample rx line - rx_data_o <= rx_pattern; //unsample rx line - rx_ready_o <= 1'b1; - rx_bitcnt <= 1'b0; - rx_pc <= 2'd2; - end else begin - rx_pc <= rx_pc; //wait for more bits - end - end else begin - rx_pc <= rx_pc; //wait for more cycles - end - end - - default: ; - - endcase - - if (data_read_en_i) begin - rx_ready_o <= 1'b0; - end - end else begin - rx_pc <= 1'b0; - rx_cyclecnt <= 1'b1; - rx_bitcnt <= 1'b0; - rx_ready_o <= 1'b0; - - end - end // always @ (posedge clk_i, posedge arst_i) - end - endgenerate - - -endmodule diff --git a/lib/hardware/iob_uart/hardware/uart_core.py b/lib/hardware/iob_uart/hardware/uart_core.py deleted file mode 100644 index f1b0da190..000000000 --- a/lib/hardware/iob_uart/hardware/uart_core.py +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_rst_s", - "interface": { - "type": "clk_rst", - "subtype": "slave", - }, - "descr": "Clock and reset", - }, - { - "name": "reg_interface", - "descr": "", - "signals": [ - {"name": "rst_soft", "width": "1", "direction": "input"}, - {"name": "tx_en", "width": "1", "direction": "input"}, - {"name": "rx_en", "width": "1", "direction": "input"}, - {"name": "tx_ready", "width": "1", "direction": "output"}, - {"name": "rx_ready", "width": "1", "direction": "output"}, - {"name": "tx_data", "width": "8", "direction": "input"}, - {"name": "rx_data", "width": "8", "direction": "output"}, - {"name": "data_write_en", "width": "1", "direction": "input"}, - {"name": "data_read_en", "width": "1", "direction": "input"}, - { - "name": "bit_duration", - "width": "`IOB_UART_DIV_W", - "direction": "input", - }, - ], - }, - { - "name": "rs232_m", - "interface": { - "type": "rs232", - }, - "descr": "RS232 interface", - }, - ], - "blocks": [ - { - "core_name": "iob_reg_e", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/iob_uart/iob_uart.py b/lib/hardware/iob_uart/iob_uart.py deleted file mode 100755 index bdd3dc767..000000000 --- a/lib/hardware/iob_uart/iob_uart.py +++ /dev/null @@ -1,333 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - CSR_IF = py_params_dict["csr_if"] if "csr_if" in py_params_dict else "iob" - NAME = py_params_dict["name"] if "name" in py_params_dict else "iob_uart" - attributes_dict = { - "name": NAME, - "version": "0.1", - "board_list": ["cyclonev_gt_dk", "aes_ku040_db_g"], - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width.", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "3", # Same as `IOB_UART_CSRS_ADDR_W - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "RST_POL", - "type": "M", - "val": "1", - "min": "0", - "max": "1", - "descr": "Reset polarity.", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "cbus_s", - "interface": { - "type": CSR_IF, - "subtype": "slave", - "ADDR_W": "3", # Same as `IOB_UART_CSRS_ADDR_W - "DATA_W": "DATA_W", - }, - "descr": "CPU native interface", - }, - { - "name": "rs232_m", - "interface": { - "type": "rs232", - }, - "descr": "RS232 interface", - }, - ], - "wires": [ - { - "name": "csrs_iob", - "descr": "Internal iob interface", - "interface": { - "type": "iob", - "wire_prefix": "csrs_", - "ADDR_W": "ADDR_W", - "DATA_W": "DATA_W", - }, - }, - { - "name": "softreset", - "descr": "", - "signals": [ - {"name": "softreset_wr", "width": 1}, - ], - }, - { - "name": "div", - "descr": "", - "signals": [ - {"name": "div_wr", "width": 16}, - ], - }, - { - "name": "txdata", - "descr": "", - "signals": [ - {"name": "txdata_wdata_wr", "width": 8}, - {"name": "txdata_wen_wr", "width": 1}, - {"name": "txdata_wready_wr", "width": 1}, - ], - }, - { - "name": "txen", - "descr": "", - "signals": [ - {"name": "txen_wr", "width": 1}, - ], - }, - { - "name": "rxen", - "descr": "", - "signals": [ - {"name": "rxen_wr", "width": 1}, - ], - }, - { - "name": "txready", - "descr": "", - "signals": [ - {"name": "txready_rd", "width": 1}, - ], - }, - { - "name": "rxready", - "descr": "", - "signals": [ - {"name": "rxready_rd", "width": 1}, - ], - }, - { - "name": "rxdata", - "descr": "", - "signals": [ - {"name": "rxdata_rdata_rd", "width": 8}, - {"name": "rxdata_rvalid_rd", "width": 1}, - {"name": "rxdata_ren_rd", "width": 1}, - {"name": "rxdata_rready_rd", "width": 1}, - ], - }, - # RXDATA reg - { - "name": "iob_reg_rvalid_data_i", - "descr": "", - "signals": [ - {"name": "rxdata_rvalid_nxt", "width": 1}, - ], - }, - { - "name": "iob_reg_rvalid_data_o", - "descr": "", - "signals": [ - {"name": "rxdata_rvalid_rd"}, - ], - }, - # uart core - { - "name": "clk_rst", - "descr": "Clock and reset", - "signals": [ - {"name": "clk"}, - {"name": "arst"}, - ], - }, - { - "name": "uart_core_reg_interface", - "descr": "", - "signals": [ - {"name": "softreset_wr"}, - {"name": "txen_wr"}, - {"name": "rxen_wr"}, - {"name": "txready_rd"}, - {"name": "rxready_rd"}, - {"name": "txdata_wdata_wr"}, - {"name": "rxdata_rdata_rd"}, - {"name": "txdata_wen_wr"}, - {"name": "rxdata_ren_rd"}, - {"name": "div_wr"}, - ], - }, - ], - "blocks": [ - { - "core_name": "csrs", - "instance_name": "csrs_inst", - "instance_description": "Control/Status Registers", - "autoaddr": False, - "rw_overlap": True, - "csrs": [ - { - "name": "uart", - "descr": "UART software accessible registers.", - "regs": [ - { - "name": "softreset", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "addr": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "Soft reset.", - }, - { - "name": "div", - "type": "W", - "n_bits": 16, - "rst_val": 0, - "addr": 2, - "log2n_items": 0, - "autoreg": True, - "descr": "Bit duration in system clock cycles.", - }, - { - "name": "txdata", - "type": "W", - "n_bits": 8, - "rst_val": 0, - "addr": 4, - "log2n_items": 0, - "autoreg": False, - "descr": "TX data.", - }, - { - "name": "txen", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "addr": 5, - "log2n_items": 0, - "autoreg": True, - "descr": "TX enable.", - }, - { - "name": "rxen", - "type": "W", - "n_bits": 1, - "rst_val": 0, - "addr": 6, - "log2n_items": 0, - "autoreg": True, - "descr": "RX enable.", - }, - { - "name": "txready", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "addr": 0, - "log2n_items": 0, - "autoreg": True, - "descr": "TX ready to receive data.", - }, - { - "name": "rxready", - "type": "R", - "n_bits": 1, - "rst_val": 0, - "addr": 1, - "log2n_items": 0, - "autoreg": True, - "descr": "RX data is ready to be read.", - }, - # NOTE: RXDATA needs to be the only Read register in a CPU Word - # RXDATA_ren access is used to change UART state machine - { - "name": "rxdata", - "type": "R", - "n_bits": 8, - "rst_val": 0, - "addr": 4, - "log2n_items": 0, - "autoreg": False, - "descr": "RX data.", - }, - ], - } - ], - "csr_if": CSR_IF, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "control_if_s": "cbus_s", - "csrs_iob_o": "csrs_iob", - # Register interfaces - "softreset": "softreset", - "div": "div", - "txdata": "txdata", - "txen": "txen", - "rxen": "rxen", - "txready": "txready", - "rxready": "rxready", - "rxdata": "rxdata", - }, - }, - { - "core_name": "iob_reg", - "instance_name": "iob_reg_rvalid", - "instance_description": "Register for rxdata rvalid", - "parameters": { - "DATA_W": 1, - "RST_VAL": "1'b0", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "iob_reg_rvalid_data_i", - "data_o": "iob_reg_rvalid_data_o", - }, - }, - { - "core_name": "uart_core", - "instance_name": "uart_core_inst", - "instance_description": "UART core driver", - "connect": { - "clk_rst_s": "clk_rst", - "reg_interface": "uart_core_reg_interface", - "rs232_m": "rs232_m", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - // txdata Manual logic - assign txdata_wready_wr = 1'b1; - - // rxdata Manual logic - assign rxdata_rready_rd = 1'b1; - - // rxdata rvalid is iob_valid registered - assign rxdata_rvalid_nxt = csrs_iob_valid & rxdata_ren_rd; -""", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/iob_uart/software/src/iob_uart.c b/lib/hardware/iob_uart/software/src/iob_uart.c deleted file mode 100644 index a076e80ca..000000000 --- a/lib/hardware/iob_uart/software/src/iob_uart.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include "iob_uart.h" -#include - -// TX FUNCTIONS -void uart_txwait() { - while (!IOB_UART_GET_TXREADY()) - ; -} - -void uart_putc(char c) { - while (!IOB_UART_GET_TXREADY()) - ; - IOB_UART_SET_TXDATA(c); -} - -// RX FUNCTIONS -void uart_rxwait() { - while (!IOB_UART_GET_RXREADY()) - ; -} - -uint8_t uart_getc() { - while (!IOB_UART_GET_RXREADY()) - ; - return IOB_UART_GET_RXDATA(); -} - -// UART basic functions -void uart_init(int base_address, uint16_t div) { - // capture base address for good - IOB_UART_INIT_BASEADDR(base_address); - - // pulse soft reset - IOB_UART_SET_SOFTRESET(1); - IOB_UART_SET_SOFTRESET(0); - - // Set the division factor div - // div should be equal to round (fclk/baudrate) - // E.g for fclk = 100 Mhz for a baudrate of 115200 we should - // IOB_UART_SET_DIV(868) - IOB_UART_SET_DIV(div); - - // enable TX and RX - IOB_UART_SET_TXEN(1); - IOB_UART_SET_RXEN(1); -} - -void uart_finish() { - uart_putc(EOT); - uart_txwait(); -} - -// Print string, excluding end of string (0) -void uart_puts(const char *s) { - while (*s) - uart_putc(*s++); -} - -// Sends the name of the file to use, including end of string (0) -void uart_sendstr(char *name) { - int i = 0; - do - uart_putc(name[i]); - while (name[i++]); -} - -// Receives file into mem -uint32_t uart_recvfile(char *file_name, char *mem) { - - uart_puts(UART_PROGNAME); - uart_puts(": requesting to receive file\n"); - - // send file receive request - uart_putc(FRX); - - // send file name - uart_sendstr(file_name); - - // receive file size - uint32_t file_size = uart_getc(); - file_size |= ((uint32_t)uart_getc()) << 8; - file_size |= ((uint32_t)uart_getc()) << 16; - file_size |= ((uint32_t)uart_getc()) << 24; - - // send ACK before receiving file - uart_putc(ACK); - - // write file to memory - for (uint32_t i = 0; i < file_size; i++) { - mem[i] = uart_getc(); - } - - uart_puts(UART_PROGNAME); - uart_puts(": file received\n"); - - return file_size; -} - -// Sends mem contents to a file -void uart_sendfile(char *file_name, int file_size, char *mem) { - - uart_puts(UART_PROGNAME); - uart_puts(": requesting to send file\n"); - - // send file transmit command - uart_putc(FTX); - - // send file name - uart_sendstr(file_name); - - // send file size - uart_putc((char)(file_size & 0x0ff)); - uart_putc((char)((file_size & 0x0ff00) >> 8)); - uart_putc((char)((file_size & 0x0ff0000) >> 16)); - uart_putc((char)((file_size & 0x0ff000000) >> 24)); - - // send file contents - for (int i = 0; i < file_size; i++) - uart_putc(mem[i]); - - uart_puts(UART_PROGNAME); - uart_puts(": file sent\n"); -} diff --git a/lib/hardware/iob_uart/software/src/iob_uart.h b/lib/hardware/iob_uart/software/src/iob_uart.h deleted file mode 100644 index 14a189191..000000000 --- a/lib/hardware/iob_uart/software/src/iob_uart.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include - -#include "iob_uart_csrs.h" - -#define UART_PROGNAME "IOb-UART" - -// UART commands -#define STX 2 // start text -#define ETX 3 // end text -#define EOT 4 // end of transission -#define ENQ 5 // enquiry -#define ACK 6 // acklowledge -#define FTX 7 // transmit file -#define FRX 8 // receive file - -// UART functions - -// Reset UART and set the division factor -void uart_init(int base_address, uint16_t div); - -// Close transmission -void uart_finish(); - -// TX FUNCTIONS - -// Enable / disable tx -void uart_txen(uint8_t val); - -// Wait for tx to be ready -void uart_txwait(); - -// Print char -void uart_putc(char c); - -// Print string -void uart_puts(const char *s); - -// Send file -void uart_sendfile(char *file_name, int file_size, char *mem); - -// RX FUNCTIONS - -// Wait for rx to be ready -void uart_rxwait(); - -// Get char -uint8_t uart_getc(); - -// Receive file -uint32_t uart_recvfile(char *file_name, char *mem); diff --git a/lib/hardware/iob_uart/software/src/iob_uart_csrs_pc_emul.c b/lib/hardware/iob_uart/software/src/iob_uart_csrs_pc_emul.c deleted file mode 100644 index d041dc370..000000000 --- a/lib/hardware/iob_uart/software/src/iob_uart_csrs_pc_emul.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* PC Emulation of UART peripheral */ - -#include -#include -#include - -#include "iob_uart_csrs.h" - -static uint16_t div_value; - -static FILE *cnsl2soc_fd; -static FILE *soc2cnsl_fd; - -void pc_emul_error(char *s) { - printf("ERROR in iob-uart PC emulation: %s", s); - exit(1); -} - -static int base; -void IOB_UART_INIT_BASEADDR(uint32_t addr) { - - // wait for console to create communication files - while ((cnsl2soc_fd = fopen("./cnsl2soc", "rb")) == NULL) - ; - fclose(cnsl2soc_fd); - soc2cnsl_fd = fopen("./soc2cnsl", "wb"); - - base = addr; - return; -} - -void IOB_UART_SET_SOFTRESET(uint8_t value) { - div_value = 0; - return; -} - -void IOB_UART_SET_DIV(uint16_t value) { - div_value = value; - return; -} - -void IOB_UART_SET_TXDATA(uint8_t value) { - // send byte to console - char aux_char; - int nbytes; - - fwrite(&value, sizeof(char), 1, soc2cnsl_fd); - fflush(soc2cnsl_fd); -} - -void IOB_UART_SET_TXEN(uint8_t value) { return; } - -void IOB_UART_SET_RXEN(uint8_t value) { return; } - -uint8_t IOB_UART_GET_TXREADY() { return 1; } - -uint8_t IOB_UART_GET_RXDATA() { - // get byte from console - uint8_t c; - int nbytes; - - while (1) { - cnsl2soc_fd = fopen("./cnsl2soc", "rb"); - if (!cnsl2soc_fd) - fclose(soc2cnsl_fd); - - nbytes = fread(&c, sizeof(char), 1, cnsl2soc_fd); - if (nbytes == 1) { - fclose(cnsl2soc_fd); - - // the following removes file contents - cnsl2soc_fd = fopen("./cnsl2soc", "wb"); - fclose(cnsl2soc_fd); - - break; - } - fclose(cnsl2soc_fd); - } - return c; -} - -uint8_t IOB_UART_GET_RXREADY() { return 1; } diff --git a/lib/hardware/memories/axi_ram/axi_ram.py b/lib/hardware/memories/axi_ram/axi_ram.py deleted file mode 100644 index 73473473b..000000000 --- a/lib/hardware/memories/axi_ram/axi_ram.py +++ /dev/null @@ -1,127 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "confs": [ - { - "name": "DATA_WIDTH", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "ADDR_WIDTH", - "type": "P", - "val": "16", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "STRB_WIDTH", - "type": "P", - "val": "(DATA_WIDTH / 8)", - "min": "NA", - "max": "NA", - "descr": "Strobe bus width", - }, - { - "name": "ID_WIDTH", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "ID bus width", - }, - { - "name": "LEN_WIDTH", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "Len width", - }, - { - "name": "PIPELINE_OUTPUT", - "type": "P", - "val": "0", - "min": "NA", - "max": "NA", - "descr": "Extra pipeline register on output", - }, - { - "name": "FILE", - "type": "P", - "val": "none", - "min": "NA", - "max": "NA", - "descr": "Name of the file to be used", - }, - { - "name": "FILE_SIZE", - "type": "P", - "val": "1", - "min": "NA", - "max": "NA", - "descr": "File size", - }, - { - "name": "HEX_DATA_W", - "type": "P", - "val": "DATA_WIDTH", - "min": "NA", - "max": "NA", - "descr": "Width of hex data", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - { - "name": "clk", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "rst_i", - "descr": "Synchronous reset", - "signals": [ - { - "name": "rst", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "axi_s", - "interface": { - "type": "axi", - "subtype": "slave", - "ID_W": "ID_WIDTH", - "ADDR_W": "ADDR_WIDTH", - "DATA_W": "DATA_WIDTH", - "LEN_W": "LEN_WIDTH", - }, - "descr": "AXI interface", - }, - ], - "blocks": [ - { - "core_name": "iob_ram_sp", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/axi_ram/hardware/src/axi_ram.v b/lib/hardware/memories/axi_ram/hardware/src/axi_ram.v deleted file mode 100644 index 8e932b078..000000000 --- a/lib/hardware/memories/axi_ram/hardware/src/axi_ram.v +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Alex Forencich -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -/* -Changes made (2023 Pedro Antunes): -- formated the code with Verible; -- removed intialization in "reg" type signals declaration; -- added reset to registers that did not previously have a reset value; -- separated memory writes and reads from the registers always block -- added support for memories that can not read and write at the same time -*/ - -// Language: Verilog 2001 - -`timescale 1ns / 1ps - -/* - * AXI4 RAM - */ -module axi_ram #( - parameter DATA_WIDTH = 32, - parameter ADDR_WIDTH = 16, - parameter STRB_WIDTH = (DATA_WIDTH / 8), - parameter READ_ON_WRITE = 1, - // Width of AXI signals - parameter ID_WIDTH = 8, - parameter LEN_WIDTH = 8, - // Extra pipeline register on output - parameter PIPELINE_OUTPUT = 0, - // File with which to preload RAM - parameter FILE = "none" -) ( - input wire clk_i, - input wire rst_i, - - input wire [ ID_WIDTH-1:0] axi_awid_i, - input wire [ADDR_WIDTH-1:0] axi_awaddr_i, - input wire [ LEN_WIDTH-1:0] axi_awlen_i, - input wire [ 2:0] axi_awsize_i, - input wire [ 1:0] axi_awburst_i, - input wire [ 1:0] axi_awlock_i, - input wire [ 3:0] axi_awcache_i, - input wire [ 2:0] axi_awprot_i, - input wire [ 3:0] axi_awqos_i, - input wire axi_awvalid_i, - output wire axi_awready_o, - - input wire [DATA_WIDTH-1:0] axi_wdata_i, - input wire [STRB_WIDTH-1:0] axi_wstrb_i, - input wire axi_wlast_i, - input wire axi_wvalid_i, - output wire axi_wready_o, - - output wire [ ID_WIDTH-1:0] axi_bid_o, - output wire [ 1:0] axi_bresp_o, - output wire axi_bvalid_o, - input wire axi_bready_i, - input wire [ ID_WIDTH-1:0] axi_arid_i, - input wire [ADDR_WIDTH-1:0] axi_araddr_i, - input wire [ LEN_WIDTH-1:0] axi_arlen_i, - input wire [ 2:0] axi_arsize_i, - input wire [ 1:0] axi_arburst_i, - input wire [ 1:0] axi_arlock_i, - input wire [ 3:0] axi_arcache_i, - input wire [ 2:0] axi_arprot_i, - input wire [ 3:0] axi_arqos_i, - input wire axi_arvalid_i, - output wire axi_arready_o, - output wire [ ID_WIDTH-1:0] axi_rid_o, - output wire [DATA_WIDTH-1:0] axi_rdata_o, - output wire [ 1:0] axi_rresp_o, - output wire axi_rlast_o, - output wire axi_rvalid_o, - input wire axi_rready_i -); - - localparam VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); - localparam WORD_WIDTH = STRB_WIDTH; - localparam WORD_SIZE = DATA_WIDTH / WORD_WIDTH; - - // bus width assertions - initial begin - if (WORD_SIZE * STRB_WIDTH != DATA_WIDTH) begin - $error("Error: AXI data width not evenly divisble (instance %m)"); - $finish(); - end - - if (2 ** $clog2(WORD_WIDTH) != WORD_WIDTH) begin - $error("Error: AXI word width must be even power of two (instance %m)"); - $finish(); - end - end - - localparam [0:0] READ_STATE_IDLE = 1'd0, READ_STATE_BURST = 1'd1; - - reg [0:0] read_state_reg, read_state_next; - - localparam [1:0] WRITE_STATE_IDLE = 2'd0, WRITE_STATE_BURST = 2'd1, WRITE_STATE_RESP = 2'd2; - - reg [1:0] write_state_reg, write_state_next; - - reg mem_wr_en; - - reg [ID_WIDTH-1:0] read_id_reg, read_id_next; - reg [ADDR_WIDTH-1:0] read_addr_reg, read_addr_next; - reg [LEN_WIDTH-1:0] read_count_reg, read_count_next; - reg [2:0] read_size_reg, read_size_next; - reg [1:0] read_burst_reg, read_burst_next; - reg [ID_WIDTH-1:0] write_id_reg, write_id_next; - reg [ADDR_WIDTH-1:0] write_addr_reg, write_addr_next; - reg [LEN_WIDTH-1:0] write_count_reg, write_count_next; - reg [2:0] write_size_reg, write_size_next; - reg [1:0] write_burst_reg, write_burst_next; - - reg axi_awready_reg, axi_awready_next; - reg axi_wready_reg, axi_wready_next; - reg [ID_WIDTH-1:0] axi_bid_reg, axi_bid_next; - reg axi_bvalid_reg, axi_bvalid_next; - reg axi_arready_reg, axi_arready_next; - reg [ID_WIDTH-1:0] axi_rid_reg, axi_rid_next; - reg [DATA_WIDTH-1:0] axi_rdata_reg, axi_rdata_next; - reg axi_rlast_reg, axi_rlast_next; - reg axi_rvalid_reg, axi_rvalid_next; - reg [ID_WIDTH-1:0] axi_rid_pipe_reg; - reg [DATA_WIDTH-1:0] axi_rdata_pipe_reg; - reg axi_rlast_pipe_reg; - reg axi_rvalid_pipe_reg; - - // (* RAM_STYLE="BLOCK" *) - reg [DATA_WIDTH-1:0] mem[2**VALID_ADDR_WIDTH-1:0]; - - wire [VALID_ADDR_WIDTH-1:0] axi_awaddr_valid = axi_awaddr_i >> (ADDR_WIDTH - VALID_ADDR_WIDTH); - wire [VALID_ADDR_WIDTH-1:0] axi_araddr_valid = axi_araddr_i >> (ADDR_WIDTH - VALID_ADDR_WIDTH); - wire [VALID_ADDR_WIDTH-1:0] read_addr_valid = read_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH); - wire [VALID_ADDR_WIDTH-1:0] write_addr_valid = write_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH); - - assign axi_awready_o = axi_awready_reg; - assign axi_wready_o = axi_wready_reg; - assign axi_bid_o = axi_bid_reg; - assign axi_bresp_o = 2'b00; - assign axi_bvalid_o = axi_bvalid_reg; - assign axi_arready_o = axi_arready_reg; - assign axi_rid_o = PIPELINE_OUTPUT ? axi_rid_pipe_reg : axi_rid_reg; - assign axi_rdata_o = PIPELINE_OUTPUT ? axi_rdata_pipe_reg : axi_rdata_reg; - assign axi_rresp_o = 2'b00; - assign axi_rlast_o = PIPELINE_OUTPUT ? axi_rlast_pipe_reg : axi_rlast_reg; - assign axi_rvalid_o = PIPELINE_OUTPUT ? axi_rvalid_pipe_reg : axi_rvalid_reg; - - generate - genvar i; - if (READ_ON_WRITE) begin : g_always_read - localparam mem_init_file_int = {FILE, ".hex"}; - initial begin - if (FILE != "none") begin - $readmemh(mem_init_file_int, mem, 0, 2 ** VALID_ADDR_WIDTH - 1); - end - end - for (i = 0; i < WORD_WIDTH; i = i + 1) begin : g_Bytes_in_word - always @(posedge clk_i) begin - if (mem_wr_en & axi_wstrb_i[i]) begin - mem[write_addr_valid][WORD_SIZE*i+:WORD_SIZE] <= axi_wdata_i[WORD_SIZE*i+:WORD_SIZE]; - end - axi_rdata_reg[WORD_SIZE*i+:WORD_SIZE] <= mem[read_addr_valid][WORD_SIZE*i+:WORD_SIZE]; - end - end - end else begin : g_no_read_on_write - localparam file_suffix = {"7", "6", "5", "4", "3", "2", "1", "0"}; - wire [VALID_ADDR_WIDTH-1:0] ram_addr_valid; - wire [ WORD_WIDTH-1:0] ram_en; - assign ram_addr_valid = mem_wr_en ? write_addr_valid : read_addr_valid; - for (i = 0; i < WORD_WIDTH; i = i + 1) begin : g_Bytes_in_word - localparam mem_init_file_int = (FILE != "none") ? - {FILE, "_", file_suffix[8*(i+1)-1-:8], ".hex"} : "none"; - assign ram_en[i] = mem_wr_en ? axi_wstrb_i[i] : 1'b1; - - iob_ram_sp #( - .HEXFILE(mem_init_file_int), - .ADDR_W (VALID_ADDR_WIDTH), - .DATA_W (WORD_SIZE) - ) ram ( - .clk_i(clk_i), - - .en_i (ram_en[i]), - .addr_i(ram_addr_valid), - .d_i (axi_wdata_i[WORD_SIZE*i+:WORD_SIZE]), - .we_i (mem_wr_en), - .d_o (axi_rdata_reg[WORD_SIZE*i+:WORD_SIZE]) - ); - end - end - endgenerate - - // always_comb in SystemVerilog - always @(*) begin - write_state_next = WRITE_STATE_IDLE; - - mem_wr_en = 1'b0; - - write_id_next = write_id_reg; - write_addr_next = write_addr_reg; - write_count_next = write_count_reg; - write_size_next = write_size_reg; - write_burst_next = write_burst_reg; - - axi_awready_next = 1'b0; - axi_wready_next = 1'b0; - axi_bid_next = axi_bid_reg; - axi_bvalid_next = axi_bvalid_reg && !axi_bready_i; - - case (write_state_reg) - WRITE_STATE_IDLE: begin - axi_awready_next = 1'b1; - - if (axi_awready_o && axi_awvalid_i) begin - write_id_next = axi_awid_i; - write_addr_next = axi_awaddr_i; - write_count_next = axi_awlen_i; - write_size_next = axi_awsize_i < $clog2(STRB_WIDTH) ? axi_awsize_i : - $clog2(STRB_WIDTH); - write_burst_next = axi_awburst_i; - - axi_awready_next = 1'b0; - axi_wready_next = 1'b1; - write_state_next = WRITE_STATE_BURST; - end else begin - write_state_next = WRITE_STATE_IDLE; - end - end - WRITE_STATE_BURST: begin - axi_wready_next = 1'b1; - - if (axi_wready_o && axi_wvalid_i) begin - mem_wr_en = 1'b1; - if (write_burst_reg != 2'b00) begin - write_addr_next = write_addr_reg + (1 << write_size_reg); - end - write_count_next = write_count_reg - 1; - if (write_count_reg > 0) begin - write_state_next = WRITE_STATE_BURST; - end else begin - axi_wready_next = 1'b0; - if (axi_bready_i || !axi_bvalid_o) begin - axi_bid_next = write_id_reg; - axi_bvalid_next = 1'b1; - axi_awready_next = 1'b1; - write_state_next = WRITE_STATE_IDLE; - end else begin - write_state_next = WRITE_STATE_RESP; - end - end - end else begin - write_state_next = WRITE_STATE_BURST; - end - end - WRITE_STATE_RESP: begin - if (axi_bready_i || !axi_bvalid_o) begin - axi_bid_next = write_id_reg; - axi_bvalid_next = 1'b1; - axi_awready_next = 1'b1; - write_state_next = WRITE_STATE_IDLE; - end else begin - write_state_next = WRITE_STATE_RESP; - end - end - default: ; - endcase - end - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - write_state_reg <= WRITE_STATE_IDLE; - axi_awready_reg <= 1'b0; - axi_wready_reg <= 1'b0; - axi_bvalid_reg <= 1'b0; - - write_id_reg <= {ID_WIDTH{1'b0}}; - write_addr_reg <= {ADDR_WIDTH{1'b0}}; - write_count_reg <= {LEN_WIDTH{1'b0}}; - write_size_reg <= 3'd0; - write_burst_reg <= 2'd0; - - axi_bid_reg <= {ID_WIDTH{1'b0}}; - end else begin - write_state_reg <= write_state_next; - axi_awready_reg <= axi_awready_next; - axi_wready_reg <= axi_wready_next; - axi_bvalid_reg <= axi_bvalid_next; - - write_id_reg <= write_id_next; - write_addr_reg <= write_addr_next; - write_count_reg <= write_count_next; - write_size_reg <= write_size_next; - write_burst_reg <= write_burst_next; - - axi_bid_reg <= axi_bid_next; - end - end - - // always_comb in SystemVerilog - always @(*) begin - read_state_next = READ_STATE_IDLE; - - axi_rid_next = axi_rid_reg; - axi_rlast_next = axi_rlast_reg; - axi_rvalid_next = axi_rvalid_reg && !(axi_rready_i || (PIPELINE_OUTPUT && !axi_rvalid_pipe_reg)); - - read_id_next = read_id_reg; - read_addr_next = read_addr_reg; - read_count_next = read_count_reg; - read_size_next = read_size_reg; - read_burst_next = read_burst_reg; - - axi_arready_next = 1'b0; - - case (read_state_reg) - READ_STATE_IDLE: begin - axi_arready_next = 1'b1; - - if (axi_arready_o && axi_arvalid_i) begin - read_id_next = axi_arid_i; - read_addr_next = axi_araddr_i; - read_count_next = axi_arlen_i; - read_size_next = axi_arsize_i < $clog2(STRB_WIDTH) ? axi_arsize_i : - $clog2(STRB_WIDTH); - read_burst_next = axi_arburst_i; - - axi_arready_next = 1'b0; - read_state_next = READ_STATE_BURST; - end else begin - read_state_next = READ_STATE_IDLE; - end - end - READ_STATE_BURST: begin - if (axi_rready_i || (PIPELINE_OUTPUT && !axi_rvalid_pipe_reg) || !axi_rvalid_reg) begin - axi_rvalid_next = 1'b1; - axi_rid_next = read_id_reg; - axi_rlast_next = read_count_reg == 0; - if (read_burst_reg != 2'b00) begin - read_addr_next = read_addr_reg + (1 << read_size_reg); - end - read_count_next = read_count_reg - 1; - if (read_count_reg > 0) begin - read_state_next = READ_STATE_BURST; - end else begin - axi_arready_next = 1'b1; - read_state_next = READ_STATE_IDLE; - end - end else begin - read_state_next = READ_STATE_BURST; - end - end // case: READ_STATE_BURST - default: ; - endcase - end - - always @(posedge clk_i, posedge rst_i) begin - if (rst_i) begin - read_state_reg <= READ_STATE_IDLE; - axi_arready_reg <= 1'b0; - axi_rvalid_reg <= 1'b0; - axi_rvalid_pipe_reg <= 1'b0; - - read_id_reg <= {ID_WIDTH{1'b0}}; - read_addr_reg <= {ADDR_WIDTH{1'b0}}; - read_count_reg <= 0; - read_size_reg <= 3'd0; - read_burst_reg <= 2'd0; - - axi_rid_reg <= {ID_WIDTH{1'b0}}; - axi_rlast_reg <= 1'b0; - axi_rid_pipe_reg <= {ID_WIDTH{1'b0}}; - axi_rdata_pipe_reg <= {DATA_WIDTH{1'b0}}; - axi_rlast_pipe_reg <= 1'b0; - end else begin - read_state_reg <= read_state_next; - axi_arready_reg <= axi_arready_next; - axi_rvalid_reg <= axi_rvalid_next; - - if (!axi_rvalid_pipe_reg || axi_rready_i) begin - axi_rvalid_pipe_reg <= axi_rvalid_reg; - end - - read_id_reg <= read_id_next; - read_addr_reg <= read_addr_next; - read_count_reg <= read_count_next; - read_size_reg <= read_size_next; - read_burst_reg <= read_burst_next; - - axi_rid_reg <= axi_rid_next; - axi_rlast_reg <= axi_rlast_next; - - if (!axi_rvalid_pipe_reg || axi_rready_i) begin - axi_rid_pipe_reg <= axi_rid_reg; - axi_rdata_pipe_reg <= axi_rdata_reg; - axi_rlast_pipe_reg <= axi_rlast_reg; - end - end - end - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_2p/hardware/simulation/src/iob_ram_2p_tb.v b/lib/hardware/memories/ram/iob_ram_2p/hardware/simulation/src/iob_ram_2p_tb.v deleted file mode 100644 index f0f012c1d..000000000 --- a/lib/hardware/memories/ram/iob_ram_2p/hardware/simulation/src/iob_ram_2p_tb.v +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`define ADDR_W 10 -`define DATA_W 32 - -module iob_ram_2p_tb; - - // Inputs - reg clk; - - // Write signals - reg w_en; - reg [`DATA_W-1:0] w_data; - reg [`ADDR_W-1:0] w_addr; - wire w_ready; - - - // Read signals - reg r_en; - reg [`ADDR_W-1:0] r_addr; - wire [`DATA_W-1:0] r_data; - wire r_ready; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - clk = 1; - r_en = 0; - w_en = 0; - r_addr = 0; - w_addr = 0; - w_data = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - @(posedge clk) #1; - w_en = 1; - - // Write all the locations of RAM - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - w_data = i + seq_ini; - w_addr = i; - // wait for w_ready - while (!w_ready) begin - @(posedge clk) #1; - end - @(posedge clk) #1; - end - - w_en = 0; - @(posedge clk) #1; - - // Read all the locations of RAM with r_en = 0 - r_en = 0; - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - // wait for r_ready - while (!r_ready) begin - @(posedge clk) #1; - end - @(posedge clk) #1; - if (r_data != 0) begin - $display("ERROR: with r_en = 0, at position %0d, r_data should be 0 but is %d", i, - r_data); - $fatal(1); - end - end - - r_en = 1; - @(posedge clk) #1; - - // Read all the locations of RAM with r_en = 1 - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - // wait for r_ready - while (!r_ready) begin - @(posedge clk) #1; - end - @(posedge clk) #1; - if (r_data != i + seq_ini) begin - $display("ERROR: on position %0d, r_data is %d where it should be %0d", i, r_data, - i + seq_ini); - $fatal(1); - end - end - - r_en = 0; - - #(5 * clk_per); - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_2p #( - .WRITE_FIRST(1), - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i(clk), - - .w_en_i (w_en), - .w_addr_i(w_addr), - .w_data_i(w_data), - .w_ready_o(w_ready), - - .r_en_i (r_en), - .r_addr_i(r_addr), - .r_data_o(r_data), - .r_ready_o(r_ready) - - ); - - // Clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_2p/iob_ram_2p.py b/lib/hardware/memories/ram/iob_ram_2p/iob_ram_2p.py deleted file mode 100644 index 9f5deaed4..000000000 --- a/lib/hardware/memories/ram/iob_ram_2p/iob_ram_2p.py +++ /dev/null @@ -1,174 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "DATA_W", - "type": "P", - "val": "0", - "min": "NA", - "max": "NA", - "descr": "DATA width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "0", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "WRITE_FIRST", - "type": "P", - "val": "1", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_en_i", - "descr": "Input port", - "signals": [ - {"name": "w_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_addr_i", - "descr": "Input port", - "signals": [ - {"name": "w_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "w_data_i", - "descr": "Input port", - "signals": [ - {"name": "w_data", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "w_ready_o", - "descr": "Output port", - "signals": [ - {"name": "w_ready", "width": 1, "direction": "output"}, - ], - }, - { - "name": "r_en_i", - "descr": "Input port", - "signals": [ - {"name": "r_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_addr_i", - "descr": "Input port", - "signals": [ - {"name": "r_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_data_o", - "descr": "Output port", - "signals": [ - {"name": "r_data", "width": "DATA_W", "direction": "output"}, - ], - }, - { - "name": "r_ready_o", - "descr": "Output port", - "signals": [ - {"name": "r_ready", "width": 1, "direction": "output"}, - ], - }, - ], - "wires": [ - { - "name": "en_int", - "descr": "en wire", - "signals": [ - {"name": "en_in", "width": 1}, - ], - }, - { - "name": "we_int", - "descr": "we wire", - "signals": [ - {"name": "we_int", "width": 1}, - ], - }, - { - "name": "addr_int", - "descr": "addr wire", - "signals": [ - {"name": "addr_int", "width": "ADDR_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_ram_sp", - "instance_name": "iob_ram_sp_inst", - "parameters": { - "HEXFILE": "HEXFILE", - "DATA_W": "DATA_W", - "ADDR_W": "ADDR_W", - }, - "connect": { - "clk_i": "clk_i", - "en_i": "en_int", - "we_i": "we_int", - "addr_i": "addr_int", - "d_i": "w_data_i", - "d_o": "r_data_o", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - generate - if (WRITE_FIRST) begin : write_first - assign en_int = w_en_i | r_en_i; - assign we_int = w_en_i; - assign addr_int = w_en_i ? w_addr_i : r_addr_i; - assign w_ready_o = 1'b1; - assign r_ready_o = ~w_en_i; - end else begin : read_first - assign en_int = w_en_i | r_en_i; - assign we_int = w_en_i & (~r_en_i); - assign addr_int = r_en_i ? r_addr_i : w_addr_i; - assign w_ready_o = ~r_en_i; - assign r_ready_o = 1'b1; - end - endgenerate - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_at2p/hardware/simulation/src/iob_ram_at2p_tb.v b/lib/hardware/memories/ram/iob_ram_at2p/hardware/simulation/src/iob_ram_at2p_tb.v deleted file mode 100644 index 344e80846..000000000 --- a/lib/hardware/memories/ram/iob_ram_at2p/hardware/simulation/src/iob_ram_at2p_tb.v +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_ram_at2p_tb; - - // Inputs - reg w_clk; - reg r_clk; - - // Write signals - reg w_en; - reg [`DATA_W-1:0] w_data; - reg [`ADDR_W-1:0] w_addr; - - - // Read signals - reg r_en; - reg [`ADDR_W-1:0] r_addr; - wire [`DATA_W-1:0] r_data; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - w_clk = 1; - r_clk = 1; - r_en = 0; - w_en = 0; - r_addr = 0; - w_addr = 0; - w_data = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - @(posedge w_clk) #1; - @(posedge r_clk) #1; - w_en = 1; - - // Write all the locations of RAM - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - w_data = i + seq_ini; - w_addr = i; - @(posedge w_clk) #1; - end - - w_en = 0; - @(posedge r_clk) #1; - - // Read all the locations of RAM with r_en = 0 - r_en = 0; - @(posedge r_clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - @(posedge r_clk) #1; - if (r_data != 0) begin - $display("ERROR: with r_en = 0, at position %0d, r_data should be 0 but is %d", i, - r_data); - $finish(); - end - end - - r_en = 1; - @(posedge r_clk) #1; - - // Read all the locations of RAM with r_en = 1 - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - @(posedge r_clk) #1; - if (r_data != i + seq_ini) begin - $display("ERROR: on position %0d, r_data is %d where it should be %0d", i, r_data, - i + seq_ini); - $finish(); - end - end - - r_en = 0; - - #(5 * clk_per); - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_at2p #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .w_clk_i (w_clk), - .w_en_i (w_en), - .w_addr_i(w_addr), - .w_data_i(w_data), - - .r_clk_i (r_clk), - .r_en_i (r_en), - .r_addr_i(r_addr), - .r_data_o(r_data) - ); - - // Clock - always #(clk_per / 2) w_clk = ~w_clk; - always #(clk_per / 2) r_clk = ~r_clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_at2p/iob_ram_at2p.py b/lib/hardware/memories/ram/iob_ram_at2p/iob_ram_at2p.py deleted file mode 100644 index 6dca17c81..000000000 --- a/lib/hardware/memories/ram/iob_ram_at2p/iob_ram_at2p.py +++ /dev/null @@ -1,134 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "DATA_W", - "type": "P", - "val": "0", - "min": "NA", - "max": "NA", - "descr": "DATA width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "0", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "w_clk_i", - "descr": "Input port", - "signals": [ - {"name": "w_clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_en_i", - "descr": "Input port", - "signals": [ - {"name": "w_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_addr_i", - "descr": "Input port", - "signals": [ - {"name": "w_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "w_data_i", - "descr": "Input port", - "signals": [ - {"name": "w_data", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "r_clk_i", - "descr": "Input port", - "signals": [ - {"name": "r_clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_en_i", - "descr": "Input port", - "signals": [ - {"name": "r_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_addr_i", - "descr": "Input port", - "signals": [ - {"name": "r_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_data_o", - "descr": "Output port", - "signals": [ - {"name": "r_data", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Declare the RAM - reg [DATA_W-1:0] ram[(2**ADDR_W)-1:0]; - reg [DATA_W-1:0] r_data_o_reg; - assign r_data_o=r_data_o_reg; - - // Initialize the RAM - initial begin - if (MEM_INIT_FILE_INT != "none") begin - $readmemh(MEM_INIT_FILE_INT, ram, 0, (2 ** ADDR_W) - 1); - end - end - - //write - always @(posedge w_clk_i) begin - if (w_en_i) begin - ram[w_addr_i] <= w_data_i; - end - end - - //read - always @(posedge r_clk_i) begin - if (r_en_i) begin - r_data_o_reg <= ram[r_addr_i]; - end - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_atdp/hardware/simulation/src/iob_ram_atdp_tb.v b/lib/hardware/memories/ram/iob_ram_atdp/hardware/simulation/src/iob_ram_atdp_tb.v deleted file mode 100644 index b689f7524..000000000 --- a/lib/hardware/memories/ram/iob_ram_atdp/hardware/simulation/src/iob_ram_atdp_tb.v +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_ram_atdp_tb; - - // Inputs - reg clkA; - reg [`DATA_W-1:0] data_a; - reg [`ADDR_W-1:0] addr_a; - reg en_a; - reg we_a; - - reg clkB; - reg [`DATA_W-1:0] data_b; - reg [`ADDR_W-1:0] addr_b; - reg en_b; - reg we_b; - - // Outputs - reg [`DATA_W-1:0] q_a; - reg [`DATA_W-1:0] q_b; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clkA = 1; - clkB = 1; - - data_a = 0; - addr_a = 0; - en_a = 0; - we_a = 0; - - data_b = 0; - addr_b = 0; - en_b = 0; - we_b = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - #clk_per; - @(posedge clkA) #1; - @(posedge clkB) #1; - - @(posedge clkA) #1; - en_a = 1; - - // Write into port A and read from it - @(posedge clkA) #1; - we_a = 1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_a = i; - data_a = i + seq_ini; - @(posedge clkA) #1; - end - - @(posedge clkA) #1; - we_a = 0; - - @(posedge clkA) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_a = i; - @(posedge clkA) #1; - if (i + seq_ini != q_a) begin - $display("ERROR: write error in port A position %d, where data=%h but q_a=%h", i, - i + seq_ini, q_a); - $finish(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 64; - - @(posedge clkB) #1; - en_b = 1; - - // Write into port B and read from it - @(posedge clkB) #1; - we_b = 1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_b = i; - data_b = i + seq_ini; - @(posedge clkB) #1; - end - - @(posedge clkB) #1; - we_b = 0; - - @(posedge clkB) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_b = i; - @(posedge clkB) #1; - if (i + seq_ini != q_b) begin - $display("ERROR: write error in port B position %d, where data=%h but q_b=%h", i, - i + seq_ini, q_b); - $finish(); - end - end - - @(posedge clkB) #1; - en_a = 0; - en_b = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_atdp #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clkA_i (clkA), - .dA_i (data_a), - .addrA_i(addr_a), - .enA_i (en_a), - .weA_i (we_a), - .dA_o (q_a), - - .clkB_i (clkB), - .dB_i (data_b), - .addrB_i(addr_b), - .enB_i (en_b), - .weB_i (we_b), - .dB_o (q_b) - ); - - // system clock - always #(clk_per / 2) clkA = ~clkA; - always #(clk_per / 2) clkB = ~clkB; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_atdp/iob_ram_atdp.py b/lib/hardware/memories/ram/iob_ram_atdp/iob_ram_atdp.py deleted file mode 100644 index b409f7d00..000000000 --- a/lib/hardware/memories/ram/iob_ram_atdp/iob_ram_atdp.py +++ /dev/null @@ -1,170 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "DATA_W", - "type": "P", - "val": "0", - "min": "NA", - "max": "NA", - "descr": "DATA width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "0", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clkA_i", - "descr": "Input port", - "signals": [ - {"name": "clkA", "width": 1, "direction": "input"}, - ], - }, - { - "name": "dA_i", - "descr": "Input port", - "signals": [ - {"name": "dA", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "addrA_i", - "descr": "Input port", - "signals": [ - {"name": "addrA", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "enA_i", - "descr": "Input port", - "signals": [ - {"name": "enA", "width": 1, "direction": "input"}, - ], - }, - { - "name": "weA_i", - "descr": "Input port", - "signals": [ - {"name": "weA", "width": 1, "direction": "input"}, - ], - }, - { - "name": "dA_o", - "descr": "Output port", - "signals": [ - {"name": "dA", "width": "DATA_W", "direction": "output"}, - ], - }, - { - "name": "clkB_i", - "descr": "Input port", - "signals": [ - {"name": "clkB", "width": 1, "direction": "input"}, - ], - }, - { - "name": "dB_i", - "descr": "Input port", - "signals": [ - {"name": "dB", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "addrB_i", - "descr": "Input port", - "signals": [ - {"name": "addrB", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "enB_i", - "descr": "Input port", - "signals": [ - {"name": "enB", "width": 1, "direction": "input"}, - ], - }, - { - "name": "weB_i", - "descr": "Input port", - "signals": [ - {"name": "weB", "width": 1, "direction": "input"}, - ], - }, - { - "name": "dB_o", - "descr": "Output port", - "signals": [ - {"name": "dB", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Declare the RAM - reg [DATA_W-1:0] ram[2**ADDR_W-1:0]; - reg [DATA_W-1:0] dA_o_reg; - reg [DATA_W-1:0] dB_o_reg; - assign dA_o=dA_o_reg; - assign dB_o=dB_o_reg; - - // Initialize the RAM - initial if (MEM_INIT_FILE_INT != "none") $readmemh(MEM_INIT_FILE_INT, ram, 0, 2 ** ADDR_W - 1); - - //read port - always @(posedge clkA_i) begin // Port A - if (enA_i) -`ifdef IOB_MEM_NO_READ_ON_WRITE - if (weA_i) ram[addrA_i] <= dA_i; - else dA_o_reg <= ram[addrA_i]; -`else - if (weA_i) ram[addrA_i] <= dA_i; - dA_o_reg <= ram[addrA_i]; -`endif - end - - //write port - always @(posedge clkB_i) begin // Port B - if (enB_i) -`ifdef IOB_MEM_NO_READ_ON_WRITE - if (weB_i) ram[addrB_i] <= dB_i; - else dB_o_reg <= ram[addrB_i]; -`else - if (weB_i) ram[addrB_i] <= dB_i; - dB_o_reg <= ram[addrB_i]; -`endif - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_atdp_be/hardware/simulation/src/iob_ram_atdp_be_tb.v b/lib/hardware/memories/ram/iob_ram_atdp_be/hardware/simulation/src/iob_ram_atdp_be_tb.v deleted file mode 100644 index d295abe6c..000000000 --- a/lib/hardware/memories/ram/iob_ram_atdp_be/hardware/simulation/src/iob_ram_atdp_be_tb.v +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 32 -`define ADDR_W 4 - -module iob_ram_atdp_be_tb; - - // Inputs - reg clkA; - reg enaA; // enable access to ram - reg [`DATA_W/8-1:0] weA; // write enable vector - reg [ `ADDR_W-1:0] addrA; - reg [ `DATA_W-1:0] data_inA; - - reg clkB; - reg enaB; // enable access to ram - reg [`DATA_W/8-1:0] weB; // write enable vector - reg [ `ADDR_W-1:0] addrB; - reg [ `DATA_W-1:0] data_inB; - - // Ouptuts - reg [ `DATA_W-1:0] data_outA; - reg [ `DATA_W-1:0] data_outB; - - integer i, seq_ini, first_seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clkA = 1; - clkB = 1; - enaA = 0; - enaB = 0; - for (i = 0; i < `DATA_W / 8; i = i + 1) begin - weA[i] = 0; - weB[i] = 0; - end - addrA = 0; - addrB = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - first_seq_ini = seq_ini; - - #clk_per; - @(posedge clkA) #1; - @(posedge clkB) #1; - - - @(posedge clkA) #1; - enaA = 1; - - // Write into RAM port A in all positions and read from it - @(posedge clkA) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - weA[i] = 1; - @(posedge clkA) #1; - addrA = i; - data_inA = i + seq_ini; - @(posedge clkA) #1; - end - - @(posedge clkA) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) weA[i] = 0; - - @(posedge clkA) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrA = i; - @(posedge clkA) #1; - if (i + seq_ini != data_outA) begin - $display("ERROR: write error in port A position %d, where data=%h but data_outA=%h", i, - i + seq_ini, data_outA); - $finish(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 64; - - @(posedge clkB) #1; - enaB = 1; - - // Write into RAM port B in all positions and read from it - @(posedge clkB) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - weB[i] = 1; - @(posedge clkB) #1; - addrB = i; - data_inB = i + seq_ini; - @(posedge clkB) #1; - end - - @(posedge clkB) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) weB[i] = 0; - - @(posedge clkB) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrB = i; - @(posedge clkB) #1; - if (i + seq_ini != data_outB) begin - $display("ERROR: write error in port B position %d, where data=%h but data_outB=%h", i, - i + seq_ini, data_outB); - $finish(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = first_seq_ini; - - // Test if output is truly different - // Port A - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrA = i; - @(posedge clkA) #1; - if (i + seq_ini == data_outA) begin - $display( - "ERROR: read error in port A position %d, where data and data_outA are '%h' but should not be the same", - i, data_outA); - $finish(); - end - end - - @(posedge clkA) #1; - enaA = 0; - - // Port B - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrB = i; - @(posedge clkB) #1; - if (i + seq_ini == data_outB) begin - $display( - "ERROR: read error in port B position %d, where data and data_outB are '%h' but should not be the same", - i, data_outB); - $finish(); - end - end - - @(posedge clkB) #1; - enaB = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_atdp_be #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clkA_i (clkA), - .enA_i (enaA), - .weA_i (weA), - .addrA_i(addrA), - .dA_i (data_inA), - .dA_o (data_outA), - - .clkB_i (clkB), - .enB_i (enaB), - .weB_i (weB), - .addrB_i(addrB), - .dB_i (data_inB), - .dB_o (data_outB) - ); - - // system clock - always #(clk_per / 2) clkA = ~clkA; - always #(clk_per / 2) clkB = ~clkB; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_atdp_be/iob_ram_atdp_be.py b/lib/hardware/memories/ram/iob_ram_atdp_be/iob_ram_atdp_be.py deleted file mode 100644 index d7902410f..000000000 --- a/lib/hardware/memories/ram/iob_ram_atdp_be/iob_ram_atdp_be.py +++ /dev/null @@ -1,225 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "DATA_W", - "type": "P", - "val": "10", - "min": "NA", - "max": "NA", - "descr": "DATA width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "COL_W", - "type": "F", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "NUM_COL", - "type": "F", - "val": "DATA_W / COL_W", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clkA_i", - "descr": "Input port", - "signals": [ - {"name": "clkA", "width": 1, "direction": "input"}, - ], - }, - { - "name": "enA_i", - "descr": "Input port", - "signals": [ - {"name": "enA", "width": 1, "direction": "input"}, - ], - }, - { - "name": "weA_i", - "descr": "Input port", - "signals": [ - {"name": "weA", "width": "DATA_W/8", "direction": "input"}, - ], - }, - { - "name": "addrA_i", - "descr": "Input port", - "signals": [ - {"name": "addrA", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "dA_i", - "descr": "Input port", - "signals": [ - {"name": "dA", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "dA_o", - "descr": "Output port", - "signals": [ - {"name": "dA", "width": "DATA_W", "direction": "output"}, - ], - }, - { - "name": "clkB_i", - "descr": "Input port", - "signals": [ - {"name": "clkB", "width": 1, "direction": "input"}, - ], - }, - { - "name": "enB_i", - "descr": "Input port", - "signals": [ - {"name": "enB", "width": 1, "direction": "input"}, - ], - }, - { - "name": "weB_i", - "descr": "Input port", - "signals": [ - {"name": "weB", "width": "DATA_W/8", "direction": "input"}, - ], - }, - { - "name": "addrB_i", - "descr": "Input port", - "signals": [ - {"name": "addrB", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "dB_i", - "descr": "Input port", - "signals": [ - {"name": "dB", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "dB_o", - "descr": "Output port", - "signals": [ - {"name": "dB", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_ram_atdp", - "instantiate": False, - }, - ], - "snippets": [ - { - "verilog_code": """ - `ifdef IOB_MEM_NO_READ_ON_WRITE - localparam file_suffix = {"7", "6", "5", "4", "3", "2", "1", "0"}; - - genvar i; - generate - for (i = 0; i < NUM_COL; i = i + 1) begin : ram_col - localparam mem_init_file_int = (HEXFILE != "none") ? - {HEXFILE, "_", file_suffix[8*(i+1)-1-:8], ".hex"} : "none"; - - iob_ram_atdp #( - .HEXFILE(mem_init_file_int), - .ADDR_W (ADDR_W), - .DATA_W (COL_W) - ) ram ( - .clkA_i (clkA_i), - .enA_i (enA_i), - .addrA_i(addrA_i), - .dA_i (dA_i[i*COL_W+:COL_W]), - .weA_i (weA_i[i]), - .dA_o (dA_o[i*COL_W+:COL_W]), - - .clkB_i (clkB_i), - .enB_i (enB_i), - .addrB_i(addrB_i), - .dB_i (dB_i[i*COL_W+:COL_W]), - .weB_i (weB_i[i]), - .dB_o (dB_o[i*COL_W+:COL_W]) - ); - end - endgenerate -`else // !IOB_MEM_NO_READ_ON_WRITE - // this allow ISE 14.7 to work; do not remove - localparam mem_init_file_int = {HEXFILE, ".hex"}; - - // Core Memory - reg [DATA_W-1:0] ram_block[(2**ADDR_W)-1:0]; - - // Initialize the RAM - initial - if (mem_init_file_int != "none.hex") - $readmemh(mem_init_file_int, ram_block, 0, 2 ** ADDR_W - 1); - - // Port-A Operation - reg [DATA_W-1:0] dA_o_int; - integer i; - always @(posedge clkA_i) begin - if (enA_i) begin - for (i = 0; i < NUM_COL; i = i + 1) begin - if (weA_i[i]) begin - ram_block[addrA_i][i*COL_W+:COL_W] <= dA_i[i*COL_W+:COL_W]; - end - end - dA_o_int <= ram_block[addrA_i]; // Send Feedback - end - end - - assign dA_o = dA_o_int; - - // Port-B Operation - reg [DATA_W-1:0] dB_o_int; - integer j; - always @(posedge clkB_i) begin - if (enB_i) begin - for (j = 0; j < NUM_COL; j = j + 1) begin - if (weB_i[j]) begin - ram_block[addrB_i][j*COL_W+:COL_W] <= dB_i[j*COL_W+:COL_W]; - end - end - dB_o_int <= ram_block[addrB_i]; // Send Feedback - end - end - - assign dB_o = dB_o_int; -`endif - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_sp/hardware/simulation/src/iob_ram_sp_tb.v b/lib/hardware/memories/ram/iob_ram_sp/hardware/simulation/src/iob_ram_sp_tb.v deleted file mode 100644 index 9183831e7..000000000 --- a/lib/hardware/memories/ram/iob_ram_sp/hardware/simulation/src/iob_ram_sp_tb.v +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_ram_sp_tb; - - // Inputs - reg clk; - reg en; // enable access to ram - reg we; // write enable - reg [`ADDR_W-1:0] addr; - reg [`DATA_W-1:0] data_in; - - // Ouptuts - reg [`DATA_W-1:0] data_out; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk = 1; - en = 0; - we = 0; - addr = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - #clk_per; - @(posedge clk) #1; - en = 1; - - // Write to all RAM words - @(posedge clk) #1; - we = 1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - data_in = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - we = 0; - - // Read from all RAM words - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (i + seq_ini != data_out) begin - $display( - "ERROR: read error in data_out. \n \t i=%0d; data = %h when it should have been %0h", - i, i + seq_ini, data_out); - $fatal(); - end - end - - @(posedge clk) #1; - en = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_sp #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i (clk), - .en_i (en), - .we_i (we), - .addr_i(addr), - .d_i (data_in), - .d_o (data_out) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_sp/iob_ram_sp.py b/lib/hardware/memories/ram/iob_ram_sp/iob_ram_sp.py deleted file mode 100644 index d5d78ff9e..000000000 --- a/lib/hardware/memories/ram/iob_ram_sp/iob_ram_sp.py +++ /dev/null @@ -1,108 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "DATA_W", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "DATA width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "14", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "NA", - "max": "NA", - "descr": "Address bus width", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "en_i", - "descr": "Input port", - "signals": [ - {"name": "en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "we_i", - "descr": "Input port", - "signals": [ - {"name": "we", "width": 1, "direction": "input"}, - ], - }, - { - "name": "addr_i", - "descr": "Input port", - "signals": [ - {"name": "addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "d_o", - "descr": "Output port", - "signals": [ - {"name": "d", "width": "DATA_W", "direction": "output"}, - ], - }, - { - "name": "d_i", - "descr": "Input port", - "signals": [ - {"name": "d", "width": "DATA_W", "direction": "input"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Declare the RAM - reg [DATA_W-1:0] ram[2**ADDR_W-1:0]; - reg [DATA_W-1:0] d_o_reg; - assign d_o=d_o_reg; - - // Initialize the RAM - initial if (MEM_INIT_FILE_INT != "none") $readmemh(MEM_INIT_FILE_INT, ram, 0, 2 ** ADDR_W - 1); - - // Operate the RAM - always @(posedge clk_i) - if (en_i) - if (we_i) ram[addr_i] <= d_i; - else d_o_reg<= ram[addr_i]; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_sp_be/hardware/simulation/src/iob_ram_sp_be_tb.v b/lib/hardware/memories/ram/iob_ram_sp_be/hardware/simulation/src/iob_ram_sp_be_tb.v deleted file mode 100644 index 24a0d3cce..000000000 --- a/lib/hardware/memories/ram/iob_ram_sp_be/hardware/simulation/src/iob_ram_sp_be_tb.v +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 32 -`define ADDR_W 4 - -module iob_ram_sp_be_tb; - - // Inputs - reg clk; - reg en; // enable access to ram - reg [`DATA_W/8-1:0] we; // write enable vector - reg [ `ADDR_W-1:0] addr; - reg [ `DATA_W-1:0] data_in; - - // Ouptuts - reg [ `DATA_W-1:0] data_out; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk = 1; - en = 0; - for (i = 0; i < `DATA_W / 8; i = i + 1) we[i] = 0; - addr = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - #clk_per; - @(posedge clk) #1; - en = 1; - - // Write into RAM in all positions and read from it - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - we[i] = 1; - @(posedge clk) #1; - addr = i; - data_in = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) we = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (i + seq_ini != data_out) begin - $display("ERROR: write error in position %d, where data=%h but data_out=%h", i, - i + seq_ini, data_out); - $fatal(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 64; - - // Test if output is truly different - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (i + seq_ini == data_out) begin - $display( - "ERROR: read error in position %d, where data and data_out are '%h' but should not be the same", - i, data_out); - $fatal(); - end - end - - @(posedge clk) #1; - en = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_sp_be #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i (clk), - .en_i (en), - .we_i (we), - .addr_i(addr), - .d_i (data_in), - .d_o (data_out) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_sp_be/iob_ram_sp_be.py b/lib/hardware/memories/ram/iob_ram_sp_be/iob_ram_sp_be.py deleted file mode 100644 index 1298bc2fb..000000000 --- a/lib/hardware/memories/ram/iob_ram_sp_be/iob_ram_sp_be.py +++ /dev/null @@ -1,138 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "10", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "COL_W", - "type": "F", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "NUM_COL", - "type": "F", - "val": "DATA_W / COL_W", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "mem_if", - "descr": "Memory interface", - "signals": [ - {"name": "en", "width": 1, "direction": "input"}, - {"name": "we", "width": "DATA_W/8", "direction": "input"}, - {"name": "addr", "width": "ADDR_W", "direction": "input"}, - {"name": "d", "width": "DATA_W", "direction": "input"}, - {"name": "d", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_ram_sp", - "instantiate": False, - }, - ], - "snippets": [ - { - "verilog_code": """ - - // Operation -`ifdef IOB_MEM_NO_READ_ON_WRITE - localparam file_suffix = {"7", "6", "5", "4", "3", "2", "1", "0"}; - - genvar i; - generate - for (i = 0; i < NUM_COL; i = i + 1) begin : ram_col - localparam mem_init_file_int = (HEXFILE != "none") ? - {HEXFILE, "_", file_suffix[8*(i+1)-1-:8], ".hex"} : "none"; - - iob_ram_sp #( - .HEXFILE(mem_init_file_int), - .ADDR_W (ADDR_W), - .DATA_W (COL_W) - ) ram ( - .clk_i(clk_i), - - .en_i (en_i), - .addr_i(addr_i), - .d_i (d_i[i*COL_W+:COL_W]), - .we_i (we_i[i]), - .d_o (d_o[i*COL_W+:COL_W]) - ); - end - endgenerate -`else // !IOB_MEM_NO_READ_ON_WRITE - // this allows ISE 14.7 to work; do not remove - localparam mem_init_file_int = {HEXFILE, ".hex"}; - - // Core Memory - reg [DATA_W-1:0] ram_block[(2**ADDR_W)-1:0]; - - // Initialize the RAM - initial - if (mem_init_file_int != "none.hex") - $readmemh(mem_init_file_int, ram_block, 0, 2 ** ADDR_W - 1); - - reg [DATA_W-1:0] d_o_int; - integer i; - always @(posedge clk_i) begin - if (en_i) begin - for (i = 0; i < NUM_COL; i = i + 1) begin - if (we_i[i]) begin - ram_block[addr_i][i*COL_W+:COL_W] <= d_i[i*COL_W+:COL_W]; - end - end - d_o_int <= ram_block[addr_i]; // Send Feedback - end - end - - assign d_o = d_o_int; -`endif - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_sp_se/hardware/simulation/src/iob_ram_sp_se_tb.v b/lib/hardware/memories/ram/iob_ram_sp_se/hardware/simulation/src/iob_ram_sp_se_tb.v deleted file mode 100644 index b88c9ec0a..000000000 --- a/lib/hardware/memories/ram/iob_ram_sp_se/hardware/simulation/src/iob_ram_sp_se_tb.v +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 32 -`define ADDR_W 4 - -module iob_ram_sp_se_tb; - - // Inputs - reg clk; - reg en; // enable access to ram - reg [`DATA_W/8-1:0] we; // write enable vector - reg [ `ADDR_W-1:0] addr; - reg [ `DATA_W-1:0] data_in; - - // Ouptuts - reg [ `DATA_W-1:0] data_out; - - integer i, seq_ini; - integer fd; - - parameter CLK_PER = 10; // clk period = 10 timeticks - parameter COL_W = 8; - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk = 1; - en = 0; - for (i = 0; i < `DATA_W / 8; i = i + 1) we[i] = 0; - addr = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - #CLK_PER; - @(posedge clk) #1; - en = 1; - - // Write into RAM in all positions and read from it - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - we[i] = 1; - @(posedge clk) #1; - addr = i; - data_in = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) we = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (i + seq_ini != data_out) begin - $display("ERROR: write error in position %d, where data=%h but data_out=%h", i, - i + seq_ini, data_out); - $fatal(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 64; - - // Test if output is truly different - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (i + seq_ini == data_out) begin - $display( - "ERROR: read error in position %d, where data and data_out are '%h' but should not be the same", - i, data_out); - $fatal(); - end - end - - @(posedge clk) #1; - en = 0; - - #CLK_PER; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * CLK_PER) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_sp_se #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W), - .COL_W(COL_W) - ) uut ( - .clk_i (clk), - .en_i (en), - .we_i (we), - .addr_i(addr), - .d_i (data_in), - .d_o (data_out) - ); - - // system clock - always #(CLK_PER / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_sp_se/iob_ram_sp_se.py b/lib/hardware/memories/ram/iob_ram_sp_se/iob_ram_sp_se.py deleted file mode 100644 index b42cfffa6..000000000 --- a/lib/hardware/memories/ram/iob_ram_sp_se/iob_ram_sp_se.py +++ /dev/null @@ -1,139 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "10", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "COL_W", - "type": "P", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "NUM_COL", - "type": "F", - "val": "DATA_W / COL_W", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "mem_if", - "descr": "Memory interface", - "signals": [ - {"name": "en", "width": 1, "direction": "input"}, - {"name": "we", "width": "DATA_W/COL_W", "direction": "input"}, - {"name": "addr", "width": "ADDR_W", "direction": "input"}, - {"name": "d", "width": "DATA_W", "direction": "input"}, - {"name": "d", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_ram_sp", - "instantiate": False, - }, - ], - "snippets": [ - { - "verilog_code": """ - - - // Operation -`ifdef IOB_MEM_NO_READ_ON_WRITE - localparam file_suffix = {"7", "6", "5", "4", "3", "2", "1", "0"}; - - genvar i; - generate - for (i = 0; i < NUM_COL; i = i + 1) begin : ram_col - localparam mem_init_file_int = (HEXFILE != "none") ? - {HEXFILE, "_", file_suffix[COL_W*(i+1)-1-:COL_W], ".hex"} : "none"; - - iob_ram_sp #( - .HEXFILE(mem_init_file_int), - .ADDR_W (ADDR_W), - .DATA_W (COL_W) - ) ram ( - .clk_i(clk_i), - - .en_i (en_i), - .addr_i(addr_i), - .d_i (d_i[i*COL_W+:COL_W]), - .we_i (we_i[i]), - .d_o (d_o[i*COL_W+:COL_W]) - ); - end - endgenerate -`else // !IOB_MEM_NO_READ_ON_WRITE - // this allows ISE 14.7 to work; do not remove - localparam mem_init_file_int = {HEXFILE, ".hex"}; - - // Core Memory - reg [DATA_W-1:0] ram_block[(2**ADDR_W)-1:0]; - - // Initialize the RAM - initial - if (mem_init_file_int != "none.hex") - $readmemh(mem_init_file_int, ram_block, 0, 2 ** ADDR_W - 1); - - reg [DATA_W-1:0] d_o_int; - integer i; - always @(posedge clk_i) begin - if (en_i) begin - for (i = 0; i < NUM_COL; i = i + 1) begin - if (we_i[i]) begin - ram_block[addr_i][i*COL_W+:COL_W] <= d_i[i*COL_W+:COL_W]; - end - end - d_o_int <= ram_block[addr_i]; // Send Feedback - end - end - - assign d_o = d_o_int; -`endif - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_t2p/hardware/simulation/src/iob_ram_t2p_tb.v b/lib/hardware/memories/ram/iob_ram_t2p/hardware/simulation/src/iob_ram_t2p_tb.v deleted file mode 100644 index 6ba6026d2..000000000 --- a/lib/hardware/memories/ram/iob_ram_t2p/hardware/simulation/src/iob_ram_t2p_tb.v +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`define ADDR_W 10 -`define DATA_W 32 - -module iob_ram_t2p_tb; - - // Inputs - reg clk; - - // Write signals - reg w_en; - reg [`DATA_W-1:0] w_data; - reg [`ADDR_W-1:0] w_addr; - - - // Read signals - reg r_en; - reg [`ADDR_W-1:0] r_addr; - wire [`DATA_W-1:0] r_data; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - clk = 1; - r_en = 0; - w_en = 0; - r_addr = 0; - w_addr = 0; - w_data = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - @(posedge clk) #1; - w_en = 1; - - // Write all the locations of RAM - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - w_data = i + seq_ini; - w_addr = i; - @(posedge clk) #1; - end - - w_en = 0; - @(posedge clk) #1; - - // Read all the locations of RAM with r_en = 0 - r_en = 0; - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - @(posedge clk) #1; - if (r_data != 0) begin - $display("ERROR: with r_en = 0, at position %0d, r_data should be 0 but is %d", i, - r_data); - $fatal(1); - end - end - - r_en = 1; - @(posedge clk) #1; - - // Read all the locations of RAM with r_en = 1 - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - @(posedge clk) #1; - if (r_data != i + seq_ini) begin - $display("ERROR: on position %0d, r_data is %d where it should be %0d", i, r_data, - i + seq_ini); - $fatal(1); - end - end - - r_en = 0; - - #(5 * clk_per); - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_t2p #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i(clk), - - .w_en_i (w_en), - .w_addr_i(w_addr), - .w_data_i(w_data), - - .r_en_i (r_en), - .r_addr_i(r_addr), - .r_data_o(r_data) - ); - - // Clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_t2p/iob_ram_t2p.py b/lib/hardware/memories/ram/iob_ram_t2p/iob_ram_t2p.py deleted file mode 100644 index 4d147fad5..000000000 --- a/lib/hardware/memories/ram/iob_ram_t2p/iob_ram_t2p.py +++ /dev/null @@ -1,131 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "0", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_en_i", - "descr": "Input port", - "signals": [ - {"name": "w_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_addr_i", - "descr": "Input port", - "signals": [ - {"name": "w_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "w_data_i", - "descr": "Input port", - "signals": [ - {"name": "w_data", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "r_en_i", - "descr": "Input port", - "signals": [ - {"name": "r_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_addr_i", - "descr": "Input port", - "signals": [ - {"name": "r_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_data_o", - "descr": "Output port", - "signals": [ - {"name": "r_data", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - - - - // Declare the RAM - reg [DATA_W-1:0] mem [(2**ADDR_W)-1:0]; - - reg [DATA_W-1:0] r_data; - // Initialize the RAM - initial begin - if (MEM_INIT_FILE_INT != "none") begin - $readmemh(MEM_INIT_FILE_INT, mem, 0, (2 ** ADDR_W) - 1); - end - end - - //read port - always @(posedge clk_i) begin - if (r_en_i) begin - r_data <= mem[r_addr_i]; - end - end - - //write port - always @(posedge clk_i) begin - if (w_en_i) begin - mem[w_addr_i] <= w_data_i; - end - end - - assign r_data_o = r_data; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_t2p_be/hardware/simulation/src/iob_ram_t2p_be_tb.v b/lib/hardware/memories/ram/iob_ram_t2p_be/hardware/simulation/src/iob_ram_t2p_be_tb.v deleted file mode 100644 index 7c0c32d6c..000000000 --- a/lib/hardware/memories/ram/iob_ram_t2p_be/hardware/simulation/src/iob_ram_t2p_be_tb.v +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_ram_t2p_be_tb; - - // Inputs - reg clk; - - // Write signals - reg w_en; - reg [`DATA_W-1:0] w_data; - reg [`ADDR_W-1:0] w_addr; - - - // Read signals - reg r_en; - reg [`ADDR_W-1:0] r_addr; - wire [`DATA_W-1:0] r_data; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - clk = 1; - r_en = 0; - w_en = 0; - r_addr = 0; - w_addr = 0; - w_data = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - @(posedge clk) #1; - w_en = 1; - - // Write all the locations of RAM - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - w_data = i + seq_ini; - w_addr = i; - @(posedge clk) #1; - end - - w_en = 0; - @(posedge clk) #1; - - // Read all the locations of RAM with r_en = 0 - r_en = 0; - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - @(posedge clk) #1; - if (r_data != 0) begin - $display("ERROR: with r_en = 0, at position %0d, r_data should be 0 but is %d", i, - r_data); - $fatal(); - end - end - - r_en = 1; - @(posedge clk) #1; - - // Read all the locations of RAM with r_en = 1 - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r_addr = i; - @(posedge clk) #1; - if (r_data != i + seq_ini) begin - $display("ERROR: on position %0d, r_data is %d where it should be %0d", i, r_data, - i + seq_ini); - $fatal(); - end - end - - r_en = 0; - - #(5 * clk_per); - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_t2p_be #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i(clk), - - .w_en_i (w_en), - .w_addr_i(w_addr), - .w_data_i(w_data), - - .r_en_i (r_en), - .r_addr_i(r_addr), - .r_data_o(r_data) - ); - - // Clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_t2p_be/iob_ram_t2p_be.py b/lib/hardware/memories/ram/iob_ram_t2p_be/iob_ram_t2p_be.py deleted file mode 100644 index f0a878e1e..000000000 --- a/lib/hardware/memories/ram/iob_ram_t2p_be/iob_ram_t2p_be.py +++ /dev/null @@ -1,166 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "0", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "COL_W", - "type": "F", - "val": "8", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "NUM_COL", - "type": "F", - "val": "DATA_W / COL_W", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_en_i", - "descr": "Input port", - "signals": [ - {"name": "w_en", "width": "DATA_W/8", "direction": "input"}, - ], - }, - { - "name": "w_addr_i", - "descr": "Input port", - "signals": [ - {"name": "w_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "w_data_i", - "descr": "Input port", - "signals": [ - {"name": "w_data", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "r_en_i", - "descr": "Input port", - "signals": [ - {"name": "r_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_addr_i", - "descr": "Input port", - "signals": [ - {"name": "r_addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_data_o", - "descr": "Output port", - "signals": [ - {"name": "r_data", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_ram_t2p", - "instantiate": False, - }, - ], - "snippets": [ - { - "verilog_code": """ - reg [DATA_W-1:0] r_data_o_reg; - assign r_data_o=r_data_o_reg; - `ifdef IOB_MEM_NO_READ_ON_WRITE - localparam file_suffix = {"7", "6", "5", "4", "3", "2", "1", "0"}; - - genvar i; - generate - for (i = 0; i < NUM_COL; i = i + 1) begin : ram_col - localparam mem_init_file_int = (HEXFILE != "none") ? - {HEXFILE, "_", file_suffix[8*(i+1)-1-:8], ".hex"} : "none"; - - iob_ram_t2p #( - .HEXFILE(mem_init_file_int), - .ADDR_W (ADDR_W), - .DATA_W (COL_W) - ) ram ( - .clk_i(clk_i), - - .w_en_i (w_en_i[i]), - .w_addr_i(w_addr_i), - .w_data_i(w_data_i[i*COL_W+:COL_W]), - .r_en_i (r_en_i), - .r_addr_i(r_addr_i), - .r_data_o(r_data_o_reg[i*COL_W+:COL_W]) - ); - end - endgenerate -`else // !IOB_MEM_NO_READ_ON_WRITE - //this allows ISE 14.7 to work; do not remove - localparam mem_init_file_int = HEXFILE; - - // Declare the RAM - reg [DATA_W-1:0] mem[(2**ADDR_W)-1:0]; - - // Initialize the RAM - initial if (mem_init_file_int != "none") $readmemh(mem_init_file_int, mem, 0, (2 ** ADDR_W) - 1); - - //read port - always @(posedge clk_i) if (r_en_i) r_data_o_reg <= mem[r_addr_i]; - - //write port - integer i; - always @(posedge clk_i) begin - for (i = 0; i < NUM_COL; i = i + 1) begin - if (w_en_i[i]) begin - mem[w_addr_i][i*COL_W+:COL_W] <= w_data_i[i*COL_W+:COL_W]; - end - end - end -`endif - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_t2p_tiled/hardware/simulation/src/iob_ram_t2p_tiled_tb.v b/lib/hardware/memories/ram/iob_ram_t2p_tiled/hardware/simulation/src/iob_ram_t2p_tiled_tb.v deleted file mode 100644 index a96c2384f..000000000 --- a/lib/hardware/memories/ram/iob_ram_t2p_tiled/hardware/simulation/src/iob_ram_t2p_tiled_tb.v +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 16 -`define ADDR_W 13 -`define TILE_ADDR_W 11 - -module iob_ram_t2p_tiled_tb; - - // Inputs - reg clk; - reg w_en; - reg r_en; - reg [`DATA_W-1:0] w_data; - reg [`ADDR_W-1:0] addr; - - // Outputs - wire [`DATA_W-1:0] r_data; - - integer i, seq_ini; - integer test, base_block; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - // Instantiate the Unit Under Test (UUT) - iob_ram_t2p_tiled #( - .DATA_W (`DATA_W), - .ADDR_W (`ADDR_W), - .TILE_ADDR_W(`TILE_ADDR_W) - ) uut ( - .clk_i (clk), - .w_en_i (w_en), - .r_en_i (r_en), - .w_data_i(w_data), - .addr_i (addr), - .r_data_o(r_data) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - - initial begin - // Initialize Inputs - clk = 1; - addr = 0; - w_en = 0; - r_en = 0; - w_data = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - @(posedge clk) #1; - w_en = 1; - - //Write all the locations of RAM - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - w_data = i + seq_ini; - addr = i; - @(posedge clk) #1; - end - - w_en = 0; - @(posedge clk) #1; - - //Read all the locations of RAM with r_en = 0 - r_en = 0; - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (r_data != 0) begin - $display("ERROR: with r_en = 0, at position %0d, r_data should be 0 but is %d", i, - r_data); - $fatal(); - end - end - - r_en = 1; - @(posedge clk) #1; - - //Read all the locations of RAM with r_en = 1 - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (r_data != i + seq_ini) begin - $display("ERROR: on position %0d, r_data is %d where it should be %0d", i, r_data, - i + seq_ini); - $fatal(); - end - end - - r_en = 0; - - #(5 * clk_per); - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_t2p_tiled/iob_ram_t2p_tiled.py b/lib/hardware/memories/ram/iob_ram_t2p_tiled/iob_ram_t2p_tiled.py deleted file mode 100644 index 9ec1d42a1..000000000 --- a/lib/hardware/memories/ram/iob_ram_t2p_tiled/iob_ram_t2p_tiled.py +++ /dev/null @@ -1,186 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "ADDR_W", - "type": "P", - "val": "13", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "TILE_ADDR_W", - "type": "P", - "val": "11", - "min": "0", - "max": "NA", - "descr": "", - }, - { - "name": "K", - "type": "F", - "val": "$ceil(2 ** (ADDR_W - TILE_ADDR_W))", - "min": "0", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_en_i", - "descr": "Input port", - "signals": [ - {"name": "w_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_en_i", - "descr": "Input port", - "signals": [ - {"name": "r_en", "width": 1, "direction": "input"}, - ], - }, - { - "name": "w_data_i", - "descr": "Input port", - "signals": [ - {"name": "w_data", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "addr_i", - "descr": "Input port", - "signals": [ - {"name": "addr", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_data_o", - "descr": "Output port", - "signals": [ - {"name": "r_data", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "wires": [ - { - "name": "addr_en", - "descr": "addr_en wire", - "signals": [ - {"name": "addr_en", "width": "K"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_ram_t2p", - "instantiate": False, - }, - ], - "snippets": [ - { - "verilog_code": """ - reg [DATA_W-1:0] r_data_o_reg; - assign r_data_o=r_data_o_reg; - decN #( - .N_OUTPUTS(K) - ) addr_dec ( - .dec_i(addr_i[ADDR_W-1:ADDR_W-$clog2(K)]), // only the first clog2(K) MSBs select the BRAM - .dec_o(addr_en) - ); - - // Generate K BRAMs - genvar i; - generate - // Vector containing all BRAM outputs - wire [DATA_W-1:0] r_data_vec[K-1:0]; - for (i = 0; i < K; i = i + 1) begin : ram_tile - iob_ram_t2p #( - .DATA_W(DATA_W), - .ADDR_W(ADDR_W - $clog2(K)) - ) bram ( - .clk_i(clk_i), - - .w_en_i (w_en_i & addr_en[i]), - .w_addr_i(addr_i[ADDR_W-$clog2(K)-1:0]), - .w_data_i(w_data_i), - - .r_en_i (r_en_i & addr_en[i]), - .r_addr_i(addr_i[ADDR_W-$clog2(K)-1:0]), - .r_data_o(r_data_vec[i]) - ); - end - endgenerate - - // bram mux: outputs selected BRAM - muxN #( - .N_INPUTS(K), - .INPUT_W (DATA_W) - ) bram_out_sel ( - .data_i(r_data_vec), - .sel_i (addr_i[ADDR_W-1:ADDR_W-$clog2(K)]), - .data_o(r_data_o_reg) - ); - -endmodule - -// decoder with parameterizable output -module decN #( - parameter N_OUTPUTS = 16 -) ( - input [$clog2(N_OUTPUTS)-1:0] dec_i, - output reg [ N_OUTPUTS-1:0] dec_o -); - - always @* begin - dec_o = 0; - dec_o[dec_i] = 1'b1; - end -endmodule - -// multiplexer with parameterizable input -module muxN #( - parameter N_INPUTS = 4, // number of inputs - parameter INPUT_W = 8, // input bit width - parameter S = $clog2(N_INPUTS), // number of select lines - parameter W = N_INPUTS * INPUT_W // total data width -) ( - // Inputs - input [INPUT_W-1:0] data_i[N_INPUTS-1:0], // input port - input [ S-1:0] sel_i, // selection port - - // Outputs - output reg [INPUT_W-1:0] data_o // output port -); - - always @* begin - data_o = data_i[sel_i]; - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_tdp/hardware/simulation/src/iob_ram_tdp_tb.v b/lib/hardware/memories/ram/iob_ram_tdp/hardware/simulation/src/iob_ram_tdp_tb.v deleted file mode 100644 index ebe6b771b..000000000 --- a/lib/hardware/memories/ram/iob_ram_tdp/hardware/simulation/src/iob_ram_tdp_tb.v +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_ram_tdp_tb; - - // Inputs - reg clk; - - reg [`DATA_W-1:0] data_a; - reg [`ADDR_W-1:0] addr_a; - reg en_a; - reg we_a; - - reg [`DATA_W-1:0] data_b; - reg [`ADDR_W-1:0] addr_b; - reg en_b; - reg we_b; - - // Outputs - reg [`DATA_W-1:0] q_a; - reg [`DATA_W-1:0] q_b; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk = 1; - - data_a = 0; - addr_a = 0; - en_a = 0; - we_a = 0; - - data_b = 0; - addr_b = 0; - en_b = 0; - we_b = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - #clk_per; - @(posedge clk) #1; - en_a = 1; - - // Write into port A and read from it - @(posedge clk) #1; - we_a = 1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_a = i; - data_a = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - we_a = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_a = i; - @(posedge clk) #1; - if (i + seq_ini != q_a) begin - $display("ERROR: write error in port A position %d, where data=%h but q_a=%h", i, - i + seq_ini, q_a); - $fatal(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 64; - - @(posedge clk) #1; - en_b = 1; - - // Write into port B and read from it - @(posedge clk) #1; - we_b = 1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_b = i; - data_b = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - we_b = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_b = i; - @(posedge clk) #1; - if (i + seq_ini != q_b) begin - $display("ERROR: write error in port B position %d, where data=%h but q_b=%h", i, - i + seq_ini, q_b); - $fatal(); - end - end - - @(posedge clk) #1; - en_a = 0; - en_b = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_tdp #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i(clk), - - .dA_i (data_a), - .addrA_i(addr_a), - .enA_i (en_a), - .weA_i (we_a), - .dA_o (q_a), - - .dB_i (data_b), - .addrB_i(addr_b), - .enB_i (en_b), - .weB_i (we_b), - .dB_o (q_b) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_tdp/iob_ram_tdp.py b/lib/hardware/memories/ram/iob_ram_tdp/iob_ram_tdp.py deleted file mode 100644 index 234fcde33..000000000 --- a/lib/hardware/memories/ram/iob_ram_tdp/iob_ram_tdp.py +++ /dev/null @@ -1,171 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "6", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "8", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "MEM_NO_READ_ON_WRITE", - "type": "P", - "val": "1", - "min": "0", - "max": "NA", - "descr": "", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "0", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "dA_i", - "descr": "Input port", - "signals": [ - {"name": "dA", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "addrA_i", - "descr": "Input port", - "signals": [ - {"name": "addrA", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "enA_i", - "descr": "Input port", - "signals": [ - {"name": "enA", "width": 1, "direction": "input"}, - ], - }, - { - "name": "weA_i", - "descr": "Input port", - "signals": [ - {"name": "weA", "width": 1, "direction": "input"}, - ], - }, - { - "name": "dA_o", - "descr": "Output port", - "signals": [ - {"name": "dA", "width": "DATA_W", "direction": "output"}, - ], - }, - { - "name": "dB_i", - "descr": "Input port", - "signals": [ - {"name": "dB", "width": "DATA_W", "direction": "input"}, - ], - }, - { - "name": "addrB_i", - "descr": "Input port", - "signals": [ - {"name": "addrB", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "enB_i", - "descr": "Input port", - "signals": [ - {"name": "enB", "width": 1, "direction": "input"}, - ], - }, - { - "name": "weB_i", - "descr": "Input port", - "signals": [ - {"name": "weB", "width": 1, "direction": "input"}, - ], - }, - { - "name": "dB_o", - "descr": "Output port", - "signals": [ - {"name": "dB", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - reg [DATA_W-1:0] dA_o_reg; - reg [DATA_W-1:0] dB_o_reg; - assign dA_o=dA_o_reg; - assign dB_o=dB_o_reg; - // Declare the RAM - reg [DATA_W-1:0] ram[2**ADDR_W-1:0]; - - // Initialize the RAM - initial if (MEM_INIT_FILE_INT != "none") $readmemh(MEM_INIT_FILE_INT, ram, 0, 2 ** ADDR_W - 1); - - generate - if (MEM_NO_READ_ON_WRITE) begin : with_MEM_NO_READ_ON_WRITE - always @(posedge clk_i) begin // Port A - if (enA_i) - if (weA_i) ram[addrA_i] <= dA_i; - else dA_o_reg <= ram[addrA_i]; - end - always @(posedge clk_i) begin // Port B - if (enB_i) - if (weB_i) ram[addrB_i] <= dB_i; - else dB_o_reg <= ram[addrB_i]; - end - end else begin : not_MEM_NO_READ_ON_WRITE - always @(posedge clk_i) begin // Port A - if (enA_i) if (weA_i) ram[addrA_i] <= dA_i; - dA_o_reg <= ram[addrA_i]; - end - always @(posedge clk_i) begin // Port B - if (enB_i) if (weB_i) ram[addrB_i] <= dB_i; - dB_o_reg <= ram[addrB_i]; - end - end - endgenerate - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_tdp_be/hardware/simulation/src/iob_ram_tdp_be_tb.v b/lib/hardware/memories/ram/iob_ram_tdp_be/hardware/simulation/src/iob_ram_tdp_be_tb.v deleted file mode 100644 index c0673f5e7..000000000 --- a/lib/hardware/memories/ram/iob_ram_tdp_be/hardware/simulation/src/iob_ram_tdp_be_tb.v +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 32 -`define ADDR_W 4 - -module iob_ram_tdp_be_tb; - - // Inputs - reg clk; - - reg enaA; // enable access to ram - reg [`DATA_W/8-1:0] weA; // write enable vector - reg [ `ADDR_W-1:0] addrA; - reg [ `DATA_W-1:0] data_inA; - - reg enaB; // enable access to ram - reg [`DATA_W/8-1:0] weB; // write enable vector - reg [ `ADDR_W-1:0] addrB; - reg [ `DATA_W-1:0] data_inB; - - // Ouptuts - reg [ `DATA_W-1:0] data_outA; - reg [ `DATA_W-1:0] data_outB; - - integer i, seq_ini, first_seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - //Initialize Inputs - clk = 1; - enaA = 0; - enaB = 0; - for (i = 0; i < `DATA_W / 8; i = i + 1) begin - weA[i] = 0; - weB[i] = 0; - end - addrA = 0; - addrB = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - first_seq_ini = seq_ini; - - #clk_per; - @(posedge clk) #1; - enaA = 1; - - // Write into RAM port A in all positions and read from it - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - weA[i] = 1; - @(posedge clk) #1; - addrA = i; - data_inA = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) weA[i] = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrA = i; - @(posedge clk) #1; - if (i + seq_ini != data_outA) begin - $display("ERROR: write error in port A position %d, where data=%h but data_outA=%h", i, - i + seq_ini, data_outA); - $fatal(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 64; - - @(posedge clk) #1; - enaB = 1; - - // Write into RAM port B in all positions and read from it - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - weB[i] = 1; - @(posedge clk) #1; - addrB = i; - data_inB = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) weB[i] = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrB = i; - @(posedge clk) #1; - if (i + seq_ini != data_outB) begin - $display("ERROR: write error in port B position %d, where data=%h but data_outB=%h", i, - i + seq_ini, data_outB); - $fatal(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = first_seq_ini; - - // Test if output is truly different - // Port A - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrA = i; - @(posedge clk) #1; - if (i + seq_ini == data_outA) begin - if (i + seq_ini != 10) begin // rule out EOL - $display( - "ERROR: read error in port A position %d, where data and data_outA are '%h' but should not be the same", - i, data_outA); - $fatal(); - end - end - end - - @(posedge clk) #1; - enaA = 0; - - // Port B - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrB = i; - @(posedge clk) #1; - if (i + seq_ini == data_outB) begin - $display( - "ERROR: read error in port B position %d, where data and data_outB are '%h' but should not be the same", - i, data_outB); - $fatal(); - end - end - - @(posedge clk) #1; - enaB = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_tdp_be #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i (clk), - .enA_i (enaA), - .weA_i (weA), - .addrA_i(addrA), - .dA_i (data_inA), - .dA_o (data_outA), - - .enB_i (enaB), - .weB_i (weB), - .addrB_i(addrB), - .dB_i (data_inB), - .dB_o (data_outB) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_tdp_be/iob_ram_tdp_be.py b/lib/hardware/memories/ram/iob_ram_tdp_be/iob_ram_tdp_be.py deleted file mode 100644 index 937fa24ac..000000000 --- a/lib/hardware/memories/ram/iob_ram_tdp_be/iob_ram_tdp_be.py +++ /dev/null @@ -1,139 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "10", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "MEM_NO_READ_ON_WRITE", - "type": "P", - "val": "0", - "min": "0", - "max": "1", - "descr": "No simultaneous read/write", - }, - { - "name": "COL_W", - "type": "F", - "val": "DATA_W / 4", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "NUM_COL", - "type": "F", - "val": "DATA_W / COL_W", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "FILE_SUFFIX", - "type": "F", - "val": '{"7", "6", "5", "4", "3", "2", "1", "0"}', - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "port_a", - "descr": "Memory interface A", - "signals": [ - {"name": "enA", "width": 1, "direction": "input"}, - {"name": "weA", "width": "DATA_W/8", "direction": "input"}, - {"name": "addrA", "width": "ADDR_W", "direction": "input"}, - {"name": "dA", "width": "DATA_W", "direction": "input"}, - {"name": "dA", "width": "DATA_W", "direction": "output"}, - ], - }, - { - "name": "port_b", - "descr": "Memory interface B", - "signals": [ - {"name": "enB", "width": 1, "direction": "input"}, - {"name": "weB", "width": "DATA_W/8", "direction": "input"}, - {"name": "addrB", "width": "ADDR_W", "direction": "input"}, - {"name": "dB", "width": "DATA_W", "direction": "input"}, - {"name": "dB", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_ram_tdp", - "instantiate": False, - }, - ], - "snippets": [ - { - "verilog_code": """ - genvar index; - generate - for (index = 0; index < NUM_COL; index = index + 1) begin : ram_col - localparam mem_init_file_int = (HEXFILE != "none") ? - {HEXFILE, "_", FILE_SUFFIX[8*(index+1)-1-:8], ".hex"} : "none"; - iob_ram_tdp #( - .HEXFILE (mem_init_file_int), - .ADDR_W (ADDR_W), - .DATA_W (COL_W), - .MEM_NO_READ_ON_WRITE(MEM_NO_READ_ON_WRITE) - ) ram ( - .clk_i(clk_i), - - .enA_i (enA_i), - .addrA_i(addrA_i), - .dA_i (dA_i[index*COL_W+:COL_W]), - .weA_i (weA_i[index]), - .dA_o (dA_o[index*COL_W+:COL_W]), - - .enB_i (enB_i), - .addrB_i(addrB_i), - .dB_i (dB_i[index*COL_W+:COL_W]), - .weB_i (weB_i[index]), - .dB_o (dB_o[index*COL_W+:COL_W]) - ); - end - endgenerate - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/ram/iob_ram_tdp_be_xil/hardware/simulation/src/iob_ram_tdp_be_xil_tb.v b/lib/hardware/memories/ram/iob_ram_tdp_be_xil/hardware/simulation/src/iob_ram_tdp_be_xil_tb.v deleted file mode 100644 index 32e2ffeb2..000000000 --- a/lib/hardware/memories/ram/iob_ram_tdp_be_xil/hardware/simulation/src/iob_ram_tdp_be_xil_tb.v +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 32 -`define ADDR_W 4 - -module iob_ram_tdp_be_xil_tb; - - // Inputs - reg clk; - - reg enaA; // enable access to ram - reg [`DATA_W/8-1:0] weA; // write enable vector - reg [ `ADDR_W-1:0] addrA; - reg [ `DATA_W-1:0] data_inA; - - reg enaB; // enable access to ram - reg [`DATA_W/8-1:0] weB; // write enable vector - reg [ `ADDR_W-1:0] addrB; - reg [ `DATA_W-1:0] data_inB; - - // Ouptuts - reg [ `DATA_W-1:0] data_outA; - reg [ `DATA_W-1:0] data_outB; - - integer i, seq_ini, first_seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - //Initialize Inputs - clk = 1; - enaA = 0; - enaB = 0; - for (i = 0; i < `DATA_W / 8; i = i + 1) begin - weA[i] = 0; - weB[i] = 0; - end - addrA = 0; - addrB = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - first_seq_ini = seq_ini; - - #clk_per; - @(posedge clk) #1; - enaA = 1; - - // Write into RAM port A in all positions and read from it - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - weA[i] = 1; - @(posedge clk) #1; - addrA = i; - data_inA = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) weA[i] = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrA = i; - @(posedge clk) #1; - if (i + seq_ini != data_outA) begin - $display("ERROR: write error in port A position %d, where data=%h but data_outA=%h", i, - i + seq_ini, data_outA); - $fatal(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 64; - - @(posedge clk) #1; - enaB = 1; - - // Write into RAM port B in all positions and read from it - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - weB[i] = 1; - @(posedge clk) #1; - addrB = i; - data_inB = i + seq_ini; - @(posedge clk) #1; - end - - @(posedge clk) #1; - for (i = 0; i < `DATA_W / 8; i = i + 1) weB[i] = 0; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrB = i; - @(posedge clk) #1; - if (i + seq_ini != data_outB) begin - $display("ERROR: write error in port B position %d, where data=%h but data_outB=%h", i, - i + seq_ini, data_outB); - $fatal(); - end - end - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = first_seq_ini; - - // Test if output is truly different - // Port A - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrA = i; - @(posedge clk) #1; - if (i + seq_ini == data_outA) begin - if (i + seq_ini != 10) begin // rule out EOL - $display( - "ERROR: read error in port A position %d, where data and data_outA are '%h' but should not be the same", - i, data_outA); - $fatal(); - end - end - end - - @(posedge clk) #1; - enaA = 0; - - // Port B - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addrB = i; - @(posedge clk) #1; - if (i + seq_ini == data_outB) begin - $display( - "ERROR: read error in port B position %d, where data and data_outB are '%h' but should not be the same", - i, data_outB); - $fatal(); - end - end - - @(posedge clk) #1; - enaB = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_ram_tdp_be_xil #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i (clk), - .enA_i (enaA), - .weA_i (weA), - .addrA_i(addrA), - .dA_i (data_inA), - .dA_o (data_outA), - - .enB_i (enaB), - .weB_i (weB), - .addrB_i(addrB), - .dB_i (data_inB), - .dB_o (data_outB) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/ram/iob_ram_tdp_be_xil/iob_ram_tdp_be_xil.py b/lib/hardware/memories/ram/iob_ram_tdp_be_xil/iob_ram_tdp_be_xil.py deleted file mode 100644 index 95de8953b..000000000 --- a/lib/hardware/memories/ram/iob_ram_tdp_be_xil/iob_ram_tdp_be_xil.py +++ /dev/null @@ -1,137 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "10", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "COL_W", - "type": "F", - "val": "DATA_W / 4", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "NUM_COL", - "type": "F", - "val": "DATA_W / COL_W", - "min": "NA", - "max": "NA", - "descr": "", - }, - { - "name": "mem_init_file_int", - "type": "F", - "val": '{HEXFILE, ".hex"}', - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "port_a", - "descr": "Memory interface A", - "signals": [ - {"name": "enA", "width": 1, "direction": "input"}, - {"name": "weA", "width": "DATA_W/8", "direction": "input"}, - {"name": "addrA", "width": "ADDR_W", "direction": "input"}, - {"name": "dA", "width": "DATA_W", "direction": "input"}, - {"name": "dA", "width": "DATA_W", "direction": "output"}, - ], - }, - { - "name": "port_b", - "descr": "Memory interface B", - "signals": [ - {"name": "enB", "width": 1, "direction": "input"}, - {"name": "weB", "width": "DATA_W/8", "direction": "input"}, - {"name": "addrB", "width": "ADDR_W", "direction": "input"}, - {"name": "dB", "width": "DATA_W", "direction": "input"}, - {"name": "dB", "width": "DATA_W", "direction": "output"}, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Core Memory - reg [DATA_W-1:0] ram_block[(2**ADDR_W)-1:0]; - - // Initialize the RAM - initial - if (MEM_INIT_FILE_INT != "none.hex") - $readmemh(MEM_INIT_FILE_INT, ram_block, 0, 2 ** ADDR_W - 1); - - // Port-A Operation - reg [DATA_W-1:0] dA_o_int; - integer i; - always @(posedge clk_i) begin - if (enA_i) begin - for (i = 0; i < NUM_COL; i = i + 1) begin - if (weA_i[i]) begin - ram_block[addrA_i][i*COL_W+:COL_W] <= dA_i[i*COL_W+:COL_W]; - end - end - dA_o_int <= ram_block[addrA_i]; // Send Feedback - end - end - - assign dA_o = dA_o_int; - - // Port-B Operation - reg [DATA_W-1:0] dB_o_int; - integer j; - always @(posedge clk_i) begin - if (enB_i) begin - for (j = 0; j < NUM_COL; j = j + 1) begin - if (weB_i[j]) begin - ram_block[addrB_i][j*COL_W+:COL_W] <= dB_i[j*COL_W+:COL_W]; - end - end - dB_o_int <= ram_block[addrB_i]; // Send Feedback - end - end - - assign dB_o = dB_o_int; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/regfile/iob_regfile_2p/hardware/src/iob_regfile_2p.v b/lib/hardware/memories/regfile/iob_regfile_2p/hardware/src/iob_regfile_2p.v deleted file mode 100644 index df11f5259..000000000 --- a/lib/hardware/memories/regfile/iob_regfile_2p/hardware/src/iob_regfile_2p.v +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1 ns / 1 ps - -module iob_regfile_2p #( - parameter N = 0, //number of registers - parameter W = 0, //register width - parameter WDATA_W = 0, //width of write data - parameter WADDR_W = 0, //width of write address - parameter RDATA_W = 0, //width of read data - parameter RADDR_W = 0, //width of read address - //cpu interface - //the address on the cpu side must be a byte address - parameter DATA_W = 0, //width of data - parameter WSTRB_W = WDATA_W / 8 //width of write strobe -) ( - `include "iob_regfile_2p_clk_en_rst_s_port.vs" - input wen_i, - input [((RADDR_W+WADDR_W)+(WSTRB_W+WDATA_W))-1:0] req_i, - output [ RDATA_W-1:0] resp_o -); - - //register file and register file write enable - wire [(N*W)-1 : 0] regfile; - wire [ N-1:0] wen; - - //reconstruct write address from waddr_i and wstrb_i - wire [WSTRB_W-1:0] wstrb = req_i[WDATA_W+:WSTRB_W]; - wire [WADDR_W-1:0] waddr = req_i[WSTRB_W+WDATA_W+:WADDR_W]; - localparam WADDR_INT_W = (WADDR_W > ($clog2( - DATA_W / 8 - ) + 1)) ? WADDR_W : ($clog2( - DATA_W / 8 - ) + 1); - wire [($clog2(DATA_W/8)+1)-1:0] waddr_incr; - wire [ WADDR_INT_W-1:0] waddr_int = waddr + waddr_incr; - - iob_ctls #( - .W (DATA_W / 8), - .MODE (0), - .SYMBOL(0) - ) iob_ctls_txinst ( - .data_i (wstrb), - .count_o(waddr_incr) - ); - - //write register file - wire [WDATA_W-1:0] wdata_int = req_i[WDATA_W-1:0]; - genvar row_sel; - genvar col_sel; - - localparam LAST_I = (N / WSTRB_W) * WSTRB_W; - localparam REM_I = (N - LAST_I) + 1; - - generate - for (row_sel = 0; row_sel < N; row_sel = row_sel + WSTRB_W) begin : g_rows - for ( - col_sel = 0; col_sel < ((row_sel == LAST_I) ? REM_I : WSTRB_W); col_sel = col_sel + 1 - ) begin : g_columns - if ((row_sel + col_sel) < N) begin : g_if - assign wen[row_sel+col_sel] = wen_i & (waddr_int == (row_sel + col_sel)) & wstrb[col_sel]; - iob_reg_e #( - .DATA_W (W), - .RST_VAL({W{1'b0}}) - ) iob_reg_inst ( - `include "iob_regfile_2p_clk_en_rst_s_s_portmap.vs" - .en_i (wen[row_sel+col_sel]), - .data_i(wdata_int[(col_sel*8)+:W]), - .data_o(regfile[(row_sel+col_sel)*W+:W]) - ); - end - end - end - endgenerate - - //read register file - generate - if (RADDR_W > 0) begin : g_read - wire [RADDR_W-1:0] raddr = req_i[(WSTRB_W+WDATA_W)+WADDR_W+:RADDR_W]; - assign resp_o = regfile[RDATA_W*raddr+:RDATA_W]; - end else begin : g_read - assign resp_o = regfile; - end - endgenerate - -endmodule diff --git a/lib/hardware/memories/regfile/iob_regfile_2p/iob_regfile_2p.py b/lib/hardware/memories/regfile/iob_regfile_2p/iob_regfile_2p.py deleted file mode 100644 index e049898f5..000000000 --- a/lib/hardware/memories/regfile/iob_regfile_2p/iob_regfile_2p.py +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - ], - "blocks": [ - { - "core_name": "iob_ctls", - "instance_name": "iob_ctls_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/regfile/iob_regfile_at2p/hardware/src/iob_regfile_at2p.v b/lib/hardware/memories/regfile/iob_regfile_at2p/hardware/src/iob_regfile_at2p.v deleted file mode 100644 index ee51d760a..000000000 --- a/lib/hardware/memories/regfile/iob_regfile_at2p/hardware/src/iob_regfile_at2p.v +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1 ns / 1 ps - - -module iob_regfile_at2p #( - parameter ADDR_W = 3, - parameter DATA_W = 21 -) ( - // Write Port - input w_clk_i, - input w_cke_i, - input w_arst_i, - input [ADDR_W-1:0] w_addr_i, - input [DATA_W-1:0] w_data_i, - - // Read Port - input r_clk_i, - input r_cke_i, - input r_arst_i, - input [ADDR_W-1:0] r_addr_i, - output [DATA_W-1:0] r_data_o -); - - //write - wire [((2**ADDR_W)*DATA_W)-1:0] regfile_in; - wire [((2**ADDR_W)*DATA_W)-1:0] regfile_synced; - wire [ (2**ADDR_W)-1:0] regfile_en; - - genvar addr; - generate - for (addr = 0; addr < (2 ** ADDR_W); addr = addr + 1) begin : gen_register_file - assign regfile_en[addr] = (w_addr_i == addr); - iob_reg_e #( - .DATA_W (DATA_W), - .RST_VAL({DATA_W{1'd0}}) - ) rdata ( - .clk_i (w_clk_i), - .cke_i (w_cke_i), - .arst_i(w_arst_i), - .en_i (regfile_en[addr]), - .data_i(w_data_i), - .data_o(regfile_in[addr*DATA_W+:DATA_W]) - ); - end - endgenerate - - - //sync - iob_sync #( - .DATA_W ((2 ** ADDR_W) * DATA_W), - .RST_VAL({((2 ** ADDR_W) * DATA_W) {1'b0}}) - ) iob_sync_regfile_synced ( - .clk_i (r_clk_i), - .arst_i (r_arst_i), - .signal_i(regfile_in), - .signal_o(regfile_synced) - ); - - wire [DATA_W-1:0] r_data = regfile_synced[r_addr_i*DATA_W+:DATA_W]; - //read - iob_reg #( - .DATA_W (DATA_W), - .RST_VAL({DATA_W{1'd0}}) - ) rdata ( - .clk_i (r_clk_i), - .cke_i (r_cke_i), - .arst_i(r_arst_i), - .data_i(r_data), - .data_o(r_data_o) - ); - -endmodule diff --git a/lib/hardware/memories/regfile/iob_regfile_at2p/iob_regfile_at2p.py b/lib/hardware/memories/regfile/iob_regfile_at2p/iob_regfile_at2p.py deleted file mode 100644 index 1c5c454cf..000000000 --- a/lib/hardware/memories/regfile/iob_regfile_at2p/iob_regfile_at2p.py +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_sync", - "instance_name": "iob_sync_inst", - }, - { - "core_name": "iob_reg_e", - "instance_name": "iob_reg_e_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/regfile/iob_regfile_sp/hardware/simulation/src/iob_regfile_sp_tb.v b/lib/hardware/memories/regfile/iob_regfile_sp/hardware/simulation/src/iob_regfile_sp_tb.v deleted file mode 100644 index 8afaed1e7..000000000 --- a/lib/hardware/memories/regfile/iob_regfile_sp/hardware/simulation/src/iob_regfile_sp_tb.v +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 32 -`define ADDR_W 4 - -module iob_regfile_sp_tb; - - // Inputs - reg clk; - reg arst = 0; - reg rst; - reg [ `DATA_W-1:0] w_data; - reg [ `ADDR_W-1:0] addr; - reg en; - - // Ouptuts - reg [`DATA_W-1 : 0] r_data; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk = 1; - rst = 0; - w_data = 0; - addr = 0; - en = 0; - - // Number from which to start the incremental sequence to write into the RAM - seq_ini = 32; - - #clk_per; - @(posedge clk) #1; - rst = 1; - @(posedge clk) #1; - rst = 0; - - @(posedge clk) #1; - en = 1; - - // Write and read all the locations - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - w_data = i + seq_ini; - @(posedge clk) #1; - if (r_data != i + seq_ini) begin - $display("Write ERROR: read error in r_data.\n \t data=%0d; r_data=%0d", i + seq_ini, r_data); - $finish(); - end - @(posedge clk) #1; - end - - @(posedge clk) #1; - en = 0; - addr = 0; - - // Read all the locations and check if still stored - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (r_data != i + seq_ini) begin - $display("Read ERROR: read error in r_data.\n \t data=%0d; r_data=%0d", i + seq_ini, r_data); - $finish(); - end - @(posedge clk) #1; - end - - // Resets the entire memory - @(posedge clk) #1; - rst = 1; - @(posedge clk) #1; - rst = 0; - - // Read all the locations and check if reset worked - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (r_data != 0) begin - $display("ERROR: r_data is not null"); - $finish(); - end - @(posedge clk) #1; - end - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - end - - // Instantiate the Unit Under Test (UUT) - iob_regfile_sp #( - .ADDR_W(`ADDR_W), - .DATA_W(`DATA_W) - ) uut ( - .clk_i (clk), - .cke_i (1'b1), - .arst_i (arst), - .rst_i (rst), - .we_i (en), - .addr_i (addr), - .d_i (w_data), - .d_o (r_data) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/regfile/iob_regfile_sp/hardware/src/iob_regfile_sp.v b/lib/hardware/memories/regfile/iob_regfile_sp/hardware/src/iob_regfile_sp.v deleted file mode 100644 index 65442579e..000000000 --- a/lib/hardware/memories/regfile/iob_regfile_sp/hardware/src/iob_regfile_sp.v +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1 ns / 1 ps - -module iob_regfile_sp #( - parameter ADDR_W = 2, - parameter DATA_W = 21 -) ( - `include "iob_regfile_sp_clk_en_rst_s_port.vs" - input rst_i, - - input we_i, - input [ADDR_W-1:0] addr_i, - input [DATA_W-1:0] d_i, - output [DATA_W-1:0] d_o -); - - wire [DATA_W*(2**ADDR_W)-1:0] data_in = d_i << (addr_i * DATA_W); - wire [DATA_W*(2**ADDR_W)-1:0] data_out; - assign d_o = data_out >> (addr_i * DATA_W); - - genvar i; - generate - for (i = 0; i < 2 ** ADDR_W; i = i + 1) begin : g_regfile - wire reg_en_i = we_i & (addr_i == i); - iob_reg_re #( - .DATA_W(DATA_W) - ) regfile_sp_inst ( - `include "iob_regfile_sp_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .en_i (reg_en_i), - .data_i(data_in[DATA_W*(i+1)-1:DATA_W*i]), - .data_o(data_out[DATA_W*(i+1)-1:DATA_W*i]) - ); - end - endgenerate - -endmodule diff --git a/lib/hardware/memories/regfile/iob_regfile_sp/iob_regfile_sp.py b/lib/hardware/memories/regfile/iob_regfile_sp/iob_regfile_sp.py deleted file mode 100644 index 757d18396..000000000 --- a/lib/hardware/memories/regfile/iob_regfile_sp/iob_regfile_sp.py +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.09", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - # TODO: Remaining ports - ], - "blocks": [ - { - "core_name": "iob_reg_re", - "instance_name": "iob_reg_re_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/rom/iob_rom_2p/hardware/simulation/src/iob_rom_2p_tb.v b/lib/hardware/memories/rom/iob_rom_2p/hardware/simulation/src/iob_rom_2p_tb.v deleted file mode 100644 index b74fbe707..000000000 --- a/lib/hardware/memories/rom/iob_rom_2p/hardware/simulation/src/iob_rom_2p_tb.v +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps -`define ADDR_W 10 -`define DATA_W 32 - -module iob_rom_2p_tb; - - // Inputs - reg clk; - - // Read 1 signals - reg r1_en; - reg [`ADDR_W-1:0] r1_addr; - wire r1_ready; - - - // Read 2 signals - reg r2_en; - reg [`ADDR_W-1:0] r2_addr; - wire r2_ready; - - wire [`DATA_W-1:0] r_data; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - - // Initialize Inputs - clk = 1; - r1_en = 0; - r2_en = 0; - r1_addr = 0; - r2_addr = 0; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Number from which to start the incremental sequence to initialize the ROM - seq_ini = 32; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - uut.iob_rom_sp_inst.rom[i] = i + seq_ini; - end - - // Attempt to read all the locations of ROM with r1_en = 0 - r1_en = 0; - @(posedge clk) #1; - - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r1_addr = i; - @(posedge clk) #1; - if (r_data != 0) begin - $display("ERROR: with r1_en = 0, at position %0d, r_data should be 0 but is %d", i, - r_data); - $fatal(1); - end - end - - r2_en = 1; - @(posedge clk) #1; - - // Read all the locations of ROM with r2_en = 1 - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - r2_addr = i; - // wait for r2_ready - while (!r2_ready) begin - @(posedge clk) #1; - end - @(posedge clk) #1; - if (r_data != i + seq_ini) begin - $display("ERROR: on position %0d, r_data is %d where it should be %0d", i, r_data, - i + seq_ini); - $fatal(1); - end - end - - r2_en = 0; - - #(5 * clk_per); - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - $finish(); - end - - // Instantiate the Unit Under Test (UUT) - iob_rom_2p #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i (clk), - .r1_en_i (r1_en), - .r1_addr_i (r1_addr), - .r1_ready_o(r1_ready), - .r2_en_i (r2_en), - .r2_addr_i (r2_addr), - .r2_ready_o(r2_ready), - .r_data_o (r_data) - ); - - // Clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/rom/iob_rom_2p/hardware/src/iob_rom_2p.v b/lib/hardware/memories/rom/iob_rom_2p/hardware/src/iob_rom_2p.v deleted file mode 100644 index 87c0b2192..000000000 --- a/lib/hardware/memories/rom/iob_rom_2p/hardware/src/iob_rom_2p.v +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_rom_2p #( - parameter HEXFILE = "none", - parameter DATA_W = 0, - parameter ADDR_W = 0 -) ( - input clk_i, - - //read port 1 - input r1_en_i, - input [ADDR_W-1:0] r1_addr_i, - output r1_ready_o, - - //read port 2 - input r2_en_i, - input [ADDR_W-1:0] r2_addr_i, - output r2_ready_o, - - output [DATA_W-1:0] r_data_o -); - - wire en_int; - wire [ADDR_W-1:0] addr_int; - - // Internal Single Port ROM - iob_rom_sp #( - .HEXFILE(HEXFILE), - .DATA_W (DATA_W), - .ADDR_W (ADDR_W) - ) iob_rom_sp_inst ( - .clk_i (clk_i), - .r_en_i (en_int), - .addr_i (addr_int), - .r_data_o(r_data_o) - ); - - assign en_int = r1_en_i | r2_en_i; - assign addr_int = r1_en_i ? r1_addr_i : r2_addr_i; - assign r1_ready_o = 1'b1; - assign r2_ready_o = ~r1_en_i; - -endmodule diff --git a/lib/hardware/memories/rom/iob_rom_2p/iob_rom_2p.py b/lib/hardware/memories/rom/iob_rom_2p/iob_rom_2p.py deleted file mode 100644 index ecb7939a7..000000000 --- a/lib/hardware/memories/rom/iob_rom_2p/iob_rom_2p.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_rom_sp", - "instance_name": "iob_rom_sp_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/rom/iob_rom_atdp/hardware/simulation/src/iob_rom_atdp_tb.v b/lib/hardware/memories/rom/iob_rom_atdp/hardware/simulation/src/iob_rom_atdp_tb.v deleted file mode 100644 index 4652b7d1e..000000000 --- a/lib/hardware/memories/rom/iob_rom_atdp/hardware/simulation/src/iob_rom_atdp_tb.v +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_rom_atdp_tb; - - // Inputs - reg clk_a; - reg clk_b; - reg r_en_a; - reg [`ADDR_W-1:0] addr_a; - reg r_en_b; - reg [`ADDR_W-1:0] addr_b; - - // Ouptuts - reg [`DATA_W-1:0] r_data_a; - reg [`DATA_W-1:0] r_data_b; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk_a = 1; - clk_b = 1; - r_en_a = 0; - addr_a = 0; - r_en_b = 0; - addr_b = 0; - - // Number from which to start the incremental sequence to initialize the ROM - seq_ini = 32; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - uut.rom[i] = i + seq_ini; - end - - #clk_per; - @(posedge clk_a) #1; - @(posedge clk_b) #1; - - @(posedge clk_a) #1; - r_en_a = 1; - r_en_b = 1; - - @(posedge clk_a) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_a = i; - addr_b = 2 ** `ADDR_W - 1 - i; - @(posedge clk_a) #1; - if (i + seq_ini != r_data_a) begin - $display( - "ERROR: Port A - read error in position %d, where expected data=%h but r_data=%h", - i, i + seq_ini, r_data_a); - $fatal(); - end - if (seq_ini + 2 ** `ADDR_W - 1 - i != r_data_b) begin - $display( - "ERROR: Port B - read error in position %d, where expected data=%h but r_data=%h", - 2 ** `ADDR_W - 1 - i, seq_ini + 2 ** `ADDR_W - 1 - i, r_data_b); - $fatal(); - end - end - - @(posedge clk_b) #1; - r_en_a = 0; - r_en_b = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_rom_atdp #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_a_i (clk_a), - .r_en_a_i (r_en_a), - .addr_a_i (addr_a), - .r_data_a_o(r_data_a), - - .clk_b_i (clk_b), - .r_en_b_i (r_en_b), - .addr_b_i (addr_b), - .r_data_b_o(r_data_b) - ); - - // system clock - always #(clk_per / 2) clk_a = ~clk_a; - always #(clk_per / 2) clk_b = ~clk_b; - -endmodule diff --git a/lib/hardware/memories/rom/iob_rom_atdp/iob_rom_atdp.py b/lib/hardware/memories/rom/iob_rom_atdp/iob_rom_atdp.py deleted file mode 100644 index 3bd0ee18a..000000000 --- a/lib/hardware/memories/rom/iob_rom_atdp/iob_rom_atdp.py +++ /dev/null @@ -1,132 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "1", - "max": "NA", - "descr": "DATA width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "11", - "min": "1", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_a_i", - "descr": "Input port", - "signals": [ - {"name": "clk_a", "width": 1, "direction": "input"}, - ], - }, - { - "name": "addr_a_i", - "descr": "Input port", - "signals": [ - {"name": "addr_a", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_en_a_i", - "descr": "Input port", - "signals": [ - {"name": "r_en_a", "width": 1, "direction": "input"}, - ], - }, - { - "name": "clk_b_i", - "descr": "Input port", - "signals": [ - {"name": "clk_b", "width": 1, "direction": "input"}, - ], - }, - { - "name": "addr_b_i", - "descr": "Input port", - "signals": [ - {"name": "addr_b", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_en_b_i", - "descr": "Input port", - "signals": [ - {"name": "r_en_b", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_data_a_o", - "descr": "Output port", - "signals": [ - { - "name": "r_data_a", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - { - "name": "r_data_b_o", - "descr": "Output port", - "signals": [ - { - "name": "r_data_b", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Declare the ROM - reg [DATA_W-1:0] rom[2**ADDR_W-1:0]; - - // Initialize the ROM - initial if ( MEM_INIT_FILE_INT != "none") $readmemh( MEM_INIT_FILE_INT, rom, 0, 2 ** ADDR_W - 1); - - always @(posedge clk_a_i) // Port A - if (r_en_a_i) - r_data_a_o <= rom[addr_a_i]; - - always @(posedge clk_b_i) // Port B - if (r_en_b_i) - r_data_b_o <= rom[addr_b_i]; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/rom/iob_rom_sp/hardware/simulation/src/iob_rom_sp_tb.v b/lib/hardware/memories/rom/iob_rom_sp/hardware/simulation/src/iob_rom_sp_tb.v deleted file mode 100644 index 8a2840507..000000000 --- a/lib/hardware/memories/rom/iob_rom_sp/hardware/simulation/src/iob_rom_sp_tb.v +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_rom_sp_tb; - - // Inputs - reg clk; - reg r_en; - reg [`ADDR_W-1:0] addr; - - // Ouptuts - reg [`DATA_W-1:0] r_data; - - integer i, seq_ini, fp; - - - parameter clk_per = 10; // clk period = 10 timeticks - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk = 1; - r_en = 0; - addr = 0; - - // Number from which to start the incremental sequence to initialize the ROM - seq_ini = 32; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - uut.rom[i] = i + seq_ini; - end - - #clk_per; - @(posedge clk) #1; - r_en = 1; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr = i; - @(posedge clk) #1; - if (i + seq_ini != r_data) begin - $display("ERROR: read error in position %d, where expected data=%h but r_data=%h", i, - i + seq_ini, r_data); - $fatal(); - end else begin - fp = $fopen("test.log", "w"); - $fdisplay(fp, "Test passed!"); - end - end - - @(posedge clk) #1; - r_en = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_rom_sp #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i (clk), - .r_en_i (r_en), - .addr_i (addr), - .r_data_o(r_data) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/rom/iob_rom_sp/iob_rom_sp.py b/lib/hardware/memories/rom/iob_rom_sp/iob_rom_sp.py deleted file mode 100644 index efbb25ec1..000000000 --- a/lib/hardware/memories/rom/iob_rom_sp/iob_rom_sp.py +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "10", - "min": "0", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "0", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk", - "descr": "Clock", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "rom_if", - "descr": "Memory interface", - "signals": [ - {"name": "r_en", "width": 1, "direction": "input"}, - {"name": "addr", "width": "ADDR_W", "direction": "input"}, - { - "name": "r_data", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Declare the ROM - reg [DATA_W-1:0] rom[(2**ADDR_W)-1:0]; - - // Initialize the ROM - initial if ( MEM_INIT_FILE_INT != "none") $readmemh( MEM_INIT_FILE_INT, rom, 0, (2 ** ADDR_W) - 1); - - // Operate the ROM - always @(posedge clk_i) if (r_en_i) - r_data_o <= rom[addr_i]; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/memories/rom/iob_rom_tdp/hardware/simulation/src/iob_rom_tdp_tb.v b/lib/hardware/memories/rom/iob_rom_tdp/hardware/simulation/src/iob_rom_tdp_tb.v deleted file mode 100644 index 46339d21c..000000000 --- a/lib/hardware/memories/rom/iob_rom_tdp/hardware/simulation/src/iob_rom_tdp_tb.v +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -`define DATA_W 8 -`define ADDR_W 4 - -module iob_rom_tdp_tb; - - // Inputs - reg clk; - reg r_en_a; - reg [`ADDR_W-1:0] addr_a; - reg r_en_b; - reg [`ADDR_W-1:0] addr_b; - - // Ouptuts - reg [`DATA_W-1:0] r_data_a; - reg [`DATA_W-1:0] r_data_b; - - integer i, seq_ini; - integer fd; - - parameter clk_per = 10; // clk period = 10 timeticks - - - initial begin - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - - // Initialize Inputs - clk = 1; - r_en_a = 0; - addr_a = 0; - r_en_b = 0; - addr_b = 0; - - // Number from which to start the incremental sequence to initialize the ROM - seq_ini = 32; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - uut.rom[i] = i + seq_ini; - end - - #clk_per; - @(posedge clk) #1; - r_en_a = 1; - r_en_b = 1; - - @(posedge clk) #1; - for (i = 0; i < 2 ** `ADDR_W; i = i + 1) begin - addr_a = i; - addr_b = 2 ** `ADDR_W - 1 - i; - @(posedge clk) #1; - if (i + seq_ini != r_data_a) begin - $display( - "ERROR: Port A - read error in position %d, where expected data=%h but r_data=%h", - i, i + seq_ini, r_data_a); - $fatal(); - end - if (seq_ini + 2 ** `ADDR_W - 1 - i != r_data_b) begin - $display( - "ERROR: Port B - read error in position %d, where expected data=%h but r_data=%h", - 2 ** `ADDR_W - 1 - i, seq_ini + 2 ** `ADDR_W - 1 - i, r_data_b); - $fatal(); - end - end - - @(posedge clk) #1; - r_en_a = 0; - r_en_b = 0; - - #clk_per; - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - #(5 * clk_per) $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_rom_tdp #( - .DATA_W(`DATA_W), - .ADDR_W(`ADDR_W) - ) uut ( - .clk_i(clk), - - .r_en_a_i (r_en_a), - .addr_a_i (addr_a), - .r_data_a_o(r_data_a), - - .r_en_b_i (r_en_b), - .addr_b_i (addr_b), - .r_data_b_o(r_data_b) - ); - - // system clock - always #(clk_per / 2) clk = ~clk; - -endmodule diff --git a/lib/hardware/memories/rom/iob_rom_tdp/iob_rom_tdp.py b/lib/hardware/memories/rom/iob_rom_tdp/iob_rom_tdp.py deleted file mode 100644 index 05782c85f..000000000 --- a/lib/hardware/memories/rom/iob_rom_tdp/iob_rom_tdp.py +++ /dev/null @@ -1,133 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "HEXFILE", - "type": "P", - "val": '"none"', - "min": "NA", - "max": "NA", - "descr": "Name of file to load into RAM", - }, - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "1", - "max": "NA", - "descr": "DATA width", - }, - { - "name": "ADDR_W", - "type": "P", - "val": "11", - "min": "1", - "max": "NA", - "descr": "Address bus width", - }, - { - "name": "MEM_INIT_FILE_INT", - "type": "F", - "val": "HEXFILE", - "min": "NA", - "max": "NA", - "descr": "", - }, - ], - "ports": [ - { - "name": "clk_i", - "descr": "Input port", - "signals": [ - {"name": "clk", "width": 1, "direction": "input"}, - ], - }, - { - "name": "addr_a_i", - "descr": "Input port", - "signals": [ - {"name": "addr_a", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_en_a_i", - "descr": "Input port", - "signals": [ - {"name": "r_en_a", "width": 1, "direction": "input"}, - ], - }, - { - "name": "addr_b_i", - "descr": "Input port", - "signals": [ - {"name": "addr_b", "width": "ADDR_W", "direction": "input"}, - ], - }, - { - "name": "r_en_b_i", - "descr": "Input port", - "signals": [ - {"name": "r_en_b", "width": 1, "direction": "input"}, - ], - }, - { - "name": "r_data_a_o", - "descr": "Output port", - "signals": [ - { - "name": "r_data_a", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - { - "name": "r_data_b_o", - "descr": "Output port", - "signals": [ - { - "name": "r_data_b", - "width": "DATA_W", - "direction": "output", - "isvar": True, - }, - ], - }, - ], - "snippets": [ - { - "verilog_code": """ - // Declare the ROM - reg [DATA_W-1:0] rom[(2**ADDR_W)-1:0]; - - // Initialize the ROM - initial begin - if (MEM_INIT_FILE_INT != "none") begin - $readmemh(MEM_INIT_FILE_INT, rom, 0, (2 ** ADDR_W) - 1); - end - end - - always @(posedge clk_i) begin // Port A - if (r_en_a_i) begin - r_data_a_o <= rom[addr_a_i]; - end - end - - always @(posedge clk_i) begin // Port B - if (r_en_b_i) begin - r_data_b_o <= rom[addr_b_i]; - end - end - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/shifters/iob_pack/hardware/src/iob_pack.v b/lib/hardware/shifters/iob_pack/hardware/src/iob_pack.v deleted file mode 100644 index 133fdd51b..000000000 --- a/lib/hardware/shifters/iob_pack/hardware/src/iob_pack.v +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_pack #( - parameter DATA_W = 21 -) ( - `include "iob_pack_clk_en_rst_s_port.vs" - - input rst_i, - input wrap_i, - input [$clog2(DATA_W):0] width_i, - - //read unpacked data to be packed - output read_o, - input rready_i, - input [DATA_W-1:0] rdata_i, - - //write packed data - output write_o, - input wready_i, - output [DATA_W-1:0] wdata_o -); - - localparam CALC_POP_WIDTH = 2'd0; - localparam WAIT_DATA = 2'd1; - localparam PUSH_DATA = 2'd2; - localparam WAIT_POP = 2'd3; - - //input fifo read - reg read; - - //bit fifo control - wire [$clog2(2*DATA_W):0] push_level; - reg push; - - wire [$clog2(2*DATA_W):0] pop_level; - reg [ $clog2(DATA_W):0] pop_width; - reg pop; - - //wrap control accumulator - reg [ $clog2(DATA_W):0] wrap_acc_nxt; - wire [ $clog2(DATA_W):0] wrap_acc; - reg [$clog2(DATA_W)+1:0] wrap_acc_int; - - //program counter - wire [ 1:0] pcnt; - reg [ 1:0] pcnt_nxt; - - //read unpacked data from external input fifo - assign read_o = read; - //write packed data to external output fifo - assign write_o = pop; - - //control logic - always @* begin - pop_width = wrap_i ? wrap_acc : DATA_W; - wrap_acc_int = {1'd0, wrap_acc} + {1'd0, width_i}; - - //defaults - pop = 1'b0; - push = 1'b0; - read = 1'b0; - wrap_acc_nxt = wrap_acc; - pcnt_nxt = pcnt + 1'b1; - - case (pcnt) - CALC_POP_WIDTH: begin //compute pop width - if (wrap_i && (wrap_acc_int <= DATA_W)) begin - pcnt_nxt = pcnt; - wrap_acc_nxt = wrap_acc_int[0+:$clog2(DATA_W)+1]; - end - end - WAIT_DATA: begin //wait to read data from input fifo - if (rready_i && (push_level >= {1'd0, width_i})) begin - read = 1'b1; - end else begin - pcnt_nxt = WAIT_POP; - end - end - PUSH_DATA: begin //push data to bit fifo - push = 1'b1; - end - default: begin //wait to pop data from bit fifo - if ((pop_level >= {1'd0, pop_width}) && wready_i) begin - pop = 1'b1; - end - pcnt_nxt = WAIT_DATA; - end - endcase - end - - //bit fifo - iob_bfifo #( - .DATA_W(DATA_W) - ) bfifo ( - `include "iob_pack_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - //push unpacked data to be packed - .write_i (push), - .wwidth_i(width_i), - .wdata_i (rdata_i), - .wlevel_o(push_level), - //pop packed data to be output - .read_i (pop), - .rwidth_i(pop_width), - .rdata_o (wdata_o), - .rlevel_o(pop_level) - ); - - //wrap control accumulator - iob_reg_r #( - .DATA_W ($clog2(DATA_W) + 1), - .RST_VAL({$clog2(DATA_W) + 1{1'b0}}) - ) wrap_acc_reg ( - `include "iob_pack_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(wrap_acc_nxt), - .data_o(wrap_acc) - ); - - //pcnt register (state counter) - iob_reg_r #( - .DATA_W (2), - .RST_VAL(2'b0) - ) pcnt_reg ( - `include "iob_pack_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(pcnt_nxt), - .data_o(pcnt) - ); - -endmodule - - diff --git a/lib/hardware/shifters/iob_pack/iob_pack.py b/lib/hardware/shifters/iob_pack/iob_pack.py deleted file mode 100644 index 0d31bcf9c..000000000 --- a/lib/hardware/shifters/iob_pack/iob_pack.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_bfifo", - "instance_name": "iob_bfifo_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/shifters/iob_piso_reg/iob_piso_reg.py b/lib/hardware/shifters/iob_piso_reg/iob_piso_reg.py deleted file mode 100644 index 867770a0b..000000000 --- a/lib/hardware/shifters/iob_piso_reg/iob_piso_reg.py +++ /dev/null @@ -1,104 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "32", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - { - "name": "ld_i", - "descr": "Input port", - "signals": [ - { - "name": "ld", - "width": 1, - "direction": "input", - }, - ], - }, - { - "name": "p_i", - "descr": "Input port", - "signals": [ - { - "name": "p", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "s_o", - "descr": "Output port", - "signals": [ - { - "name": "s", - "width": 1, - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "data_reg_int", - "descr": "data_reg_int wire", - "signals": [ - {"name": "data_reg_int", "width": "DATA_W"}, - ], - }, - { - "name": "data_int", - "descr": "data_int wire", - "signals": [ - {"name": "data_int", "width": "DATA_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_reg", - "instance_name": "reg0", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": 0, - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "data_int", - "data_o": "data_reg_int", - }, - }, - ], - "snippets": [ - { - "verilog_code": """ - assign data_int = ld_i ? p_i : data_reg_int << 1'b1; - assign s_o = data_reg_int[DATA_W-1]; - - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/shifters/iob_shift_reg/hardware/simulation/src/iob_shift_reg_tb.v b/lib/hardware/shifters/iob_shift_reg/hardware/simulation/src/iob_shift_reg_tb.v deleted file mode 100644 index 158c86b4a..000000000 --- a/lib/hardware/shifters/iob_shift_reg/hardware/simulation/src/iob_shift_reg_tb.v +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_shift_reg_tb; - - localparam DATA_W = 8; - localparam N = 10; - localparam ADDR_W = $clog2(N); - - localparam TESTSIZE = 2 ** ADDR_W; - - reg arst = 0; - reg clk = 0; - reg cke = 1; - - reg rst = 0; - reg en = 0; - reg ld = 0; - - reg [DATA_W-1:0] data_i; - wire [DATA_W-1:0] data_o; - - - parameter CLK_PER = 10; // clk period = 10 timeticks - always #(CLK_PER / 2) clk = ~clk; - - integer i, j; //iterators - integer fd; - - reg [TESTSIZE*DATA_W-1:0] test_data; - reg [TESTSIZE*DATA_W-1:0] read_data; - - //FIFO memory - wire ext_mem_clk; - wire ext_mem_w_en; - wire [ DATA_W-1:0] ext_mem_w_data; - wire [ ADDR_W-1:0] ext_mem_w_addr; - wire ext_mem_r_en; - wire [ ADDR_W-1:0] ext_mem_r_addr; - wire [ DATA_W-1:0] ext_mem_r_data; - - - //WRITE - initial begin - //create the test data - for (i = 0; i < TESTSIZE; i = i + 1) test_data[i*DATA_W+:DATA_W] = i[0+:DATA_W]; - - // optional VCD -`ifdef VCD - $dumpfile("uut.vcd"); - $dumpvars(); -`endif - repeat (4) @(posedge clk) #1; - en = 0; - - #8 arst = 1; - #CLK_PER @(posedge clk) #1; - arst = 0; - repeat (4) @(posedge clk) #1; - ld = 1; - @(posedge clk) #1; - ld = 0; - repeat (4) @(posedge clk) #1; - - for (i = 0; i < 2 ** (ADDR_W + 1); i = i + 1) begin - en = 1; - data_i = test_data[i*DATA_W+:DATA_W]; - if (i < N && data_o !== 0) begin - $fatal(1, "ERROR: got %d, expected 0 while not full\n", data_o); - end - if (i >= N && data_o !== test_data[(i-N)*DATA_W+:DATA_W]) begin - $fatal(1, "ERROR: got %d, expected %d", data_o, test_data[(i-N)*DATA_W+:DATA_W]); - end - @(posedge clk) #1; - end - en = 0; - - $display("%c[1;34m", 27); - $display("Test completed successfully."); - $display("%c[0m", 27); - - fd = $fopen("test.log", "w"); - $fdisplay(fd, "Test passed!"); - $fclose(fd); - - #1000 $finish(); - - end - - // Instantiate the Unit Under Test (UUT) - iob_shift_reg #( - .DATA_W(DATA_W), - .N(N) - ) uut ( - .clk_i (clk), - .arst_i(arst), - .cke_i (cke), - - .en_i (en), - .rst_i (rst), - .data_i(data_i), - .data_o(data_o), - - .ext_mem_clk_o (ext_mem_clk), - .ext_mem_w_en_o (ext_mem_w_en), - .ext_mem_w_addr_o(ext_mem_w_addr), - .ext_mem_w_data_o(ext_mem_w_data), - .ext_mem_r_en_o (ext_mem_r_en), - .ext_mem_r_addr_o(ext_mem_r_addr), - .ext_mem_r_data_i(ext_mem_r_data) - ); - - iob_ram_t2p #( - .DATA_W(DATA_W), - .ADDR_W(ADDR_W) - ) iob_ram_t2p_inst ( - .clk_i (ext_mem_clk), - .w_en_i (ext_mem_w_en), - .w_addr_i(ext_mem_w_addr), - .w_data_i(ext_mem_w_data), - .r_en_i (ext_mem_r_en), - .r_addr_i(ext_mem_r_addr), - .r_data_o(ext_mem_r_data) - ); - -endmodule diff --git a/lib/hardware/shifters/iob_shift_reg/hardware/src/iob_shift_reg.v b/lib/hardware/shifters/iob_shift_reg/hardware/src/iob_shift_reg.v deleted file mode 100644 index d2aad570a..000000000 --- a/lib/hardware/shifters/iob_shift_reg/hardware/src/iob_shift_reg.v +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - - -module iob_shift_reg #( - parameter DATA_W = 21, - parameter N = 21, - parameter ADDR_W = $clog2(N) - -) ( - `include "iob_shift_reg_clk_en_rst_s_port.vs" - - input en_i, - input rst_i, - input [DATA_W-1:0] data_i, - output [DATA_W-1:0] data_o, - - //memory clock - output ext_mem_clk_o, - //memory write port - output ext_mem_w_en_o, - output [ADDR_W-1:0] ext_mem_w_addr_o, - output [DATA_W-1:0] ext_mem_w_data_o, - //read port - output ext_mem_r_en_o, - output [ADDR_W-1:0] ext_mem_r_addr_o, - input [DATA_W-1:0] ext_mem_r_data_i -); - - //address - wire [ADDR_W-1:0] addr_w; - reg [ADDR_W-1:0] addr_r; - - wire out_en; - wire out_en_nxt; - - wire rst_int_w; - wire rst_int_r; - - - assign data_o = ext_mem_r_data_i & {DATA_W{out_en}}; - - assign ext_mem_clk_o = clk_i; - - assign ext_mem_w_en_o = en_i; - assign ext_mem_w_addr_o = addr_w; - assign ext_mem_w_data_o = data_i; - - assign ext_mem_r_en_o = en_i; - assign ext_mem_r_addr_o = addr_r; - - - //counter enable - assign out_en_nxt = out_en | (addr_w == (N - 1)); - - assign rst_int_w = rst_i | (addr_w == (N - 1)); - assign rst_int_r = rst_i | (addr_r == (N - 1)); - - always @* begin - if (addr_w == (N - 1)) begin - addr_r = 0; - end else begin - addr_r = addr_w + 1'b1; - end - end - - //write address - iob_counter #( - .DATA_W (ADDR_W), - .RST_VAL({ADDR_W{1'd0}}) - ) w_addr_cnt0 ( - `include "iob_shift_reg_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_int_w), - .en_i (en_i), - .data_o(addr_w) - ); - - iob_reg #( - .DATA_W (1), - .RST_VAL(0) - ) out_enable ( - `include "iob_shift_reg_clk_en_rst_s_s_portmap.vs" - .data_i(out_en_nxt), - .data_o(out_en) - ); - - -endmodule diff --git a/lib/hardware/shifters/iob_shift_reg/iob_shift_reg.py b/lib/hardware/shifters/iob_shift_reg/iob_shift_reg.py deleted file mode 100644 index eb5c4fb1a..000000000 --- a/lib/hardware/shifters/iob_shift_reg/iob_shift_reg.py +++ /dev/null @@ -1,38 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "Clock, clock enable and reset", - }, - # TODO: Remaining ports - ], - "blocks": [ - { - "core_name": "iob_reg", - "instance_name": "iob_reg_inst", - }, - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - }, - # For simulation - { - "core_name": "iob_ram_t2p", - "instance_name": "iob_ram_t2p_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/shifters/iob_sipo_reg/hardware/src/iob_sipo_reg.v b/lib/hardware/shifters/iob_sipo_reg/hardware/src/iob_sipo_reg.v deleted file mode 100644 index 2fa7b56b2..000000000 --- a/lib/hardware/shifters/iob_sipo_reg/hardware/src/iob_sipo_reg.v +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_sipo_reg #( - parameter DATA_W = 21 -) ( - `include "iob_sipo_reg_clk_en_rst_s_port.vs" - //serial input - input s_i, - //parallel output - output [DATA_W-1:0] p_o -); - - wire [DATA_W-1:0] data; - assign data = {p_o[DATA_W-2:0], s_i}; - - iob_reg #( - .DATA_W (DATA_W), - .RST_VAL(0) - ) reg0 ( - `include "iob_sipo_reg_clk_en_rst_s_s_portmap.vs" - .data_i(data), - .data_o(p_o) - ); - -endmodule diff --git a/lib/hardware/shifters/iob_sipo_reg/iob_sipo_reg.py b/lib/hardware/shifters/iob_sipo_reg/iob_sipo_reg.py deleted file mode 100644 index 400b8811c..000000000 --- a/lib/hardware/shifters/iob_sipo_reg/iob_sipo_reg.py +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_counter", - "instance_name": "iob_counter_inst", - }, - { - "core_name": "iob_reg", - "instance_name": "iob_reg_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/shifters/iob_unpack/hardware/src/iob_unpack.v b/lib/hardware/shifters/iob_unpack/hardware/src/iob_unpack.v deleted file mode 100644 index d476588db..000000000 --- a/lib/hardware/shifters/iob_unpack/hardware/src/iob_unpack.v +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_unpack #( - parameter DATA_W = 21 -) ( - `include "iob_unpack_clk_en_rst_s_port.vs" - - input rst_i, - input wrap_i, - input [$clog2(DATA_W):0] width_i, - - //read packed data to be unpacked - output read_o, - input rready_i, - input [DATA_W-1:0] rdata_i, - - //write unpacked data - output write_o, - input wready_i, - output [DATA_W-1:0] wdata_o -); - - localparam CALC_PUSH_WIDTH = 2'd0; - localparam WAIT_DATA = 2'd1; - localparam PUSH_DATA = 2'd2; - localparam POP_DATA = 2'd3; - - //input fifo read - reg read; - - //bit fifo control - wire [$clog2(2*DATA_W):0] push_level; - reg [ $clog2(DATA_W):0] push_width; - reg push; - - wire [$clog2(2*DATA_W):0] pop_level; - reg pop; - - //wrap control accumulator - reg [ $clog2(DATA_W):0] wrap_acc_nxt; - wire [ $clog2(DATA_W):0] wrap_acc; - reg [$clog2(DATA_W)+1:0] wrap_acc_int; - - //program counter - wire [ 1:0] pcnt; - reg [ 1:0] pcnt_nxt; - - //read unpacked data from external input fifo - assign read_o = read; - //write packed data to external output fifo - assign write_o = pop; - - //control logic - always @* begin - push_width = wrap_i ? wrap_acc : DATA_W; - wrap_acc_int = {1'd0, wrap_acc} + {1'd0, width_i}; - - //defaults - pop = 1'b0; - push = 1'b0; - read = 1'b0; - wrap_acc_nxt = wrap_acc; - pcnt_nxt = pcnt + 1'b1; - - case (pcnt) - CALC_PUSH_WIDTH: begin //compute push width - if (wrap_i && (wrap_acc_int <= DATA_W)) begin - pcnt_nxt = pcnt; - wrap_acc_nxt = wrap_acc_int[0+:$clog2(DATA_W)+1]; - end - end - WAIT_DATA: begin //wait to read data from input fifo - if (rready_i && (push_level >= {1'd0, push_width})) begin - read = 1'b1; - end else begin - pcnt_nxt = POP_DATA; - end - end - PUSH_DATA: begin //push data to bit fifo - push = 1'b1; - end - default: begin //wait to pop data from bit fifo - if ((pop_level >= {1'd0, width_i}) && wready_i) begin - pop = 1'b1; - end - pcnt_nxt = WAIT_DATA; - end - endcase - end - - //bit fifo - iob_bfifo #( - .DATA_W(DATA_W) - ) bfifo ( - `include "iob_unpack_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - //push packed data to be unpacked - .write_i (push), - .wwidth_i(push_width), - .wdata_i (rdata_i), - .wlevel_o(push_level), - //pop unpacked data to be output - .read_i (pop), - .rwidth_i(width_i), - .rdata_o (wdata_o), - .rlevel_o(pop_level) - ); - - //wrap control accumulator - iob_reg_r #( - .DATA_W ($clog2(DATA_W) + 1), - .RST_VAL({$clog2(DATA_W) + 1{1'b0}}) - ) wrap_acc_reg ( - `include "iob_unpack_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(wrap_acc_nxt), - .data_o(wrap_acc) - ); - - //pcnt register (state counter) - iob_reg_r #( - .DATA_W (2), - .RST_VAL(2'b0) - ) pcnt_reg ( - `include "iob_unpack_clk_en_rst_s_s_portmap.vs" - .rst_i (rst_i), - .data_i(pcnt_nxt), - .data_o(pcnt) - ); - -endmodule - - diff --git a/lib/hardware/shifters/iob_unpack/iob_unpack.py b/lib/hardware/shifters/iob_unpack/iob_unpack.py deleted file mode 100644 index 0d31bcf9c..000000000 --- a/lib/hardware/shifters/iob_unpack/iob_unpack.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_bfifo", - "instance_name": "iob_bfifo_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/synchronizers/iob_f2s_1bit_sync/hardware/src/iob_f2s_1bit_sync.v b/lib/hardware/synchronizers/iob_f2s_1bit_sync/hardware/src/iob_f2s_1bit_sync.v deleted file mode 100644 index dacfce6da..000000000 --- a/lib/hardware/synchronizers/iob_f2s_1bit_sync/hardware/src/iob_f2s_1bit_sync.v +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2024 IObundle -// -// SPDX-License-Identifier: MIT - -`timescale 1ns / 1ps - -module iob_f2s_1bit_sync ( - input clk_i, - input cke_i, - input value_i, - output value_o -); - - wire [1:0] sync; - wire [1:0] data; - assign data = {sync[0], 1'b0}; - - iob_reg #(2, 1) reg0 ( - .clk_i (clk_i), - .arst_i(value_i), - .cke_i (cke_i), - - .data_i(data), - .data_o(sync) - ); - - assign value_o = sync[1]; - -endmodule diff --git a/lib/hardware/synchronizers/iob_f2s_1bit_sync/iob_f2s_1bit_sync.py b/lib/hardware/synchronizers/iob_f2s_1bit_sync/iob_f2s_1bit_sync.py deleted file mode 100644 index 3a7d37d81..000000000 --- a/lib/hardware/synchronizers/iob_f2s_1bit_sync/iob_f2s_1bit_sync.py +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "blocks": [ - { - "core_name": "iob_reg", - "instance_name": "iob_reg_inst", - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/synchronizers/iob_neg2posedge_sync/iob_neg2posedge_sync.py b/lib/hardware/synchronizers/iob_neg2posedge_sync/iob_neg2posedge_sync.py deleted file mode 100644 index f064a5a9d..000000000 --- a/lib/hardware/synchronizers/iob_neg2posedge_sync/iob_neg2posedge_sync.py +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "RST_VAL", - "type": "P", - "val": "{2 * DATA_W{1'b0}}", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - ], - "ports": [ - { - "name": "clk_en_rst_s", - "interface": { - "type": "clk_en_rst", - "subtype": "slave", - }, - "descr": "clock, clock enable and reset", - }, - { - "name": "signal_i", - "descr": "Input port", - "signals": [ - { - "name": "signal", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "signal_o", - "descr": "Output port", - "signals": [ - { - "name": "signal", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "synchronizer", - "descr": "synchronizer wire", - "signals": [ - {"name": "synchronizer", "width": "DATA_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_regn", - "instance_name": "reg1", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "RST_VAL", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "signal_i", - "data_o": "synchronizer", - }, - }, - { - "core_name": "iob_reg", - "instance_name": "reg2", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "RST_VAL", - }, - "connect": { - "clk_en_rst_s": "clk_en_rst_s", - "data_i": "synchronizer", - "data_o": "signal_o", - }, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/synchronizers/iob_reset_sync/iob_reset_sync.py b/lib/hardware/synchronizers/iob_reset_sync/iob_reset_sync.py deleted file mode 100644 index eab7f6879..000000000 --- a/lib/hardware/synchronizers/iob_reset_sync/iob_reset_sync.py +++ /dev/null @@ -1,76 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -edge = 1 - - -def setup(py_params_dict): - global edge - if "RST_POL" in py_params_dict: - edge = py_params_dict["RST_POL"] - attributes_dict = { - "version": "0.1", - "ports": [ - { - "name": "clk_rst_s", - "interface": { - "type": "clk_rst", - "subtype": "slave", - }, - "descr": "clock and reset", - }, - { - "name": "arst_o", - "descr": "Output port", - "signals": [ - { - "name": "arst", - "width": 1, - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "data_int", - "descr": "data_int wire", - "signals": [ - {"name": "data_int", "width": 2}, - ], - }, - { - "name": "sync", - "descr": "sync wire", - "signals": [ - {"name": "sync", "width": 2}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_r", - "instance_name": "reg1", - "parameters": { - "DATA_W": 2, - "RST_VAL": "2'd3" if edge else "2'd0", - }, - "connect": { - "clk_rst_s": "clk_rst_s", - "iob_r_data_i": "data_int", - "iob_r_data_o": "sync", - }, - }, - ], - "snippets": [ - { - "verilog_code": f""" - assign data_int = {{sync[0], {"1'b0" if edge else "1'b1"}}}; - assign arst_o = sync[1]; - """, - }, - ], - } - - return attributes_dict diff --git a/lib/hardware/synchronizers/iob_sync/iob_sync.py b/lib/hardware/synchronizers/iob_sync/iob_sync.py deleted file mode 100644 index 4263ce26d..000000000 --- a/lib/hardware/synchronizers/iob_sync/iob_sync.py +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "confs": [ - { - "name": "DATA_W", - "type": "P", - "val": "21", - "min": "NA", - "max": "NA", - "descr": "Data bus width", - }, - { - "name": "RST_VAL", - "type": "P", - "val": "{DATA_W{1'b0}}", - "min": "NA", - "max": "NA", - "descr": "Reset value.", - }, - ], - "ports": [ - { - "name": "clk_rst_s", - "interface": { - "type": "clk_rst", - "subtype": "slave", - }, - "descr": "Clock and reset", - }, - { - "name": "signal_i", - "descr": "Input port", - "signals": [ - { - "name": "signal", - "width": "DATA_W", - "direction": "input", - }, - ], - }, - { - "name": "signal_o", - "descr": "Output port", - "signals": [ - { - "name": "signal", - "width": "DATA_W", - "direction": "output", - }, - ], - }, - ], - "wires": [ - { - "name": "synchronizer", - "descr": "synchronizer wire", - "signals": [ - {"name": "synchronizer", "width": "DATA_W"}, - ], - }, - ], - "blocks": [ - { - "core_name": "iob_r", - "instance_name": "reg1", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "RST_VAL", - }, - "connect": { - "clk_rst_s": "clk_rst_s", - "iob_r_data_i": "signal_i", - "iob_r_data_o": "synchronizer", - }, - }, - { - "core_name": "iob_r", - "instance_name": "reg2", - "parameters": { - "DATA_W": "DATA_W", - "RST_VAL": "RST_VAL", - }, - "connect": { - "clk_rst_s": "clk_rst_s", - "iob_r_data_i": "synchronizer", - "iob_r_data_o": "signal_o", - }, - }, - ], - } - - return attributes_dict diff --git a/lib/scripts/default.nix b/lib/scripts/default.nix index 2d378d29b..774d888e4 100644 --- a/lib/scripts/default.nix +++ b/lib/scripts/default.nix @@ -5,8 +5,8 @@ { pkgs ? import {} }: let - py2hwsw_commit = "1b80e60335d43f1912a0dd80a97bef7b18bdea57"; # Replace with the desired commit. - py2hwsw_sha256 = "Ny0kpLwJy9/zC2JdEVefxWSj1uIRzSoNEPKu4OMIFo4="; # Replace with the actual SHA256 hash. + py2hwsw_commit = "cb11eb4ec2033a57fb164b7e547b91d2e85ec3d6"; # Replace with the desired commit. + py2hwsw_sha256 = "rmXq4bOVj74iX875OYOLeMKqm+iEDSnDXoo6hnjFhUY="; # Replace with the actual SHA256 hash. py2hwsw = pkgs.python3.pkgs.buildPythonPackage rec { pname = "py2hwsw"; diff --git a/lib/scripts/test.sh b/lib/scripts/test.sh deleted file mode 100755 index ef4087311..000000000 --- a/lib/scripts/test.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -set -e - -#find directories containing testbenches -TBS=`find ${LIB_DIR}/hardware | grep _tb.v | grep -v include` - -#extract respective directories -for i in $TBS; do TB_DIRS+=" `dirname $i`" ; done - -#extract respective modules - go back from MODULE/hardware/simulation/src -for i in $TB_DIRS; do MODULES+=" `basename $(builtin cd $i/../../..; pwd)`" ; done - -#test first argument is "clean", run make clean for all modules and exit -if [ "$1" == "clean" ]; then - for i in $MODULES; do - DEFAULT_BUILD_DIR=`py2hwsw $i print_build_dir` - make clean CORE=$i BUILD_DIR=../${DEFAULT_BUILD_DIR} - done - exit 0 -fi - -#test if first argument is test and run all tests -if [ "$1" == "test" ]; then - for i in $MODULES; do - echo -e "\n\033[1;33mTesting module '${i}'\033[0m" - DEFAULT_BUILD_DIR=`py2hwsw $i print_build_dir` - make -f ${LIB_DIR}/Makefile clean setup CORE=$i BUILD_DIR=../${DEFAULT_BUILD_DIR} - make -C ../${DEFAULT_BUILD_DIR} sim-run - done - exit 0 -fi - -#test if first argument is "build" and run build for single module -if [ "$1" == "build" ]; then - DEFAULT_BUILD_DIR=`py2hwsw $2 print_build_dir` - make clean setup CORE=$2 BUILD_DIR=../${DEFAULT_BUILD_DIR} - make -C ../${DEFAULT_BUILD_DIR} sim-build - exit 0 -fi - -#run single test -DEFAULT_BUILD_DIR=`py2hwsw $1 print_build_dir` -make clean setup CORE=$1 BUILD_DIR=../${DEFAULT_BUILD_DIR} -make -C ../${DEFAULT_BUILD_DIR} sim-run VCD=$VCD diff --git a/lib/software/modules/emb_utils/software/src/iob_emb_utils.c b/lib/software/modules/emb_utils/software/src/iob_emb_utils.c deleted file mode 100644 index d1a7d4fba..000000000 --- a/lib/software/modules/emb_utils/software/src/iob_emb_utils.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -void perror(char *s) { - printf("ERROR: %s", s); - uart_finish(); -} diff --git a/lib/software/modules/emb_utils/software/src/iob_emb_utils.h b/lib/software/modules/emb_utils/software/src/iob_emb_utils.h deleted file mode 100644 index 22748a26a..000000000 --- a/lib/software/modules/emb_utils/software/src/iob_emb_utils.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -void perror(char *s); diff --git a/lib/software/modules/iob_str/iob_str.py b/lib/software/modules/iob_str/iob_str.py deleted file mode 100644 index 96aa5f364..000000000 --- a/lib/software/modules/iob_str/iob_str.py +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "instantiate": False, - } - - return attributes_dict diff --git a/lib/software/modules/iob_str/software/src/iob_str.c b/lib/software/modules/iob_str/software/src/iob_str.c deleted file mode 100644 index cc1e54d4d..000000000 --- a/lib/software/modules/iob_str/software/src/iob_str.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -// copy src to dst and return number of copied chars (excluding '\0') -int iob_strcpy(char *dst, char *src) { - if (dst == NULL || src == NULL) { - return -1; - } - int cnt = 0; - while (src[cnt] != 0) { - dst[cnt] = src[cnt]; - cnt++; - } - dst[cnt] = '\0'; - return cnt; -} - -// return 0 if equal, 1 if not equal -int iob_strcmp(char *str1, char *str2, int str_size) { - int c = 0; - while (c < str_size) { - if (str1[c] != str2[c]) { - return str1[c] - str2[c]; - } - c++; - } - return 0; -} - -// return string length -int iob_strlen(char *str) { - int c = 0; - while (str[c] != 0) { - c++; - } - return c; -} - -// concatenate src to dst up to max_len chars -// return concatenated string -char *iob_strncat(char *dst, char *src, int max_len) { - int i = 0; - int j = 0; - - while ((dst[i] != '\0')) { - i++; - } - - while ((src[j] != '\0') && (j < max_len)) { - dst[i++] = src[j++]; - } - dst[i] = '\0'; - - return dst; -} diff --git a/lib/software/modules/iob_str/software/src/iob_str.h b/lib/software/modules/iob_str/software/src/iob_str.h deleted file mode 100644 index 3000982e6..000000000 --- a/lib/software/modules/iob_str/software/src/iob_str.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -int iob_strcpy(char *dst, char *src); -int iob_strcmp(char *str1, char *str2, int str_size); -int iob_strlen(char *str); -char *iob_strncat(char *dst, char *src, int max_len); diff --git a/lib/software/modules/printf/printf.py b/lib/software/modules/printf/printf.py deleted file mode 100644 index 96aa5f364..000000000 --- a/lib/software/modules/printf/printf.py +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - - -def setup(py_params_dict): - attributes_dict = { - "version": "0.1", - "generate_hw": False, - "instantiate": False, - } - - return attributes_dict diff --git a/lib/software/modules/printf/software/src/printf.c b/lib/software/modules/printf/software/src/printf.c deleted file mode 100644 index 1d918aaa9..000000000 --- a/lib/software/modules/printf/software/src/printf.c +++ /dev/null @@ -1,968 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2019, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// 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. -// -// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for -// speed on -// embedded systems with a very limited resources. These routines are -// thread safe and reentrant! Use this instead of the bloated -// standard/newlib printf cause these use malloc for printf (and may not -// be thread safe). -// -/////////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include "printf.h" - -// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the -// printf_config.h header file -// default: undefined -#ifdef PRINTF_INCLUDE_CONFIG_H -#include "printf_config.h" -#endif - -// 'ntoa' conversion buffer size, this must be big enough to hold one converted -// numeric number including padded zeros (dynamically created on stack) -// default: 32 byte -#ifndef PRINTF_NTOA_BUFFER_SIZE -#define PRINTF_NTOA_BUFFER_SIZE 32U -#endif - -// 'ftoa' conversion buffer size, this must be big enough to hold one converted -// float number including padded zeros (dynamically created on stack) -// default: 32 byte -#ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32U -#endif - -// support for the floating point type (%f) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_FLOAT -#define PRINTF_SUPPORT_FLOAT -#endif - -// support for exponential floating point notation (%e/%g) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL -#define PRINTF_SUPPORT_EXPONENTIAL -#endif - -// define the default floating point precision -// default: 6 digits -#ifndef PRINTF_DEFAULT_FLOAT_PRECISION -#define PRINTF_DEFAULT_FLOAT_PRECISION 6U -#endif - -// define the largest float suitable to print with %f -// default: 1e9 -#ifndef PRINTF_MAX_FLOAT -#define PRINTF_MAX_FLOAT 1e9 -#endif - -// support for the long long types (%llu or %p) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG -#define PRINTF_SUPPORT_LONG_LONG -#endif - -// support for the ptrdiff_t type (%t) -// ptrdiff_t is normally defined in as long or long long type -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T -#define PRINTF_SUPPORT_PTRDIFF_T -#endif - -/////////////////////////////////////////////////////////////////////////////// - -// internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_LONG (1U << 8U) -#define FLAGS_LONG_LONG (1U << 9U) -#define FLAGS_PRECISION (1U << 10U) -#define FLAGS_ADAPT_EXP (1U << 11U) - -// import float.h for DBL_MAX -#if defined(PRINTF_SUPPORT_FLOAT) -#include -#endif - -// output function type -typedef void (*out_fct_type)(char character, void *buffer, size_t idx, - size_t maxlen); - -// wrapper (used as buffer) for output function type -typedef struct { - void (*fct)(char character, void *arg); - void *arg; -} out_fct_wrap_type; - -// Function pointer for uart -static void (*g_uart_putc_ptr)(char); - -// Initialize pointer function for uart -void printf_init(void (*uart_putc_ptr)(char)) { - g_uart_putc_ptr = uart_putc_ptr; -} - -// internal buffer output -static inline void _out_buffer(char character, void *buffer, size_t idx, - size_t maxlen) { - if (idx < maxlen) { - ((char *)buffer)[idx] = character; - } -} - -// internal null output -static inline void _out_null(char character, void *buffer, size_t idx, - size_t maxlen) { - (void)character; - (void)buffer; - (void)idx; - (void)maxlen; -} - -// internal _putchar wrapper -static inline void _out_char(char character, void *buffer, size_t idx, - size_t maxlen) { - (void)buffer; - (void)idx; - (void)maxlen; - if (character) { - (*g_uart_putc_ptr)(character); - } -} - -// internal output function wrapper -static inline void _out_fct(char character, void *buffer, size_t idx, - size_t maxlen) { - (void)idx; - (void)maxlen; - if (character) { - // buffer is the output fct pointer - ((out_fct_wrap_type *)buffer) - ->fct(character, ((out_fct_wrap_type *)buffer)->arg); - } -} - -// internal secure strlen -// \return The length of the string (excluding the terminating 0) limited by -// 'maxsize' -static inline unsigned int _strnlen_s(const char *str, size_t maxsize) { - const char *s; - for (s = str; *s && maxsize--; ++s) - ; - return (unsigned int)(s - str); -} - -// internal test if char is a digit (0-9) -// \return true if char is a digit -static inline bool _is_digit(char ch) { return (ch >= '0') && (ch <= '9'); } - -// internal ASCII string to unsigned int conversion -static unsigned int _atoi(const char **str) { - unsigned int i = 0U; - while (_is_digit(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; -} - -// output the specified string in reverse, taking care of any zero-padding -static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, - size_t maxlen, const char *buf, size_t len, - unsigned int width, unsigned int flags) { - const size_t start_idx = idx; - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - while (len) { - out(buf[--len], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} - -// internal itoa format -static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, - size_t maxlen, char *buf, size_t len, bool negative, - unsigned int base, unsigned int prec, - unsigned int width, unsigned int flags) { - // pad leading zeros - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && - (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && - (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - // handle hash - if (flags & FLAGS_HASH) { - if (!(flags & FLAGS_PRECISION) && len && - ((len == prec) || (len == width))) { - len--; - if (len && (base == 16U)) { - len--; - } - } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && - (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; - } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && - (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; - } else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_NTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); -} - -// internal itoa for 'long' type -static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, - size_t maxlen, unsigned long value, bool negative, - unsigned long base, unsigned int prec, - unsigned int width, unsigned int flags) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 - ? '0' + digit - : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, - (unsigned int)base, prec, width, flags); -} - -// internal itoa for 'long long' type -#if defined(PRINTF_SUPPORT_LONG_LONG) -static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, - size_t maxlen, unsigned long long value, - bool negative, unsigned long long base, - unsigned int prec, unsigned int width, - unsigned int flags) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 - ? '0' + digit - : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, - (unsigned int)base, prec, width, flags); -} -#endif // PRINTF_SUPPORT_LONG_LONG - -#if defined(PRINTF_SUPPORT_FLOAT) - -#if defined(PRINTF_SUPPORT_EXPONENTIAL) -// forward declaration so that _ftoa can switch to exp notation for values > -// PRINTF_MAX_FLOAT -static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, - double value, unsigned int prec, unsigned int width, - unsigned int flags); -#endif - -// internal ftoa for fixed decimal floating point -static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, - double value, unsigned int prec, unsigned int width, - unsigned int flags) { - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - double diff = 0.0; - - // powers of 10 - static const double pow10[] = {1, 10, 100, 1000, - 10000, 100000, 1000000, 10000000, - 100000000, 1000000000}; - - // test for special values - if (value != value) - return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); - if (value < -DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); - if (value > DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, - (flags & FLAGS_PLUS) ? "fni+" : "fni", - (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - - // test for very large values - // standard printf behavior is to print EVERY whole number digit -- which - // could be 100s of characters overflowing your buffers == bad - if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { -#if defined(PRINTF_SUPPORT_EXPONENTIAL) - return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); -#else - return 0U; -#endif - } - - // test for negative - bool negative = false; - if (value < 0) { - negative = true; - value = 0 - value; - } - - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { - buf[len++] = '0'; - prec--; - } - - int whole = (int)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; - diff = tmp - frac; - - if (diff > 0.5) { - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if (frac >= pow10[prec]) { - frac = 0; - ++whole; - } - } else if (diff < 0.5) { - } else if ((frac == 0U) || (frac & 1U)) { - // if halfway, round up if odd OR if last digit is 0 - ++frac; - } - - if (prec == 0U) { - diff = value - (double)whole; - if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } else { - unsigned int count = prec; - // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { - --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { - buf[len++] = '0'; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - // add decimal - buf[len++] = '.'; - } - } - - // do whole part, number is reversed - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { - break; - } - } - - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { - if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); -} - -#if defined(PRINTF_SUPPORT_EXPONENTIAL) -// internal ftoa variant for exponential floating-point type, contributed by -// Martijn Jasperse -static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, - double value, unsigned int prec, unsigned int width, - unsigned int flags) { - // check for NaN and special values - if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { - return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); - } - - // determine the sign - const bool negative = value < 0; - if (negative) { - value = -value; - } - - // default precision - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - // determine the decimal exponent - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - union { - uint64_t U; - double F; - } conv; - - conv.F = value; - int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 - conv.U = (conv.U & ((1ULL << 52U) - 1U)) | - (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) - // now approximate log10 from the log2 integer part and an expansion of ln - // around 1.5 - int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + - (conv.F - 1.5) * 0.289529654602168); - // now we want to compute 10^expval but we want to be sure it won't overflow - exp2 = (int)(expval * 3.321928094887362 + 0.5); - const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; - // compute exp(z) using continued fractions, see - // https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - // correct for rounding errors - if (value < conv.F) { - expval--; - conv.F /= 10; - } - - // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 - // characters - unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; - - // in "%g" mode, "prec" is the number of *significant figures* not decimals - if (flags & FLAGS_ADAPT_EXP) { - // do we want to fall-back to "%f" mode? - if ((value >= 1e-4) && (value < 1e6)) { - if ((int)prec > expval) { - prec = (unsigned)((int)prec - expval - 1); - } else { - prec = 0; - } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision - // no characters in exponent - minwidth = 0U; - expval = 0; - } else { - // we use one sigfig for the whole part - if ((prec > 0) && (flags & FLAGS_PRECISION)) { - --prec; - } - } - } - - // will everything fit? - unsigned int fwidth = width; - if (width > minwidth) { - // we didn't fall-back so subtract the characters required for the exponent - fwidth -= minwidth; - } else { - // not enough characters, so go back to default sizing - fwidth = 0U; - } - if ((flags & FLAGS_LEFT) && minwidth) { - // if we're padding on the right, DON'T pad the floating part - fwidth = 0U; - } - - // rescale the float value - if (expval) { - value /= conv.F; - } - - // output the floating part - const size_t start_idx = idx; - idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, - flags & ~FLAGS_ADAPT_EXP); - - // output the exponent part - if (minwidth) { - // output the exponential symbol - out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); - // output the exponent value - idx = - _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, - expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); - // might need to right-pad spaces - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) - out(' ', buffer, idx++, maxlen); - } - } - return idx; -} -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT - -// internal vsnprintf -static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, - const char *format, va_list va) { - unsigned int flags, width, precision, n; - size_t idx = 0U; - - if (!buffer) { - // use null output function - out = _out_null; - } - - while (*format) { - // format specifier? %[flags][width][.precision][length] - if (*format != '%') { - // no - out(*format, buffer, idx++, maxlen); - format++; - continue; - } else { - // yes, evaluate it - format++; - } - - // evaluate flags - flags = 0U; - do { - switch (*format) { - case '0': - flags |= FLAGS_ZEROPAD; - format++; - n = 1U; - break; - case '-': - flags |= FLAGS_LEFT; - format++; - n = 1U; - break; - case '+': - flags |= FLAGS_PLUS; - format++; - n = 1U; - break; - case ' ': - flags |= FLAGS_SPACE; - format++; - n = 1U; - break; - case '#': - flags |= FLAGS_HASH; - format++; - n = 1U; - break; - default: - n = 0U; - break; - } - } while (n); - - // evaluate width field - width = 0U; - if (_is_digit(*format)) { - width = _atoi(&format); - } else if (*format == '*') { - const int w = va_arg(va, int); - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (unsigned int)-w; - } else { - width = (unsigned int)w; - } - format++; - } - - // evaluate precision field - precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - format++; - if (_is_digit(*format)) { - precision = _atoi(&format); - } else if (*format == '*') { - const int prec = (int)va_arg(va, int); - precision = prec > 0 ? (unsigned int)prec : 0U; - format++; - } - } - - // evaluate length field - switch (*format) { - case 'l': - flags |= FLAGS_LONG; - format++; - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - format++; - } - break; - case 'h': - flags |= FLAGS_SHORT; - format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; -#if defined(PRINTF_SUPPORT_PTRDIFF_T) - case 't': - flags |= - (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; -#endif - case 'j': - flags |= - (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z': - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default: - break; - } - - // evaluate specifier - switch (*format) { - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - case 'b': { - // set the base - unsigned int base; - if (*format == 'x' || *format == 'X') { - base = 16U; - } else if (*format == 'o') { - base = 8U; - } else if (*format == 'b') { - base = 2U; - } else { - base = 10U; - flags &= ~FLAGS_HASH; // no hash for dec format - } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - } - - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; - } - - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - const long long value = va_arg(va, long long); - idx = _ntoa_long_long( - out, buffer, idx, maxlen, - (unsigned long long)(value > 0 ? value : 0 - value), value < 0, - base, precision, width, flags); -#endif - } else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned long)(value > 0 ? value : 0 - value), - value < 0, base, precision, width, flags); - } else { - const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) - : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) - : va_arg(va, int); - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned int)(value > 0 ? value : 0 - value), - value < 0, base, precision, width, flags); - } - } else { - // unsigned - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - idx = _ntoa_long_long(out, buffer, idx, maxlen, - va_arg(va, unsigned long long), false, base, - precision, width, flags); -#endif - } else if (flags & FLAGS_LONG) { - idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), - false, base, precision, width, flags); - } else { - const unsigned int value = - (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) - : (flags & FLAGS_SHORT) - ? (unsigned short int)va_arg(va, unsigned int) - : va_arg(va, unsigned int); - idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, - precision, width, flags); - } - } - format++; - break; - } -#if defined(PRINTF_SUPPORT_FLOAT) - case 'f': - case 'F': - if (*format == 'F') - flags |= FLAGS_UPPERCASE; - idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, - width, flags); - format++; - break; -#if defined(PRINTF_SUPPORT_EXPONENTIAL) - case 'e': - case 'E': - case 'g': - case 'G': - if ((*format == 'g') || (*format == 'G')) - flags |= FLAGS_ADAPT_EXP; - if ((*format == 'E') || (*format == 'G')) - flags |= FLAGS_UPPERCASE; - idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, - width, flags); - format++; - break; -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT - case 'c': { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's': { - const char *p = va_arg(va, char *); - unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 'p': { - width = sizeof(void *) * 2U; - flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; -#if defined(PRINTF_SUPPORT_LONG_LONG) - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - idx = _ntoa_long_long(out, buffer, idx, maxlen, - (uintptr_t)va_arg(va, void *), false, 16U, - precision, width, flags); - } else { -#endif - idx = _ntoa_long(out, buffer, idx, maxlen, - (unsigned long)((uintptr_t)va_arg(va, void *)), false, - 16U, precision, width, flags); -#if defined(PRINTF_SUPPORT_LONG_LONG) - } -#endif - format++; - break; - } - - case '%': - out('%', buffer, idx++, maxlen); - format++; - break; - - default: - out(*format, buffer, idx++, maxlen); - format++; - break; - } - } - - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - return (int)idx; -} - -/////////////////////////////////////////////////////////////////////////////// - -int printf_(const char *format, ...) { - va_list va; - va_start(va, format); - char buffer[1]; - const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - -int sprintf_(char *buffer, const char *format, ...) { - va_list va; - va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - -int snprintf_(char *buffer, size_t count, const char *format, ...) { - va_list va; - va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); - va_end(va); - return ret; -} - -int vprintf_(const char *format, va_list va) { - char buffer[1]; - return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); -} - -int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) { - return _vsnprintf(_out_buffer, buffer, count, format, va); -} - -int fctprintf(void (*out)(char character, void *arg), void *arg, - const char *format, ...) { - va_list va; - va_start(va, format); - const out_fct_wrap_type out_fct_wrap = {out, arg}; - const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, - (size_t)-1, format, va); - va_end(va); - return ret; -} diff --git a/lib/software/modules/printf/software/src/printf.h b/lib/software/modules/printf/software/src/printf.h deleted file mode 100644 index 093f345ac..000000000 --- a/lib/software/modules/printf/software/src/printf.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2019, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// 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. -// -// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed -// on -// embedded systems with a very limited resources. -// Use this instead of bloated standard/newlib printf. -// These routines are thread safe and reentrant. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _PRINTF_H_ -#define _PRINTF_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// Initialize pointer function for uart -void printf_init(void (*uart_putc_ptr)(char)); - -/** - * Output a character to a custom device like UART, used by the printf() - * function This function is declared here only. You have to write your custom - * implementation somewhere \param character Character to output - */ -void _putchar(char character); - -/** - * Tiny printf implementation - * You have to implement _putchar if you use printf() - * To avoid conflicts with the regular printf() API it is overridden by macro - * defines and internal underscore-appended functions like printf_() are used - * \param format A string that specifies the format of the output - * \return The number of characters that are written into the array, not - * counting the terminating null character - */ -#define printf printf_ -int printf_(const char *format, ...); - -/** - * Tiny sprintf implementation - * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING - * (V)SNPRINTF INSTEAD! \param buffer A pointer to the buffer where to store the - * formatted string. MUST be big enough to store the output! \param format A - * string that specifies the format of the output \return The number of - * characters that are WRITTEN into the buffer, not counting the terminating - * null character - */ -#define sprintf sprintf_ -int sprintf_(char *buffer, const char *format, ...); - -/** - * Tiny snprintf/vsnprintf implementation - * \param buffer A pointer to the buffer where to store the formatted string - * \param count The maximum number of characters to store in the buffer, - * including a terminating null character \param format A string that specifies - * the format of the output \param va A value identifying a variable arguments - * list \return The number of characters that COULD have been written into the - * buffer, not counting the terminating null character. A value equal or larger - * than count indicates truncation. Only when the returned value is non-negative - * and less than count, the string has been completely written. - */ -#define snprintf snprintf_ -#define vsnprintf vsnprintf_ -int snprintf_(char *buffer, size_t count, const char *format, ...); -int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); - -/** - * Tiny vprintf implementation - * \param format A string that specifies the format of the output - * \param va A value identifying a variable arguments list - * \return The number of characters that are WRITTEN into the buffer, not - * counting the terminating null character - */ -#define vprintf vprintf_ -int vprintf_(const char *format, va_list va); - -/** - * printf with output function - * You may use this as dynamic alternative to printf() with its fixed _putchar() - * output \param out An output function which takes one character and an - * argument pointer \param arg An argument pointer for user data passed to - * output function \param format A string that specifies the format of the - * output \return The number of characters that are sent to the output function, - * not counting the terminating null character - */ -int fctprintf(void (*out)(char character, void *arg), void *arg, - const char *format, ...); - -#ifdef __cplusplus -} -#endif - -#endif // _PRINTF_H_ diff --git a/lib/software/modules/prng/xoshiro256plusplus/example/Makefile b/lib/software/modules/prng/xoshiro256plusplus/example/Makefile deleted file mode 100644 index c9637bad4..000000000 --- a/lib/software/modules/prng/xoshiro256plusplus/example/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: 2024 IObundle -# -# SPDX-License-Identifier: MIT - -XOSHIRO_PATH = .. - -SOURCES = $(wildcard *.c) -SOURCES += $(wildcard $(XOSHIRO_PATH)/*.c) - -HEADERS = $(wildcard *.h) -HEADERS = $(wildcard $(XOSHIRO_PATH)/*.h) - -OBJECTS = $(patsubst %.c,%.o,$(SOURCES)) - -INCLUDE = -I$(XOSHIRO_PATH) - -CFLAGS = -O3 -std=c99 -Wall -Wextra -pedantic -Werror -Wpedantic \ - -Wredundant-decls -Wvla -Wcast-align -Wmissing-prototypes - -EXE = prng - -all: $(EXE) - ./$(EXE) - -$(EXE): $(OBJECTS) - $(CC) $(CFLAGS) $(INCLUDE) $(OBJECTS) -o $(EXE) - -%.o: %.c $(HEADERS) - $(CC) $(CFLAGS) $(INCLUDE) -c -o $@ $< - -clean: - $(RM) $(OBJECTS) - $(RM) $(EXE) diff --git a/lib/software/modules/prng/xoshiro256plusplus/example/example_xoshiro.c b/lib/software/modules/prng/xoshiro256plusplus/example/example_xoshiro.c deleted file mode 100644 index 46f302f49..000000000 --- a/lib/software/modules/prng/xoshiro256plusplus/example/example_xoshiro.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -#include "iob_xoshiro256plusplus.h" - -#define N_GEN (10) - -int main(int argc, char *argv[]) { - printf("xoshiro PRNG test\n"); - int i = 0; - for (i = 0; i < argc; i++) { - printf("%s\n", argv[i]); - } - - // a random 64 bit seed - uint64_t seed = 0x010044e8f1c678ee; - - iob_xoshiro256_init(seed); - - printf("xoshiro PRNG sequence:\n"); - for (i = 0; i < (N_GEN / 8 + 1); i++) { - printf("\t%d:%lx\n", i, iob_xoshiro256_next()); - } - - iob_xoshiro256_init(seed); - uint8_t values[N_GEN] = {0}; - - iob_getrandom(values, N_GEN, 0); - - printf("get random values:\n"); - for (i = 0; i < N_GEN; i++) { - printf("\t%d:%x\n", i, values[i]); - } - - return 0; -} diff --git a/lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.c b/lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.c deleted file mode 100644 index 501c02db4..000000000 --- a/lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -/* - * Adapted from https://prng.di.unimi.it/xoshiro256plusplus.c - * 64 bit PRNG - * - * Note: this PRNG is NOT cryptographically save. DO NOT use for cryptographic - * applications. - * Check: - * https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator - * for more information. - * - */ -#include "iob_xoshiro256plusplus.h" - -static inline uint64_t rotl(const uint64_t x, int k) { - return (x << k) | (x >> (64 - k)); -} - -static uint64_t s[4]; - -/* - * Adapted from https://prng.di.unimi.it/splitmix64.c - * xoshiro author advices using splitmix64 to initialize xoshiro PRNG - * */ -static uint64_t splitmix64_next(uint64_t splitmix64_state) { - uint64_t z = (splitmix64_state += 0x9e3779b97f4a7c15); - z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; - z = (z ^ (z >> 27)) * 0x94d049bb133111eb; - return z ^ (z >> 31); -} - -/* initialize PRNG seed */ -void iob_xoshiro256_init(uint64_t seed) { - s[0] = splitmix64_next(seed); - s[1] = splitmix64_next(s[0]); - s[2] = splitmix64_next(s[1]); - s[3] = splitmix64_next(s[2]); - return; -} - -uint64_t iob_xoshiro256_next(void) { - const uint64_t result = rotl(s[0] + s[3], 23) + s[0]; - - const uint64_t t = s[1] << 17; - - s[2] ^= s[0]; - s[3] ^= s[1]; - s[1] ^= s[2]; - s[0] ^= s[3]; - - s[2] ^= t; - - s[3] = rotl(s[3], 45); - - return result; -} - -/* Write LENGTH bytes of randomness starting at BUFFER. Return the number of - bytes written, or -1 on error. - Use this method to replace getrandom() in environments without Linux OS. -*/ -ssize_t iob_getrandom(void *buffer, size_t length, unsigned int flags) { - size_t bytes_to_get = length; - size_t ptr = 0; - uint64_t random_8bytes = 0; - int byte_cnt = 0; - - while (bytes_to_get-- > 0) { - if (byte_cnt == 0) { - random_8bytes = iob_xoshiro256_next(); - byte_cnt = 8; - } - ((uint8_t *)buffer)[ptr++] = (uint8_t)(random_8bytes & 0x0FF); - random_8bytes >>= 8; - byte_cnt--; - } - return (flags != 0) ? (ssize_t)-1 : (ssize_t)length; -} diff --git a/lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.h b/lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.h deleted file mode 100644 index 7e8907380..000000000 --- a/lib/software/modules/prng/xoshiro256plusplus/iob_xoshiro256plusplus.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 IObundle - * - * SPDX-License-Identifier: MIT - */ - -#ifndef H_IOB_XOSHIRO256PLUSPLUS_H -#define H_IOB_XOSHIRO256PLUSPLUS_H - -#include -#include - -void iob_xoshiro256_init(uint64_t seed); -uint64_t iob_xoshiro256_next(void); -ssize_t iob_getrandom(void *buffer, size_t length, unsigned int flags); -#endif // H_IOB_XOSHIRO256PLUSPLUS_H From c4b616d2337bd2da92a1f1cf40e28189ac815453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20N=C3=B3brega?= Date: Tue, 8 Oct 2024 10:38:29 +0100 Subject: [PATCH 3/5] fix(default.nix): Fix clone of py2hwsw submodules Solution based on comment: https://github.com/NixOS/nixpkgs/issues/195117#issuecomment-1410398050 --- lib/scripts/default.nix | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/scripts/default.nix b/lib/scripts/default.nix index 774d888e4..64399d46d 100644 --- a/lib/scripts/default.nix +++ b/lib/scripts/default.nix @@ -6,7 +6,7 @@ let py2hwsw_commit = "cb11eb4ec2033a57fb164b7e547b91d2e85ec3d6"; # Replace with the desired commit. - py2hwsw_sha256 = "rmXq4bOVj74iX875OYOLeMKqm+iEDSnDXoo6hnjFhUY="; # Replace with the actual SHA256 hash. + py2hwsw_sha256 = "F3p/9VIczHGWlQkRN3oxmkryVeD1lNMifbS74vpfJeE="; # Replace with the actual SHA256 hash. py2hwsw = pkgs.python3.pkgs.buildPythonPackage rec { pname = "py2hwsw"; @@ -18,12 +18,17 @@ let in if py2hwswPath != "" then pkgs.lib.cleanSource py2hwswPath else - pkgs.fetchFromGitHub { + (pkgs.fetchFromGitHub { owner = "IObundle"; repo = "py2hwsw"; rev = py2hwsw_commit; sha256 = py2hwsw_sha256; - }; + fetchSubmodules = true; + }).overrideAttrs (_: { + GIT_CONFIG_COUNT = 1; + GIT_CONFIG_KEY_0 = "url.https://github.com/.insteadOf"; + GIT_CONFIG_VALUE_0 = "git@github.com:"; +}); # Add any necessary dependencies here. #propagatedBuildInputs = [ pkgs.python38Packages.someDependency ]; From 2d5745873e07b56c2187049abc42b9b886d3bc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20N=C3=B3brega?= Date: Wed, 9 Oct 2024 04:28:52 +0100 Subject: [PATCH 4/5] chore(py2hwsw): Update py2hwsw --- lib/scripts/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/scripts/default.nix b/lib/scripts/default.nix index 64399d46d..cb8e6bc94 100644 --- a/lib/scripts/default.nix +++ b/lib/scripts/default.nix @@ -5,8 +5,8 @@ { pkgs ? import {} }: let - py2hwsw_commit = "cb11eb4ec2033a57fb164b7e547b91d2e85ec3d6"; # Replace with the desired commit. - py2hwsw_sha256 = "F3p/9VIczHGWlQkRN3oxmkryVeD1lNMifbS74vpfJeE="; # Replace with the actual SHA256 hash. + py2hwsw_commit = "2245a0c51c95f6391f32f69d57c77617756f2832"; # Replace with the desired commit. + py2hwsw_sha256 = "eHc2OdB4M/USzwWmfo38vkFAwobcllOh+nqHE9Z70qU="; # Replace with the actual SHA256 hash. py2hwsw = pkgs.python3.pkgs.buildPythonPackage rec { pname = "py2hwsw"; From 85d44e363215021b0c0751bf07c3f716162af9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20N=C3=B3brega?= Date: Wed, 9 Oct 2024 05:12:46 +0100 Subject: [PATCH 5/5] chore(py2hwsw): update py2hwsw --- lib/scripts/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/scripts/default.nix b/lib/scripts/default.nix index cb8e6bc94..7f8643de2 100644 --- a/lib/scripts/default.nix +++ b/lib/scripts/default.nix @@ -5,8 +5,8 @@ { pkgs ? import {} }: let - py2hwsw_commit = "2245a0c51c95f6391f32f69d57c77617756f2832"; # Replace with the desired commit. - py2hwsw_sha256 = "eHc2OdB4M/USzwWmfo38vkFAwobcllOh+nqHE9Z70qU="; # Replace with the actual SHA256 hash. + py2hwsw_commit = "b65476abd9a7aeb8c531d328d139f4e9309c59f2"; # Replace with the desired commit. + py2hwsw_sha256 = "s3opR2jwbcLxWGnpfwRSKM66IVh9b6YLLMGFqN1e+5M="; # Replace with the actual SHA256 hash. py2hwsw = pkgs.python3.pkgs.buildPythonPackage rec { pname = "py2hwsw";

RmMt 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 z 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