├── simvectors └── .gitignore ├── tb ├── common ├── bpc_decoder │ ├── common │ ├── sim.do │ ├── Makefile │ ├── bpc_decoder_driver.py │ ├── bpc_decoder_tests.py │ ├── bpc_decoder_monitor.py │ ├── bpc_decoder_scoreboard.py │ └── wave.do ├── bpc_encoder │ ├── common │ ├── sim.do │ ├── reports │ │ └── .gitignore │ ├── Makefile │ ├── bpc_encoder_driver.py │ ├── bpc_encoder_tests.py │ ├── bpc_encoder_monitor.py │ └── wave.do ├── ebpc_decoder │ ├── common │ ├── sim.do │ ├── Makefile │ ├── ebpc_decoder_tests.py │ ├── ebpc_decoder_monitor.py │ ├── ebpc_decoder_scoreboard.py │ └── ebpc_decoder_driver.py └── ebpc_encoder │ ├── common │ ├── sim.do │ ├── Makefile │ ├── ebpc_encoder_driver.py │ ├── ebpc_encoder_monitor.py │ ├── ebpc_encoder_tests.py │ └── ebpc_encoder_scoreboard.py ├── py ├── stimuli_gen │ ├── common │ ├── gen_ebpc_stimuli.py │ └── stimuli_params.py ├── environment.yml └── common │ ├── util.py │ ├── drivers.py │ └── data.py ├── rtl_tb ├── ebpc_decoder │ ├── decoder_questa_commands.tcl │ ├── decoder_simvision_commands.tcl │ ├── compile_sim_decoder.sh │ ├── decoder_xcelium_commands.tcl │ └── ebpc_decoder_tb.sv ├── ebpc_encoder │ ├── encoder_questa_commands.tcl │ ├── encoder_simvision_commands.tcl │ ├── compile_sim_encoder.sh │ ├── encoder_xcelium_commands.tcl │ └── ebpc_encoder_tb.sv ├── rst_clk_drv.sv ├── hs_intf.sv └── hs_drivers.sv ├── fig ├── stream_reg.png ├── decoder_doc.png ├── encoder_doc.png └── out_streams.png ├── .gitignore ├── src_files.yml └── src ├── clk_gate.sv ├── fifo_slice.sv ├── ebpc_pkg.sv ├── encoder ├── done_gen.sv ├── bpc_buffer.sv ├── bpc_encoder.sv ├── dbx_compressor.sv ├── shift_streamer.sv ├── dbp_dbx_enc.sv ├── zrle.sv └── seq_coder.sv └── decoder ├── expander.sv ├── delta_reverse.sv ├── bpc_decoder.sv ├── buffer.sv ├── unpacker.sv ├── symbol_decoder.sv ├── zrle_decoder.sv └── ebpc_decoder.sv /simvectors/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tb/common: -------------------------------------------------------------------------------- 1 | ../py/common/ -------------------------------------------------------------------------------- /py/stimuli_gen/common: -------------------------------------------------------------------------------- 1 | ../common/ -------------------------------------------------------------------------------- /tb/bpc_decoder/common: -------------------------------------------------------------------------------- 1 | ../common/ -------------------------------------------------------------------------------- /tb/bpc_encoder/common: -------------------------------------------------------------------------------- 1 | ../common/ -------------------------------------------------------------------------------- /tb/ebpc_decoder/common: -------------------------------------------------------------------------------- 1 | ../common/ -------------------------------------------------------------------------------- /tb/ebpc_encoder/common: -------------------------------------------------------------------------------- 1 | ../common/ -------------------------------------------------------------------------------- /tb/bpc_encoder/sim.do: -------------------------------------------------------------------------------- 1 | do wave.do 2 | run -all -------------------------------------------------------------------------------- /tb/ebpc_decoder/sim.do: -------------------------------------------------------------------------------- 1 | do wave.do 2 | run -all -------------------------------------------------------------------------------- /tb/bpc_decoder/sim.do: -------------------------------------------------------------------------------- 1 | do wave.do 2 | run -all 3 | -------------------------------------------------------------------------------- /tb/bpc_encoder/reports/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /tb/ebpc_encoder/sim.do: -------------------------------------------------------------------------------- 1 | do ebpc_wave.do 2 | run -all -------------------------------------------------------------------------------- /rtl_tb/ebpc_decoder/decoder_questa_commands.tcl: -------------------------------------------------------------------------------- 1 | do decoder_wave.do 2 | run -all 3 | -------------------------------------------------------------------------------- /rtl_tb/ebpc_encoder/encoder_questa_commands.tcl: -------------------------------------------------------------------------------- 1 | do encoder_wave.do 2 | run -all 3 | -------------------------------------------------------------------------------- /fig/stream_reg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/stream-ebpc/HEAD/fig/stream_reg.png -------------------------------------------------------------------------------- /fig/decoder_doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/stream-ebpc/HEAD/fig/decoder_doc.png -------------------------------------------------------------------------------- /fig/encoder_doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/stream-ebpc/HEAD/fig/encoder_doc.png -------------------------------------------------------------------------------- /fig/out_streams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulp-platform/stream-ebpc/HEAD/fig/out_streams.png -------------------------------------------------------------------------------- /rtl_tb/ebpc_decoder/decoder_simvision_commands.tcl: -------------------------------------------------------------------------------- 1 | waveform new -name "Wave View" 2 | waveform loadsignals -using "Wave View" decoder_signals.svwf 3 | -------------------------------------------------------------------------------- /rtl_tb/ebpc_encoder/encoder_simvision_commands.tcl: -------------------------------------------------------------------------------- 1 | waveform new -name "Wave View" 2 | waveform loadsignals -using "Wave View" encoder_signals.svwf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*flycheck* 2 | **/*flycheck*/ 3 | **/*gmon* 4 | **/*build/ 5 | **/__pycache__/ 6 | **/.bpad/ 7 | **/bpad* 8 | **/pydevd-pycharm.egg 9 | **/.nfs* 10 | **/*.dbg 11 | **/*.wlf 12 | **/work/ 13 | **/xcelium.d/ 14 | **/xmvlog* 15 | **/xrun* 16 | **/transcript 17 | **/modelsim.ini 18 | **/flymd* 19 | **/ucli.key 20 | **/.idea 21 | **/*.bak 22 | **/*.stim 23 | **/*.expresp 24 | **/*.rpt 25 | **/*.vstf 26 | -------------------------------------------------------------------------------- /src_files.yml: -------------------------------------------------------------------------------- 1 | ebpc_encoder: 2 | files: [ 3 | src/ebpc_pkg.sv, 4 | src/fifo_slice.sv, 5 | src/encoder/bpc_buffer.sv, 6 | src/encoder/bpc_encoder.sv, 7 | src/encoder/dbx_compressor.sv, 8 | src/encoder/ebpc_encoder.sv, 9 | src/encoder/seq_coder.sv, 10 | src/encoder/shift_streamer.sv, 11 | src/encoder/zrle.sv, 12 | src/encoder/dbp_dbx_enc.sv, 13 | ] 14 | 15 | ebpc_decoder: 16 | files: [ 17 | src/ebpc_pkg.sv, 18 | src/fifo_slice.sv, 19 | src/decoder/bpc_decoder.sv, 20 | src/decoder/buffer.sv, 21 | src/decoder/ebpc_decoder.sv, 22 | src/decoder/expander.sv, 23 | src/decoder/symbol_decoder.sv, 24 | src/decoder/unpacker.sv, 25 | src/decoder/zrle_decoder.sv, 26 | src/decoder/delta_reverse.sv, 27 | ] 28 | -------------------------------------------------------------------------------- /src/clk_gate.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2018 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed tmo in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module pulp_clock_gating 12 | ( 13 | input logic clk_i, 14 | input logic en_i, 15 | input logic test_en_i, 16 | output logic clk_o 17 | ); 18 | 19 | logic clk_en; 20 | 21 | always_latch 22 | begin 23 | if (clk_i == 1'b0) 24 | clk_en <= en_i | test_en_i; 25 | end 26 | 27 | assign clk_o = clk_i & clk_en; 28 | 29 | endmodule 30 | -------------------------------------------------------------------------------- /rtl_tb/rst_clk_drv.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | `timescale 1ps/1fs 12 | module rst_clk_drv 13 | #( 14 | parameter time CLK_PERIOD = 10ns, 15 | parameter time RST_TIME = 57ns 16 | ) 17 | ( 18 | output logic clk_o, 19 | output logic rst_no 20 | ); 21 | 22 | initial begin 23 | clk_o = 1'b1; 24 | rst_no = 1'b0; 25 | #RST_TIME rst_no = 1'b1; 26 | end 27 | 28 | always 29 | #(CLK_PERIOD/2) clk_o = ~clk_o; 30 | 31 | endmodule // rst_clk_drv 32 | -------------------------------------------------------------------------------- /rtl_tb/hs_intf.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | interface HandshakeIf_t 12 | #( 13 | parameter int unsigned DATA_W = 8 14 | ) 15 | ( 16 | input logic clk_i 17 | ); 18 | 19 | 20 | logic [DATA_W-1:0] data; 21 | logic last; 22 | logic vld; // 23 | logic rdy; 24 | // these guys are super useless because we have to use virtual interfaces with the driver classes... 25 | modport in( 26 | input data, last, vld, 27 | output rdy 28 | ); 29 | 30 | modport out( 31 | output data, last, vld, 32 | input rdy 33 | ); 34 | 35 | endinterface // HandshakeIf_t 36 | -------------------------------------------------------------------------------- /tb/bpc_encoder/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | TOPLEVEL_LANG ?= verilog 11 | 12 | 13 | WPWD=$(shell sh -c 'pwd -W') 14 | PWD=$(shell pwd) 15 | SRC_DIR = $(PWD)/../../src 16 | ENC_SRC_DIR = $(SRC_DIR)/encoder 17 | SIM_ARGS = "-t 1ps -do 'sim.do'" 18 | 19 | 20 | #PYTHONPATH := $(PWD)/../pydevd-pycharm.egg:$(PYTHONPATH) 21 | 22 | 23 | VERILOG_SOURCES += $(SRC_DIR)/ebpc_pkg.sv \ 24 | $(ENC_SRC_DIR)/dbx_compressor.sv \ 25 | $(SRC_DIR)/fifo_slice.sv \ 26 | $(ENC_SRC_DIR)/shift_streamer.sv \ 27 | $(ENC_SRC_DIR)/seq_coder.sv \ 28 | $(ENC_SRC_DIR)/dbp_dbx_enc.sv \ 29 | $(ENC_SRC_DIR)/bpc_encoder.sv 30 | 31 | 32 | TOPLEVEL = bpc_encoder 33 | MODULE = bpc_encoder_tests 34 | 35 | 36 | include $(shell cocotb-config --makefiles)/Makefile.inc 37 | include $(shell cocotb-config --makefiles)/Makefile.sim 38 | -------------------------------------------------------------------------------- /rtl_tb/ebpc_encoder/compile_sim_encoder.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | TOP="ebpc_encoder_tb" 3 | CADENCE_IUS="cds_ius-18.09.005 xrun" 4 | #CADENCE_IUS="xrun" 5 | XRUN_OPTIONS="-64bit -design_top ${TOP} -sv -timescale 100ps/1ps -input encoder_xcelium_commands.tcl" 6 | XRUN_GUI_OPTIONS="-access +rc -gui" 7 | QUESTA_VLOG="questa-2019.3 vlog" 8 | #QUESTA_VLOG="vlog" 9 | QUESTA_VSIM="questa-2019.3 vsim" 10 | #QUESTA_VSIM="vsim" 11 | QUESTA_OPTIONS="-do encoder_questa_commands.tcl -64 -t 1ps" 12 | QUESTA_BATCH_OPTIONS="-batch" 13 | 14 | SV_SOURCES="../../src/ebpc_pkg.sv \ 15 | ../../src/encoder/bpc_encoder.sv \ 16 | ../../src/encoder/dbp_dbx_enc.sv \ 17 | ../../src/encoder/dbx_compressor.sv \ 18 | ../../src/encoder/ebpc_encoder.sv \ 19 | ../../src/encoder/seq_coder.sv \ 20 | ../../src/encoder/shift_streamer.sv \ 21 | ../../src/encoder/zrle.sv \ 22 | ../../src/encoder/bpc_buffer.sv \ 23 | ../../src/fifo_slice.sv \ 24 | ../../src/clk_gate.sv \ 25 | ../hs_intf.sv \ 26 | ../hs_drivers.sv \ 27 | ../rst_clk_drv.sv \ 28 | ./${TOP}.sv" 29 | 30 | #uncomment if you use cadence 31 | #${CADENCE_IUS} ${XRUN_OPTIONS} ${XRUN_GUI_OPTIONS} ${SV_SOURCES} 32 | 33 | ${QUESTA_VLOG} ${SV_SOURCES} 34 | echo -e "-------GUI SIMULATION COMMAND------\n${QUESTA_VSIM} ${QUESTA_OPTIONS} +acc -debugdb -classdebug ${TOP} &" 35 | echo -e "------BATCH SIMULATION COMMAND-----\n${QUESTA_VSIM} ${QUESTA_OPTIONS} ${QUESTA_BATCH_OPTIONS} ${TOP}" 36 | -------------------------------------------------------------------------------- /tb/bpc_decoder/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | TOPLEVEL_LANG ?= verilog 11 | 12 | 13 | WPWD=$(shell sh -c 'pwd -W') 14 | PWD=$(shell pwd) 15 | SRC_DIR = $(PWD)/../../src 16 | DEC_SRC_DIR = $(SRC_DIR)/decoder 17 | SIM_ARGS = -t 1ps -do sim.do -debugdb 18 | 19 | 20 | #PYTHONPATH := $(PWD)/../pydevd-pycharm.egg:$(PYTHONPATH) 21 | 22 | 23 | VERILOG_SOURCES += $(SRC_DIR)/ebpc_pkg.sv \ 24 | $(DEC_SRC_DIR)/bpc_decoder.sv \ 25 | $(SRC_DIR)/fifo_slice.sv \ 26 | $(DEC_SRC_DIR)/buffer.sv \ 27 | $(DEC_SRC_DIR)/delta_reverse.sv \ 28 | $(DEC_SRC_DIR)/expander.sv \ 29 | $(DEC_SRC_DIR)/symbol_decoder.sv \ 30 | $(DEC_SRC_DIR)/unpacker.sv \ 31 | 32 | 33 | TOPLEVEL = bpc_decoder 34 | MODULE = bpc_decoder_tests 35 | 36 | 37 | include $(shell cocotb-config --makefiles)/Makefile.inc 38 | include $(shell cocotb-config --makefiles)/Makefile.sim 39 | -------------------------------------------------------------------------------- /py/environment.yml: -------------------------------------------------------------------------------- 1 | name: stream-ebpc 2 | channels: 3 | - defaults 4 | dependencies: 5 | - _libgcc_mutex=0.1=main 6 | - _pytorch_select=0.2=gpu_0 7 | - blas=1.0=mkl 8 | - ca-certificates=2019.8.28=0 9 | - certifi=2019.9.11=py37_0 10 | - cffi=1.12.3=py37h2e261b9_0 11 | - cudatoolkit=10.0.130=0 12 | - cudnn=7.6.0=cuda10.0_0 13 | - freetype=2.9.1=h8a8886c_1 14 | - intel-openmp=2019.4=243 15 | - jpeg=9b=h024ee3a_2 16 | - libedit=3.1.20181209=hc058e9b_0 17 | - libffi=3.2.1=hd88cf55_4 18 | - libgcc-ng=9.1.0=hdf63c60_0 19 | - libgfortran-ng=7.3.0=hdf63c60_0 20 | - libpng=1.6.37=hbc83047_0 21 | - libstdcxx-ng=9.1.0=hdf63c60_0 22 | - libtiff=4.0.10=h2733197_2 23 | - mkl=2019.4=243 24 | - mkl-service=2.3.0=py37he904b0f_0 25 | - mkl_fft=1.0.12=py37ha843d7b_0 26 | - mkl_random=1.0.2=py37hd81dba3_0 27 | - ncurses=6.1=he6710b0_1 28 | - ninja=1.9.0=py37hfd86e86_0 29 | - numpy=1.16.4=py37h7e9f1db_0 30 | - numpy-base=1.16.4=py37hde5b4d6_0 31 | - olefile=0.46=py37_0 32 | - openssl=1.1.1d=h7b6447c_2 33 | - pillow=6.2.0=py37h34e0f95_0 34 | - pip=19.1.1=py37_0 35 | - pycparser=2.19=py37_0 36 | - python=3.7.3=h0371630_0 37 | - pytorch=1.2.0=cuda100py37h938c94c_0 38 | - readline=7.0=h7b6447c_5 39 | - setuptools=41.0.1=py37_0 40 | - six=1.12.0=py37_0 41 | - sqlite=3.28.0=h7b6447c_0 42 | - tk=8.6.8=hbc83047_0 43 | - torchvision=0.4.0=cuda100py37hecfc37a_0 44 | - wheel=0.33.4=py37_0 45 | - xz=5.2.4=h14c3975_4 46 | - zlib=1.2.11=h7b6447c_3 47 | - zstd=1.3.7=h0b5b093_0 48 | - pip: 49 | - cocotb==1.1.0 50 | -------------------------------------------------------------------------------- /rtl_tb/ebpc_decoder/compile_sim_decoder.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | #change executable names to match your system setup 3 | TOP="ebpc_decoder_tb" 4 | CADENCE_IUS="cds_ius-18.09.005 xrun" 5 | #CADENCE_IUS="xrun" 6 | XRUN_OPTIONS="-64bit -design_top ${TOP} -sv -timescale 100ps/1ps -input decoder_xcelium_commands.tcl" 7 | XRUN_GUI_OPTIONS="-access +rc -gui" 8 | QUESTA_VLOG="questa-2019.3 vlog" 9 | #QUESTA_VLOG="vlog" 10 | QUESTA_VSIM="questa-2019.3 vsim" 11 | #QUESTA_VSIM="vsim" 12 | QUESTA_OPTIONS="-do decoder_questa_commands.tcl -64 -t 1ps" 13 | QUESTA_BATCH_OPTIONS="-batch" 14 | 15 | SV_SOURCES="../hs_drivers.sv \ 16 | ../../src/ebpc_pkg.sv \ 17 | ../../src/decoder/bpc_decoder.sv \ 18 | ../../src/decoder/buffer.sv \ 19 | ../../src/decoder/delta_reverse.sv \ 20 | ../../src/decoder/ebpc_decoder.sv \ 21 | ../../src/decoder/expander.sv \ 22 | ../../src/decoder/symbol_decoder.sv \ 23 | ../../src/decoder/unpacker.sv \ 24 | ../../src/decoder/zrle_decoder.sv \ 25 | ../../src/fifo_slice.sv \ 26 | ../rst_clk_drv.sv \ 27 | ../hs_intf.sv \ 28 | ./ebpc_decoder_tb.sv" 29 | 30 | #uncomment if you use cadence 31 | #${CADENCE_IUS} ${XRUN_OPTIONS} ${XRUN_GUI_OPTIONS} ${SV_SOURCES} 32 | ${QUESTA_VLOG} ${SV_SOURCES} 33 | echo -e "-------GUI SIMULATION COMMAND------\n${QUESTA_VSIM} ${QUESTA_OPTIONS} +acc -debugdb -classdebug ${TOP}" 34 | echo -e "------BATCH SIMULATION COMMAND-----\n${QUESTA_VSIM} ${QUESTA_OPTIONS} ${QUESTA_BATCH_OPTIONS} ${TOP}" 35 | -------------------------------------------------------------------------------- /tb/ebpc_decoder/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | TOPLEVEL_LANG ?= verilog 11 | 12 | 13 | WPWD=$(shell sh -c 'pwd -W') 14 | PWD=$(shell pwd) 15 | SRC_DIR = $(PWD)/../../src 16 | DEC_SRC_DIR = $(SRC_DIR)/decoder 17 | SIM_ARGS = -t 1ps -do sim.do -debugdb 18 | 19 | 20 | #PYTHONPATH := $(PWD)/../pydevd-pycharm.egg:$(PYTHONPATH) 21 | 22 | 23 | VERILOG_SOURCES += $(SRC_DIR)/ebpc_pkg.sv \ 24 | $(DEC_SRC_DIR)/bpc_decoder.sv \ 25 | $(DEC_SRC_DIR)/ebpc_decoder.sv \ 26 | $(SRC_DIR)/fifo_slice.sv \ 27 | $(DEC_SRC_DIR)/buffer.sv \ 28 | $(DEC_SRC_DIR)/delta_reverse.sv \ 29 | $(DEC_SRC_DIR)/expander.sv \ 30 | $(DEC_SRC_DIR)/symbol_decoder.sv \ 31 | $(DEC_SRC_DIR)/unpacker.sv \ 32 | $(DEC_SRC_DIR)/zrle_decoder.sv 33 | 34 | 35 | TOPLEVEL = ebpc_decoder 36 | MODULE = ebpc_decoder_tests 37 | 38 | 39 | include $(shell cocotb-config --makefiles)/Makefile.inc 40 | include $(shell cocotb-config --makefiles)/Makefile.sim 41 | -------------------------------------------------------------------------------- /tb/ebpc_encoder/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | TOPLEVEL_LANG ?= verilog 11 | 12 | 13 | PWD=$(shell pwd) 14 | SRC_DIR = $(PWD)/../../src 15 | ENC_SRC_DIR = $(SRC_DIR)/encoder 16 | ifeq ($(SIM),questa) 17 | RUN_ARGS = '-noautoldlibpath' 18 | SIM_ARGS = "-t 1ps -do 'sim.do' -debugdb -noautoldlibpath" 19 | endif 20 | 21 | 22 | #PYTHONPATH := $(PWD)/../pydevd-pycharm.egg:$(PYTHONPATH) 23 | 24 | 25 | VERILOG_SOURCES += $(SRC_DIR)/ebpc_pkg.sv \ 26 | $(ENC_SRC_DIR)/dbx_compressor.sv \ 27 | $(SRC_DIR)/fifo_slice.sv \ 28 | $(ENC_SRC_DIR)/shift_streamer.sv \ 29 | $(ENC_SRC_DIR)/seq_coder.sv \ 30 | $(ENC_SRC_DIR)/dbp_dbx_enc.sv \ 31 | $(ENC_SRC_DIR)/bpc_encoder.sv \ 32 | $(ENC_SRC_DIR)/ebpc_encoder.sv \ 33 | $(ENC_SRC_DIR)/bpc_buffer.sv \ 34 | $(ENC_SRC_DIR)/done_gen.sv \ 35 | $(SRC_DIR)/clk_gate.sv \ 36 | $(ENC_SRC_DIR)/zrle.sv 37 | 38 | 39 | 40 | TOPLEVEL = ebpc_encoder 41 | MODULE = ebpc_encoder_tests 42 | 43 | 44 | include $(shell cocotb-config --makefiles)/Makefile.inc 45 | include $(shell cocotb-config --makefiles)/Makefile.sim 46 | -------------------------------------------------------------------------------- /src/fifo_slice.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | module fifo_slice 13 | #( 14 | parameter type t = logic 15 | ) 16 | ( 17 | input logic clk_i, 18 | input logic rst_ni, 19 | input t din_i, 20 | input logic vld_i, 21 | output logic rdy_o, 22 | output t dout_o, 23 | output logic vld_o, 24 | input logic rdy_i 25 | ); 26 | 27 | t data_d, data_q; 28 | typedef enum {empty, full} state_t; 29 | state_t state_d, state_q; 30 | 31 | assign dout_o = data_q; 32 | 33 | always_comb begin : fsm 34 | vld_o = 1'b0; 35 | rdy_o = 1'b0; 36 | data_d = data_q; 37 | state_d = state_q; 38 | 39 | case (state_q) 40 | empty: begin 41 | rdy_o = 1'b1; 42 | if (vld_i) begin 43 | state_d = full; 44 | data_d = din_i; 45 | end 46 | end 47 | full: begin 48 | vld_o = 1'b1; 49 | if (rdy_i) begin 50 | rdy_o = 1'b1; 51 | if (vld_i) 52 | data_d = din_i; 53 | else 54 | state_d = empty; 55 | end 56 | end 57 | endcase // case (state_q) 58 | end // block: fsm 59 | 60 | always @(posedge clk_i or negedge rst_ni) begin : sequential 61 | if (~rst_ni) begin 62 | state_q <= empty; 63 | data_q <= '0; 64 | end else begin 65 | state_q <= state_d; 66 | data_q <= data_d; 67 | end 68 | end 69 | 70 | endmodule // fifo_slice 71 | -------------------------------------------------------------------------------- /py/common/util.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | from cocotb.binary import BinaryValue 13 | import random 14 | import numpy as np 15 | 16 | def zero_pad_bitstr(bitstr, length): 17 | bitstr += ('0'*((-len(bitstr))%length)) 18 | return bitstr 19 | 20 | def zero_pad_list(lst, length): 21 | lst += ([0]*((-len(lst))%length)) 22 | return lst 23 | 24 | def split_str(string, width): 25 | assert len(string) % width == 0, "Error: Tried to split string into substrings of incompatible length" 26 | return [string[k*width:(k+1)*width] for k in range(len(string)//width)] 27 | 28 | def int_list_to_binval_list(intlist, n_bits, signed): 29 | binrep = 2 if signed else 0 30 | return [BinaryValue(value=val, n_bits=n_bits, binaryRepresentation=binrep, bigEndian=False) for val in intlist] 31 | 32 | def random_vals(n, data_w): 33 | vals = [random.randint(-2**(data_w-1), 2**(data_w-1)) for k in range(n)] 34 | return vals 35 | def get_random_nonzero_in_range(lo, hi): 36 | choice = 0 37 | while choice == 0: 38 | choice = np.random.randint(lo, hi) 39 | return int(choice) 40 | 41 | def is_pwr_of_two(num): 42 | return (num & (num-1) == 0) and num > 0 43 | 44 | def write_sim_file(data, filename, length): 45 | with open(filename, 'w') as fh: 46 | for idx, line in enumerate(data): 47 | str = '' 48 | for val in line: 49 | str += (val + ' ') 50 | str = str[:-1] 51 | if (idx != length-1): 52 | str += '\n' 53 | fh.write(str) 54 | -------------------------------------------------------------------------------- /py/stimuli_gen/gen_ebpc_stimuli.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | from stimuli_params import StimuliParams 12 | import random 13 | import numpy as np 14 | import torch 15 | 16 | torch.manual_seed(220) 17 | random.seed(9) 18 | np.random.seed(62) 19 | 20 | 21 | 22 | FM_FRAC = 0.05 23 | BATCH_SIZE = 1 24 | N_BATCH = 4 25 | DATA_W = 8 26 | LOG_MAX_WORDS = 24 27 | MAX_ZRLE_LEN = 16 28 | BLOCK_SIZE = 8 29 | #Adjust these to your needs - it's recommended to use absolute paths. 30 | BASE_STIM_DIRECTORY = '/home/georgr/projects/stream-ebpc/simvectors' 31 | DATASET_PATH = '/usr/scratch2/risa/georgr/imagenet/imgs' 32 | DEBUG_FILE = None 33 | SAFETY_FAC = 0.75 34 | 35 | MODULES = ['encoder', 'decoder'] 36 | #NETS = ['vgg16', 'resnet34', 'mobilenet2', 'random', 'all_zeros'] 37 | #NETS = ['vgg16', 'resnet34'] 38 | NETS = ['resnet34'] 39 | #NETS = ['last_test'] 40 | #NETS = ['random', 'all_zeros'] 41 | #import pydevd_pycharm 42 | #pydevd_pycharm.settrace('risa', port=9100, stdoutToServer=True, stderrToServer=True) 43 | 44 | stims = [] 45 | for net in NETS: 46 | dbg_f = DEBUG_FILE if net == 'vgg16' else None 47 | stims.append(StimuliParams(network=net, fm_frac=FM_FRAC, 48 | batch_size=BATCH_SIZE, modules=MODULES, n_batches=N_BATCH, data_w=DATA_W, 49 | max_zrle_len=MAX_ZRLE_LEN, block_size=BLOCK_SIZE, 50 | dataset_path=DATASET_PATH, num_words_width=LOG_MAX_WORDS, 51 | debug_file=dbg_f, safety_factor=SAFETY_FAC)) 52 | 53 | for stim in stims: 54 | stim.write(BASE_STIM_DIRECTORY) 55 | -------------------------------------------------------------------------------- /src/ebpc_pkg.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | package ebpc_pkg; 13 | // let maximum(a,b) = a > b ? a : b; 14 | localparam logic [5-1:0] ALL_ONES = 5'b00000; 15 | localparam logic [5-1:0] DBXZ_DBPNZ = 5'b00001; 16 | localparam logic [5-1:0] TWO_ONES_PREFIX = 5'b00010; 17 | localparam logic [5-1:0] SINGLE_ONE_PREFIX = 5'b00011; 18 | 19 | // how many bits do we need to specify how long the uncompressed transmission will be? 20 | parameter int unsigned LOG_MAX_WORDS = 24; 21 | 22 | // MAX_ZRLE_LEN: maximum zero runlength to encode 23 | parameter int unsigned LOG_MAX_ZRLE_LEN = 4; 24 | parameter int unsigned MAX_ZRLE_LEN = 2**LOG_MAX_ZRLE_LEN; 25 | parameter int unsigned LOG_DATA_W = 3; // 2^3=8, 2^4=16, 2^5=32 26 | parameter int unsigned DATA_W = 2**LOG_DATA_W; 27 | parameter int unsigned BLOCK_SIZE = 8; 28 | // maximum length of an encoded symbol according to table 3.a) 29 | parameter int unsigned MAX_SYMB_LEN = BLOCK_SIZE > 3+$clog2(DATA_W) ? (BLOCK_SIZE > 5+$clog2(BLOCK_SIZE) ? BLOCK_SIZE : 5+$clog2(BLOCK_SIZE)): (3+$clog2(DATA_W)> 5+$clog2(BLOCK_SIZE)? 3+$clog2(DATA_W) : 5+$clog2(BLOCK_SIZE)); 30 | 31 | typedef enum { 32 | TWO, 33 | THREE_PLUS_LOGM, 34 | FIVE, 35 | FIVE_PLUS_LOGN, 36 | N 37 | } symb_len_t; 38 | 39 | 40 | typedef struct packed { 41 | logic [0:DATA_W] [BLOCK_SIZE-2:0] dbp; 42 | logic [DATA_W-1:0] base; 43 | } dbp_block_t; 44 | 45 | typedef struct packed { 46 | logic [MAX_SYMB_LEN-1:0] symb; 47 | symb_len_t len; 48 | logic zero; 49 | } encoding_t; 50 | 51 | endpackage // ebpc_pkg 52 | -------------------------------------------------------------------------------- /py/stimuli_gen/stimuli_params.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | from common.data import genStimFiles 12 | import os 13 | import random 14 | import numpy as np 15 | 16 | class StimuliParams: 17 | def __init__(self, network, fm_frac, batch_size, n_batches, data_w, modules, max_zrle_len, block_size, dataset_path, num_words_width, safety_factor=0.75, debug_file=None): 18 | self.net = network 19 | self.frac = fm_frac 20 | self.bs = batch_size 21 | self.nb = n_batches 22 | self.data_w = data_w 23 | self.modules = modules 24 | self.max_zrle_len = max_zrle_len 25 | self.block_size = block_size 26 | self.dataset_path = dataset_path 27 | self.debug_file = debug_file 28 | self.nww = num_words_width 29 | self.sf = safety_factor 30 | 31 | def write(self, base_dir): 32 | stim_dirs = {mod:os.path.join(base_dir, mod, self.net) for mod in self.modules} 33 | for sd in stim_dirs.values(): 34 | if not os.path.exists(sd): 35 | try: 36 | os.makedirs(sd) 37 | except: 38 | print("Problem creating directory {} - attempting to continue".format(sd)) 39 | 40 | if self.net not in ['all_zeros', 'random']: 41 | filenames = {mod:os.path.join(stim_dirs[mod], self.net) + '_' +\ 42 | 'f{}_'.format(self.frac) + 'bs{}_'.format(self.bs) +\ 43 | 'nb{}_'.format(self.nb) + 'ww{}'.format(self.data_w) for mod in self.modules} 44 | else: 45 | filenames = {mod:os.path.join(stim_dirs[mod], self.net) for mod in self.modules} 46 | genStimFiles(file_prefixes=filenames, modules=self.modules, 47 | max_zrle_len=self.max_zrle_len, block_size=self.block_size, 48 | model=self.net, dataset_path=self.dataset_path, data_w=self.data_w, 49 | num_batches=self.nb, batch_size=self.bs, fmap_frac=self.frac, debug_file=self.debug_file, safety_factor=self.sf) 50 | -------------------------------------------------------------------------------- /src/encoder/done_gen.sv: -------------------------------------------------------------------------------- 1 | module done_gen 2 | ( 3 | input logic clk_i, 4 | input logic rst_ni, 5 | input logic vld_to_znz_i, 6 | input logic znz_last_i, 7 | input logic znz_hs_active_i, 8 | input logic bpc_idle_i, 9 | input logic bpc_last_i, 10 | input logic bpc_last_hs_active_i, 11 | input logic bpc_was_last_i, 12 | input logic bpc_was_last_hs_active_i, 13 | output logic blk_done_o 14 | ); 15 | 16 | typedef enum {idle, wait_for_znz_last, wait_for_znz_and_bpc_last, wait_for_znz_and_bpc_was_last, wait_for_bpc_last, wait_for_bpc_was_last} state_t; 17 | logic bpc_signal_to_wait_for; 18 | state_t state_d, state_q; 19 | 20 | always_comb begin : fsm 21 | state_d = state_q; 22 | blk_done_o = 1'b0; 23 | unique case (state_q) 24 | idle : begin 25 | if (vld_to_znz_i) 26 | state_d = wait_for_znz_and_bpc_was_last; 27 | end 28 | wait_for_znz_and_bpc_was_last : begin 29 | if (~bpc_idle_i && ~znz_last_i) 30 | state_d = wait_for_znz_and_bpc_last; 31 | else if (~bpc_idle_i && znz_last_i && znz_hs_active_i) 32 | state_d = wait_for_bpc_last; 33 | else if (bpc_idle_i && bpc_was_last_i && bpc_was_last_hs_active_i) 34 | state_d = wait_for_znz_last; 35 | else if (znz_last_i && znz_hs_active_i) 36 | state_d = wait_for_bpc_was_last; 37 | end 38 | wait_for_znz_and_bpc_last : begin 39 | if (znz_last_i && znz_hs_active_i && bpc_last_i && bpc_last_hs_active_i) begin 40 | blk_done_o = 1'b1; 41 | state_d = idle; 42 | end else if (znz_last_i && znz_hs_active_i) 43 | state_d = wait_for_bpc_last; 44 | else if (bpc_last_i && bpc_last_hs_active_i) 45 | state_d = wait_for_znz_last; 46 | end 47 | wait_for_bpc_was_last : begin 48 | if (~bpc_idle_i) 49 | state_d = wait_for_bpc_last; 50 | else if (bpc_was_last_i && bpc_was_last_hs_active_i) begin 51 | blk_done_o = 1'b1; 52 | state_d = idle; 53 | end 54 | end 55 | wait_for_znz_last : begin 56 | if (znz_last_i && znz_hs_active_i) begin 57 | blk_done_o = 1'b1; 58 | state_d = idle; 59 | end 60 | end 61 | wait_for_bpc_last : begin 62 | if (bpc_last_i && bpc_last_hs_active_i) begin 63 | blk_done_o = 1'b1; 64 | state_d = idle; 65 | end 66 | end 67 | default : begin 68 | state_d = idle; 69 | end 70 | endcase // unique case (state_q) 71 | end 72 | 73 | always_ff @(posedge clk_i, negedge rst_ni) begin : sequential 74 | if (~rst_ni) begin 75 | state_q <= idle; 76 | end else begin 77 | state_q <= state_d; 78 | end 79 | end 80 | endmodule 81 | -------------------------------------------------------------------------------- /tb/bpc_encoder/bpc_encoder_driver.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import random 13 | import cocotb 14 | from common.drivers import HandshakeInputDriver, HandshakeOutputDriver, wait_cycles 15 | 16 | class BPCEncoderDriver: 17 | def __init__(self, dut, ta, tt): 18 | self.clk_i = dut.clk_i 19 | self.rst_ni = dut.rst_ni 20 | self.data_i = dut.data_i 21 | self.flush_i = dut.flush_i 22 | self.vld_i = dut.vld_i 23 | self.rdy_o = dut.rdy_o 24 | self.data_o = dut.data_o 25 | self.vld_o = dut.vld_o 26 | self.rdy_i = dut.rdy_i 27 | self.ta = ta 28 | self.tt = tt 29 | self.in_drv = HandshakeInputDriver(clk=self.clk_i, signals=[self.data_i, self.flush_i], vld=self.vld_i, rdy=self.rdy_o, ta=self.ta, tt=self.tt) 30 | self.out_drv = HandshakeOutputDriver(clk=self.clk_i, vld=self.vld_o, rdy=self.rdy_i, ta=self.ta, tt=self.tt) 31 | self.apply_defaults() 32 | 33 | def apply_defaults(self): 34 | self.data_i <= 0 35 | self.flush_i <= 0 36 | self.vld_i <= 0 37 | self.rdy_i <= 0 38 | 39 | 40 | 41 | @cocotb.coroutine 42 | def drive_input(self, values, tmin, tmax): 43 | # values: list of tuples (data, flush) to feed 44 | # tmin: minimum # of clock cycles to wait between input applications 45 | # tmax: maximum # of clock cycles to wait between applications 46 | assert self.rst_ni.value == 1, "BPCEncoderDriver Error: rst_ni is not high in drive_input!" 47 | for val in values: 48 | yield wait_cycles(self.clk_i, random.randint(tmin,tmax)) 49 | yield self.in_drv.write_input(val) 50 | 51 | @cocotb.coroutine 52 | def read_outputs(self, num, tmin, tmax): 53 | #num: how many output values to read 54 | #tmin: minimum # of clock cycles to wait between reads 55 | #tmax: maximum # of clock cycles to wait between reads 56 | assert self.rst_ni.value == 1, "BPCEncoderDriver Error: rst_ni is not high in read_outputs!" 57 | k = 0 58 | for i in range(num): 59 | yield wait_cycles(self.clk_i, random.randint(tmin, tmax)) 60 | yield self.out_drv.read_output() 61 | k+= 1 62 | return k 63 | -------------------------------------------------------------------------------- /src/encoder/bpc_buffer.sv: -------------------------------------------------------------------------------- 1 | module bpc_buffer 2 | import ebpc_pkg::*; 3 | ( 4 | input logic clk_i, 5 | input logic rst_ni, 6 | input logic [DATA_W-1:0] data_i, 7 | input logic last_i, 8 | input logic vld_i, 9 | output logic rdy_o, 10 | input logic was_last_i, 11 | output logic [DATA_W-1:0] data_o, 12 | output logic last_o, 13 | output logic vld_o, 14 | input logic rdy_i, 15 | output logic full_o 16 | ); 17 | 18 | 19 | typedef enum {empty, full, last} state_t; 20 | state_t state_d, state_q; 21 | logic [DATA_W-1:0] data_d, data_q; 22 | 23 | assign data_o = data_q; 24 | 25 | always_comb begin : fsm 26 | last_o = 1'b0; 27 | rdy_o = 1'b0; 28 | vld_o = 1'b0; 29 | data_d = data_q; 30 | state_d = state_q; 31 | full_o = 1'b0; 32 | 33 | case(state_q) 34 | empty : begin 35 | // if was_last_i is high in empty state, things are really weird (or 36 | // we're doing all zeros) 37 | assert (~was_last_i) else $warning("Assertion failed in bpc_buffer in state empty - was_last_i is high!"); 38 | rdy_o = 1'b1; 39 | if (vld_i) begin 40 | data_d = data_i; 41 | if (last_i) 42 | state_d = last; 43 | else 44 | state_d = full; 45 | end 46 | end 47 | full : begin 48 | rdy_o = rdy_i; 49 | full_o = 1'b1; 50 | if (vld_i) begin 51 | vld_o = 1'b1; 52 | last_o = was_last_i; 53 | if (rdy_i) begin 54 | data_d = data_i; 55 | if (last_i) 56 | state_d = last; 57 | end 58 | end else if (was_last_i) begin 59 | last_o = 1'b1; 60 | vld_o = 1'b1; 61 | if (rdy_i) 62 | state_d = empty; 63 | else 64 | state_d = last; 65 | end 66 | end // case: full 67 | last : begin 68 | // if was_last_i is high in last state, things don't really make sense... 69 | assert (~was_last_i) else $warning("Assertion failed in bpc_buffer in state last - was_last_i is high!"); 70 | vld_o = 1'b1; 71 | last_o = 1'b1; 72 | full_o = 1'b1; 73 | if (rdy_i) begin 74 | rdy_o = 1'b1; 75 | if (vld_i) begin 76 | data_d = data_i; 77 | if (~last_i) 78 | state_d = full; 79 | end else 80 | state_d = empty; 81 | end 82 | end // case: last 83 | endcase // case (state_q) 84 | end // block: fsm 85 | 86 | always_ff @(posedge clk_i or negedge rst_ni) begin : sequential 87 | if (~rst_ni) begin 88 | state_q <= empty; 89 | data_q <= 'd0; 90 | end else begin 91 | state_q <= state_d; 92 | data_q <= data_d; 93 | end 94 | end 95 | 96 | endmodule // bpc_buffer 97 | -------------------------------------------------------------------------------- /tb/bpc_encoder/bpc_encoder_tests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.clock import Clock 14 | from cocotb.result import TestFailure 15 | from cocotb.triggers import Timer, RisingEdge 16 | from cocotb.binary import BinaryValue 17 | from common.drivers import reset_dut 18 | from bpc_encoder_driver import BPCEncoderDriver 19 | from bpc_encoder_scoreboard import BPCEncoderScoreboard 20 | from bpc_encoder_monitor import BPCEncoderMonitor 21 | import random 22 | 23 | #import pydevd_pycharm 24 | #pydevd_pycharm.settrace('localhost', port=9100, stdoutToServer=True, stderrToServer=True) 25 | 26 | random.seed(a=8) 27 | 28 | CLOCK_PERIOD = 2500 29 | RESET_TIME = 15000 30 | DATA_W = 8 31 | BLOCK_SIZE = 8 32 | TA = 200 33 | TT = 2000 34 | 35 | STIM_REPORT_FILE = '../reports/stimuli.log' 36 | #STIM_REPORT_FILE = None 37 | INT_RESULTS_REPORT_FILE = '../intermediate_results.log' 38 | #INT_RESULTS_REPORT_FILE = None 39 | SUMMARY_REPORT_FILE = '../reports/summary.log' 40 | #SUMMARY_REPORT_FILE = None 41 | 42 | @cocotb.test() 43 | def basic_bringup(dut): 44 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 45 | yield reset_dut(dut.rst_ni, RESET_TIME) 46 | yield Timer(10000) 47 | 48 | @cocotb.test() 49 | def random_inputs(dut): 50 | drv = BPCEncoderDriver(dut, TA, TT) 51 | sb = BPCEncoderScoreboard(dut, BLOCK_SIZE, DATA_W, stim_report_file=STIM_REPORT_FILE) 52 | mon = BPCEncoderMonitor(dut, BLOCK_SIZE, DATA_W, sb) 53 | num_input_blocks = 1000 54 | input_vals = [BinaryValue(n_bits=DATA_W, bigEndian=False, value=random.randint(-2**(DATA_W-1), 2**(DATA_W-1)-1), binaryRepresentation=2) for k in range(BLOCK_SIZE*num_input_blocks)] 55 | flush = [0] * (len(input_vals)-1) 56 | flush.append(1) 57 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 58 | drv.apply_defaults() 59 | yield reset_dut(dut.rst_ni, RESET_TIME) 60 | dut._log.info("Reset Done!") 61 | for k in range(4): 62 | yield RisingEdge(dut.clk_i) 63 | mon.start() 64 | read_task = cocotb.fork(drv.read_outputs(len(input_vals)*2, tmin=0, tmax=0)) 65 | yield drv.drive_input(zip(input_vals, flush), tmin=0, tmax=0) 66 | yield Timer(4*CLOCK_PERIOD*len(input_vals)) 67 | read_task.kill() 68 | mon.stop() 69 | if sb.report(): 70 | raise TestFailure("Scoreboard reported problems - check log!") 71 | -------------------------------------------------------------------------------- /src/encoder/bpc_encoder.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | import ebpc_pkg::*; 13 | 14 | module bpc_encoder 15 | ( 16 | input logic clk_i, 17 | input logic rst_ni, 18 | input logic [DATA_W-1:0] data_i, 19 | input logic flush_i, 20 | input logic vld_i, 21 | output logic rdy_o, 22 | output logic [DATA_W-1:0] data_o, 23 | output logic last_o, 24 | output logic vld_o, 25 | input logic rdy_i, 26 | output logic idle_o, 27 | output logic waiting_for_data_o 28 | ); 29 | 30 | dbp_block_t dbp_enc_to_coder; 31 | //logic [0:DATA_W][BLOCK_SIZE-2:0] dbp, dbx; 32 | logic vld_enc_to_coder; 33 | logic rdy_coder_to_enc; 34 | logic dbp_dbx_idle; 35 | logic dbp_waiting; 36 | logic seq_coder_idle; 37 | logic sc_waiting; 38 | logic flush_dbp_dbx_to_coder; 39 | 40 | assign idle_o = seq_coder_idle && dbp_dbx_idle; 41 | assign waiting_for_data_o = sc_waiting & dbp_waiting; 42 | 43 | dbp_dbx_enc 44 | dbp_dbx_i ( 45 | .clk_i(clk_i), 46 | .rst_ni(rst_ni), 47 | .data_i(data_i), 48 | .flush_i(flush_i), 49 | .vld_i(vld_i), 50 | .rdy_o(rdy_o), 51 | .flush_o(flush_dbp_dbx_to_coder), 52 | .dbp_block_o(dbp_enc_to_coder), 53 | .vld_o(vld_enc_to_coder), 54 | .rdy_i(rdy_coder_to_enc), 55 | .idle_o(dbp_dbx_idle), 56 | .waiting_for_data_o(dbp_waiting) 57 | ); 58 | 59 | seq_coder 60 | seq_coder_i ( 61 | .clk_i(clk_i), 62 | .rst_ni(rst_ni), 63 | .dbp_block_i(dbp_enc_to_coder), 64 | .flush_i(flush_dbp_dbx_to_coder), 65 | .vld_i(vld_enc_to_coder), 66 | .rdy_o(rdy_coder_to_enc), 67 | .data_o(data_o), 68 | .last_o(last_o), 69 | .vld_o(vld_o), 70 | .rdy_i(rdy_i), 71 | .idle_o(seq_coder_idle), 72 | .waiting_for_data_o(sc_waiting) 73 | ); 74 | 75 | endmodule // bpc_encoder 76 | -------------------------------------------------------------------------------- /tb/bpc_decoder/bpc_decoder_driver.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import random 13 | import cocotb 14 | from common.drivers import HandshakeInputDriver, HandshakeOutputDriver, wait_cycles, apply_signal 15 | 16 | class BPCDecoderDriver: 17 | def __init__(self, dut, ta, tt): 18 | self.clk_i = dut.clk_i 19 | self.rst_ni = dut.rst_ni 20 | self.bpc_i = dut.bpc_i 21 | self.bpc_vld_i = dut.bpc_vld_i 22 | self.bpc_rdy_o = dut.bpc_rdy_o 23 | self.data_o = dut.data_o 24 | self.vld_o = dut.vld_o 25 | self.rdy_i = dut.rdy_i 26 | self.clr_i = dut.clr_i 27 | self.dut = dut 28 | self.ta = ta 29 | self.tt = tt 30 | self.bpc_i_drv = HandshakeInputDriver(clk=self.clk_i, signals=[self.bpc_i], vld=self.bpc_vld_i, rdy=self.bpc_rdy_o, ta=self.ta, tt=self.tt) 31 | self.out_drv = HandshakeOutputDriver(clk=self.clk_i, vld=self.vld_o, rdy=self.rdy_i, ta=self.ta, tt=self.tt) 32 | self.apply_defaults() 33 | 34 | def apply_defaults(self): 35 | self.bpc_i <= 0 36 | self.bpc_vld_i <= 0 37 | self.rdy_i <= 0 38 | self.clr_i <= 0 39 | 40 | 41 | @cocotb.coroutine 42 | def drive_input(self, values, tmin, tmax): 43 | # values: list of values to feed 44 | # tmin: minimum # of clock cycles to wait between input applications 45 | # tmax: maximum # of clock cycles to wait between applications 46 | assert self.rst_ni.value == 1, "BPCDecoderDriver Error: rst_ni is not high in drive_input!" 47 | for val in values: 48 | yield wait_cycles(self.clk_i, random.randint(tmin,tmax)) 49 | yield self.bpc_i_drv.write_input(val) 50 | 51 | @cocotb.coroutine 52 | def read_outputs(self, num, tmin, tmax): 53 | #num: how many output values to read 54 | #tmin: minimum # of clock cycles to wait between reads 55 | #tmax: maximum # of clock cycles to wait between reads 56 | assert self.rst_ni.value == 1, "BPCEncoderDriver Error: rst_ni is not high in read_outputs!" 57 | k = 0 58 | for i in range(num): 59 | yield wait_cycles(self.clk_i, random.randint(tmin, tmax)) 60 | yield self.out_drv.read_output() 61 | k+= 1 62 | return k 63 | 64 | @cocotb.coroutine 65 | def clear(self): 66 | yield apply_signal(dut.clr_i, 1, TA) 67 | yield wait_cycles(dut.clk_i, 1) 68 | yield apply_signal(dut.clr_i, 0, TA) 69 | -------------------------------------------------------------------------------- /py/common/drivers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.triggers import Timer, RisingEdge 14 | import numpy as np 15 | import random 16 | @cocotb.coroutine 17 | def apply_signal(signal, value, ta): 18 | yield Timer(ta) 19 | signal <= value 20 | 21 | @cocotb.coroutine 22 | def apply_signals(signals, values, ta): 23 | yield Timer(ta) 24 | for sig, val in zip(signals, values): 25 | sig <= val 26 | 27 | @cocotb.coroutine 28 | def wait_cycles(clk, c): 29 | for k in range(c): 30 | yield RisingEdge(clk) 31 | 32 | 33 | @cocotb.coroutine 34 | def reset_dut(rst_line, duration, units="ps"): 35 | rst_line <= 0 36 | yield Timer(duration, units=units) 37 | rst_line <= 1 38 | rst_line._log.debug("Reset complete") 39 | 40 | class HandshakeInputDriver: 41 | def __init__(self, clk, signals, vld, rdy, ta, tt): 42 | assert(ta <= tt) 43 | if isinstance(signals, tuple): 44 | signals = list(signals) 45 | if not isinstance(signals, list): 46 | signals = [signals] 47 | self.clk = clk 48 | self.signals = signals 49 | self.vld = vld 50 | self.rdy = rdy 51 | self.ta = ta 52 | self.tt = tt 53 | 54 | @cocotb.coroutine 55 | def write_input(self, values): 56 | t = 0 57 | if isinstance(values, tuple): 58 | values = list(values) 59 | if not isinstance(values, list): 60 | values = [values] 61 | cocotb.fork(apply_signals(self.signals + [self.vld], values + [1], self.ta)) 62 | yield Timer(self.tt) 63 | while self.rdy.value != 1: 64 | yield RisingEdge(self.clk) 65 | yield Timer(self.tt) 66 | t += 1 67 | yield RisingEdge(self.clk) 68 | cocotb.fork(apply_signal(self.vld, 0, self.ta)) 69 | return t 70 | 71 | class HandshakeOutputDriver: 72 | def __init__(self, clk, vld, rdy, ta, tt): 73 | assert(ta <= tt) 74 | self.clk = clk 75 | self.vld = vld 76 | self.rdy = rdy 77 | self.ta = ta 78 | self.tt = tt 79 | 80 | @cocotb.coroutine 81 | def read_output(self): 82 | cocotb.fork(apply_signal(self.rdy, 1, self.ta)) 83 | yield Timer(self.tt) 84 | while self.vld.value != 1: 85 | yield RisingEdge(self.clk) 86 | yield Timer(self.tt) 87 | yield RisingEdge(self.clk) 88 | cocotb.fork(apply_signal(self.rdy, 0, self.ta)) 89 | -------------------------------------------------------------------------------- /src/decoder/expander.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | module expander 13 | import ebpc_pkg::*; 14 | ( 15 | input logic [DATA_W-1:0] data_i, 16 | output logic [LOG_DATA_W:0] zeros_o, 17 | output symb_len_t len_o, 18 | output logic [BLOCK_SIZE-2:0] dbx_dbp_o, 19 | output logic is_dbp_o 20 | ); 21 | 22 | // thanks emacs for messing up the indentation 23 | always_comb begin 24 | zeros_o = 'd0; 25 | is_dbp_o = 1'b0; 26 | len_o = N; 27 | dbx_dbp_o = 'd0; 28 | if (data_i[DATA_W-1] == 1'b1) begin 29 | dbx_dbp_o = data_i[DATA_W-2 -: BLOCK_SIZE-1]; 30 | end else if (data_i[DATA_W-1:DATA_W-2] == 2'b01) begin 31 | dbx_dbp_o = 'd0; 32 | len_o = TWO; 33 | end else if (data_i[DATA_W-1:DATA_W-3] == 3'b001) begin 34 | dbx_dbp_o = 'd0; 35 | len_o = THREE_PLUS_LOGM; 36 | zeros_o = data_i[DATA_W-4 -: LOG_DATA_W] + 1; 37 | end else if (data_i[DATA_W-1:DATA_W-5] == 5'b00000) begin 38 | dbx_dbp_o = {BLOCK_SIZE-1{1'b1}}; 39 | len_o = FIVE; 40 | end else if (data_i[DATA_W-1:DATA_W-5] == 5'b00001) begin 41 | dbx_dbp_o = {BLOCK_SIZE-1{1'b0}}; 42 | len_o = FIVE; 43 | is_dbp_o = 1'b1; 44 | end else if (data_i[DATA_W-1:DATA_W-5] == 5'b00010) begin 45 | dbx_dbp_o = ({2'b11, {BLOCK_SIZE-3{1'b0}}} >> data_i[DATA_W - 6 -: $clog2(BLOCK_SIZE-1)]); 46 | len_o = FIVE_PLUS_LOGN; 47 | end else if (data_i[DATA_W-1:DATA_W-5] == 5'b00011) begin 48 | dbx_dbp_o = {BLOCK_SIZE-1{1'b0}} | ({1'b1, {BLOCK_SIZE-2{1'b0}}} >> data_i[DATA_W - 6 -: $clog2(BLOCK_SIZE-1)]); 49 | len_o = FIVE_PLUS_LOGN; 50 | end 51 | end // always_comb 52 | endmodule // expander 53 | -------------------------------------------------------------------------------- /tb/bpc_decoder/bpc_decoder_tests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.clock import Clock 14 | from cocotb.result import TestFailure 15 | from cocotb.triggers import Timer 16 | from common.drivers import reset_dut, wait_cycles 17 | from bpc_decoder_driver import BPCDecoderDriver 18 | from common.util import int_list_to_binval_list, random_vals 19 | from bpc_decoder_scoreboard import BPCDecoderScoreboard 20 | from bpc_decoder_monitor import BPCDecoderMonitor 21 | import random 22 | import numpy as np 23 | 24 | 25 | #import pydevd_pycharm 26 | #pydevd_pycharm.settrace('localhost', port=9100, stdoutToServer=True, stderrToServer=True) 27 | 28 | random.seed(89) 29 | np.random.seed(29) 30 | 31 | BLOCK_SIZE = 8 32 | DATA_W = 8 33 | NUM_BLOCKS_W = 14 34 | NUM_BLOCKS = 2000 35 | CLOCK_PERIOD = 2500 36 | RESET_TIME = 15000 37 | TA = 200 38 | TT = 2000 39 | 40 | STIM_REPORT_FILE = '../reports/stimuli.log' 41 | STIM_DBG_FILE = '../reports/stim_dbg.log' 42 | 43 | @cocotb.test() 44 | def basic_bringup(dut): 45 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 46 | yield reset_dut(dut.rst_ni, RESET_TIME) 47 | yield Timer(10000) 48 | 49 | @cocotb.test() 50 | def random_inputs_only(dut): 51 | num_blocks = 200 52 | drv = BPCDecoderDriver(dut, TA, TT) 53 | vals = random_vals(num_blocks*BLOCK_SIZE, DATA_W) 54 | vals = int_list_to_binval_list(vals, DATA_W, True) 55 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 56 | yield reset_dut(dut.rst_ni, RESET_TIME) 57 | yield wait_cycles(dut.clk_i, 4) 58 | cocotb.fork(drv.read_outputs(1000000, 0, 5)) 59 | yield drv.drive_input(vals, 0, 5) 60 | yield Timer(10000) 61 | dut._log.info("The assertion errors are bound to happen, as the inputs do not amount to a sensible encoding! No worries here :^)") 62 | 63 | @cocotb.test() 64 | def random_blocks(dut): 65 | drv = BPCDecoderDriver(dut, TA, TT) 66 | sb = BPCDecoderScoreboard(dut, BLOCK_SIZE, DATA_W, NUM_BLOCKS, STIM_REPORT_FILE, STIM_DBG_FILE) 67 | mon = BPCDecoderMonitor(dut, sb) 68 | encoded_vals = sb.gen_rand_stimuli() 69 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 70 | yield reset_dut(dut.rst_ni, RESET_TIME) 71 | yield wait_cycles(dut.clk_i, 4) 72 | cocotb.fork(drv.drive_input(encoded_vals, 0, 5)) 73 | mon.start() 74 | yield drv.read_outputs(NUM_BLOCKS * BLOCK_SIZE, 0, 5) 75 | yield Timer(1000) 76 | yield wait_cycles(dut.clk_i, 1) 77 | drv.clear() 78 | mon.stop() 79 | if sb.report(): 80 | raise TestFailure("Scoreboard reported problems - check log!") 81 | -------------------------------------------------------------------------------- /tb/bpc_decoder/bpc_decoder_monitor.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.clock import Clock 14 | from cocotb.triggers import RisingEdge, Timer 15 | 16 | class BPCDecoderMonitor: 17 | def __init__(self, dut, scoreboard): 18 | self.dut = dut 19 | self.clk = dut.clk_i 20 | self.bpc_i = dut.bpc_i 21 | self.bpc_vld_i = dut.bpc_vld_i 22 | self.bpc_rdy_o = dut.bpc_rdy_o 23 | self.data_o = dut.data_o 24 | self.vld_o = dut.vld_o 25 | self.rdy_i = dut.rdy_i 26 | self.scoreboard = scoreboard 27 | self.clk_listening_task = None 28 | self.in_listening_task = None 29 | self.out_listening_task = None 30 | 31 | 32 | @cocotb.coroutine 33 | def clk_listener(self): 34 | while True: 35 | yield RisingEdge(self.clk) 36 | self.scoreboard.clk_incr() 37 | 38 | @cocotb.coroutine 39 | def input_listener(self): 40 | while True: 41 | yield RisingEdge(self.clk) 42 | if self.bpc_vld_i.value == 1 and self.bpc_rdy_o.value == 0: 43 | self.scoreboard.stall_incr('in') 44 | 45 | @cocotb.coroutine 46 | def output_listener(self): 47 | while True: 48 | yield RisingEdge(self.clk) 49 | if self.vld_o.value == 1 and self.rdy_i.value == 1: 50 | self.scoreboard.push_output(self.data_o.value) 51 | elif self.vld_o.value == 1 and self.rdy_i.value == 0: 52 | self.scoreboard.stall_incr('out') 53 | 54 | 55 | def start(self): 56 | assert self.in_listening_task is None, "BPCDecoderMonitor.start() was probably called twice - in_listening_task is not None!" 57 | self.in_listening_task = cocotb.fork(self.input_listener()) 58 | assert self.out_listening_task is None, "BPCDecoderMonitor.start() was probably called twice - out_listening_task is not None!" 59 | assert self.clk_listening_task is None, "BPCDecoderMonitor.start() was probably called twice - clk_listening_task is not None!" 60 | self.out_listening_task = cocotb.fork(self.output_listener()) 61 | self.clk_listening_task = cocotb.fork(self.clk_listener()) 62 | 63 | def stop(self): 64 | assert self.in_listening_task is not None and self.out_listening_task is not None and self.clk_listening_task is not None, "Tried to stop a non-running BPCEncoderMonitor!" 65 | self.in_listening_task.kill() 66 | self.in_listening_task = None 67 | self.out_listening_task.kill() 68 | self.out_listening_task = None 69 | self.clk_listening_task.kill() 70 | self.clk_listening_task = None 71 | -------------------------------------------------------------------------------- /tb/bpc_encoder/bpc_encoder_monitor.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.triggers import RisingEdge 14 | 15 | class BPCEncoderMonitor: 16 | def __init__(self, dut, block_size, data_w, scoreboard): 17 | self.clk = dut.clk_i 18 | self.rst_n = dut.rst_ni 19 | self.di = dut.data_i 20 | self.flush = dut.flush_i 21 | self.vld_i = dut.vld_i 22 | self.rdy_o = dut.rdy_o 23 | self.do = dut.data_o 24 | self.vld_o = dut.vld_o 25 | self.rdy_i = dut.rdy_i 26 | self.dbx_vld = dut.seq_coder_i.vld_from_slice 27 | self.dbx_rdy = dut.seq_coder_i.rdy_to_slice 28 | self.dbx_d = dut.seq_coder_i.compressor_i.dbx 29 | self.data_w = data_w 30 | self.in_listening_task = None 31 | self.out_listening_task = None 32 | self.clk_listening_task = None 33 | self.scoreboard = scoreboard 34 | 35 | @cocotb.coroutine 36 | def clk_listener(self): 37 | while True: 38 | yield RisingEdge(self.clk) 39 | self.scoreboard.clk_incr() 40 | 41 | @cocotb.coroutine 42 | def input_listener(self): 43 | while True: 44 | yield RisingEdge(self.clk) 45 | if self.vld_i.value == 1 and self.rdy_o.value == 1: 46 | self.scoreboard.push_input((self.di.value, self.flush.value)) 47 | elif self.vld_i.value == 1 and self.rdy_o.value == 0: 48 | self.scoreboard.stall_incr('in') 49 | 50 | @cocotb.coroutine 51 | def output_listener(self): 52 | while True: 53 | yield RisingEdge(self.clk) 54 | if self.vld_o.value == 1 and self.rdy_i.value == 1: 55 | self.scoreboard.push_output(self.do.value) 56 | elif self.vld_o.value == 1 and self.rdy_i.value == 0: 57 | self.scoreboard.stall_incr('out') 58 | 59 | def start(self): 60 | assert self.in_listening_task is None, "BMCEncoderMonitor.start() was probably called twice - in_listening_task is not None!" 61 | self.in_listening_task = cocotb.fork(self.input_listener()) 62 | assert self.out_listening_task is None, "BMCEncoderMonitor.start() was probably called twice - out_listening_task is not None!" 63 | self.out_listening_task = cocotb.fork(self.output_listener()) 64 | self.clk_listening_task = cocotb.fork(self.clk_listener()) 65 | 66 | def stop(self): 67 | assert self.in_listening_task is not None and self.out_listening_task is not None, "Tried to stop a non-running BPCEncoderMonitor!" 68 | self.in_listening_task.kill() 69 | self.in_listening_task = None 70 | self.out_listening_task.kill() 71 | self.out_listening_task = None 72 | self.clk_listening_task.kill() 73 | self.clk_listening_task = None 74 | -------------------------------------------------------------------------------- /tb/ebpc_decoder/ebpc_decoder_tests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.clock import Clock 14 | from cocotb.triggers import Timer, RisingEdge 15 | from common.drivers import reset_dut, wait_cycles 16 | from ebpc_decoder_driver import EBPCDecoderDriver 17 | from ebpc_decoder_scoreboard import EBPCDecoderScoreboard 18 | from ebpc_decoder_monitor import EBPCDecoderMonitor 19 | import numpy as np 20 | import random 21 | 22 | #import pydevd_pycharm 23 | #pydevd_pycharm.settrace('localhost', port=9100, stdoutToServer=True, stderrToServer=True) 24 | 25 | random.seed(29) 26 | np.random.seed(29) 27 | 28 | CLOCK_PERIOD = 2500 29 | RESET_TIME = 15000 30 | DATA_W = 8 31 | BLOCK_SIZE = 8 32 | MAX_ZRLE_LEN = 16 33 | TA = 200 34 | TT = 2000 35 | NUM_WORDS = 60001 36 | 37 | @cocotb.test() 38 | def random_inputs_without_last(dut): 39 | 40 | drv = EBPCDecoderDriver(dut, TA, TT) 41 | sb = EBPCDecoderScoreboard(dut, BLOCK_SIZE, DATA_W, MAX_ZRLE_LEN) 42 | mon = EBPCDecoderMonitor(dut, sb) 43 | bpc_in, znz_in = sb.gen_rand_stimuli(NUM_WORDS, 10, 0.2) 44 | bpc_last_in = [0]*len(bpc_in) 45 | znz_last_in = [0]*len(znz_in) 46 | drv.apply_defaults() 47 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 48 | yield reset_dut(dut.rst_ni, RESET_TIME) 49 | yield wait_cycles(dut.clk_i, 4) 50 | mon.start() 51 | num_words_feed_task = cocotb.fork(drv.write_num_words(NUM_WORDS-1, 0, 50)) 52 | bpc_feed_task = cocotb.fork(drv.drive_bpc(bpc_in, bpc_last_in, 0, 0)) 53 | znz_feed_task = cocotb.fork(drv.drive_znz(znz_in, znz_last_in, 0, 0)) 54 | yield drv.read_outputs(100000000, 0, 0) 55 | bpc_feed_task.kill() 56 | znz_feed_task.kill() 57 | num_words_feed_task.kill() 58 | mon.stop() 59 | if sb.report(): 60 | raise TestFailure("Scoreboard reported problems - check log!") 61 | 62 | @cocotb.test() 63 | def random_inputs_with_last(dut): 64 | 65 | drv = EBPCDecoderDriver(dut, TA, TT) 66 | sb = EBPCDecoderScoreboard(dut, BLOCK_SIZE, DATA_W, MAX_ZRLE_LEN) 67 | mon = EBPCDecoderMonitor(dut, sb) 68 | bpc_in, znz_in = sb.gen_rand_stimuli(NUM_WORDS, 10, 0.2) 69 | bpc_last_in = [0]*(len(bpc_in)-1) + [1] 70 | znz_last_in = [0]*(len(znz_in)-1) + [1] 71 | drv.apply_defaults() 72 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 73 | yield reset_dut(dut.rst_ni, RESET_TIME) 74 | yield wait_cycles(dut.clk_i, 4) 75 | mon.start() 76 | num_words_feed_task = cocotb.fork(drv.write_num_words(NUM_WORDS-1, 0, 50)) 77 | bpc_feed_task = cocotb.fork(drv.drive_bpc(bpc_in, bpc_last_in, 0, 0)) 78 | znz_feed_task = cocotb.fork(drv.drive_znz(znz_in, znz_last_in, 0, 0)) 79 | yield drv.read_outputs(100000000, 0, 0) 80 | bpc_feed_task.kill() 81 | znz_feed_task.kill() 82 | num_words_feed_task.kill() 83 | mon.stop() 84 | if sb.report(): 85 | raise TestFailure("Scoreboard reported problems - check log!") 86 | -------------------------------------------------------------------------------- /src/decoder/delta_reverse.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | module delta_reverse 13 | import ebpc_pkg::*; 14 | ( 15 | input logic clk_i, 16 | input logic rst_ni, 17 | input dbp_block_t data_i, 18 | input logic vld_i, 19 | output logic rdy_o, 20 | output logic signed [DATA_W-1:0] data_o, 21 | output logic vld_o, 22 | input logic rdy_i, 23 | input logic clr_i 24 | ); 25 | 26 | logic signed [DATA_W:0] diffs [0:BLOCK_SIZE-2]; 27 | typedef enum {base, stream} state_t; 28 | state_t state_d, state_q; 29 | logic signed [DATA_W-1:0] acc_reg_d, acc_reg_q; 30 | logic [$clog2(BLOCK_SIZE-1)-1:0] diff_idx_d, diff_idx_q; 31 | 32 | always_comb begin 33 | for (int i=0; i=0; i--) begin 72 | cnt = cnt+dbx_i[i]; 73 | if (dbx_i[i]) begin 74 | if (prev_was_one) 75 | consec_ones = 1'b1; 76 | else 77 | pos = BLOCK_SIZE-2-i; 78 | prev_was_one = 1'b1; 79 | end else 80 | prev_was_one = 1'b0; 81 | if (cnt >2) 82 | break; 83 | end // for (int i=0; i 0 69 | return r 70 | 71 | def report_stim(self, fn): 72 | if fn is not None: 73 | with open(fn, "w+") as fh: 74 | fh.write("BPC DECODER STIMULI REPORT\n\n") 75 | fh.write("Number of blocks: {}\n".format(self.num_blocks)) 76 | fh.write("Words per block: {}\n".format(self.block_size)) 77 | fh.write("Word Width: {}\n\n".format(self.data_w)) 78 | for b in range(len(self.inputs)): 79 | fh.write("{} / {}\n".format(self.inputs[b].binstr, self.inputs[b].value)) 80 | -------------------------------------------------------------------------------- /tb/ebpc_decoder/ebpc_decoder_monitor.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.triggers import RisingEdge 14 | 15 | class EBPCDecoderMonitor: 16 | def __init__(self, dut, scoreboard): 17 | self.dut = dut 18 | self.clk = dut.clk_i 19 | self.bpc_i = dut.bpc_i 20 | self.bpc_vld_i = dut.bpc_vld_i 21 | self.bpc_rdy_o = dut.bpc_rdy_o 22 | self.znz_i = dut.znz_i 23 | self.znz_vld_i = dut.znz_vld_i 24 | self.znz_rdy_o = dut.znz_rdy_o 25 | self.data_o = dut.data_o 26 | self.last_o = dut.last_o 27 | self.vld_o = dut.vld_o 28 | self.rdy_i = dut.rdy_i 29 | self.scoreboard = scoreboard 30 | self.clk_listening_task = None 31 | self.bpc_listening_task = None 32 | self.znz_listening_task = None 33 | self.out_listening_task = None 34 | 35 | 36 | 37 | @cocotb.coroutine 38 | def clk_listener(self): 39 | while True: 40 | yield RisingEdge(self.clk) 41 | self.scoreboard.clk_incr() 42 | 43 | @cocotb.coroutine 44 | def bpc_listener(self): 45 | while True: 46 | yield RisingEdge(self.clk) 47 | # we want to count stalls caused by TB 48 | if self.bpc_vld_i.value == 0 and self.bpc_rdy_o.value == 1: 49 | self.scoreboard.stall_incr('bpc') 50 | 51 | @cocotb.coroutine 52 | def znz_listener(self): 53 | while True: 54 | yield RisingEdge(self.clk) 55 | if self.znz_vld_i.value == 0 and self.znz_rdy_o.value == 1: 56 | self.scoreboard.stall_incr('znz') 57 | 58 | @cocotb.coroutine 59 | def output_listener(self): 60 | while True: 61 | yield RisingEdge(self.clk) 62 | if self.vld_o.value == 1 and self.rdy_i.value == 1: 63 | self.scoreboard.push_output(self.data_o.value) 64 | elif self.vld_o.value == 1 and self.rdy_i.value == 0: 65 | self.scoreboard.stall_incr('out') 66 | 67 | def start(self): 68 | assert self.bpc_listening_task is None, "BPCDecoderMonitor.start() was probably called twice - bpc_listening_task is not None!" 69 | self.bpc_listening_task = cocotb.fork(self.bpc_listener()) 70 | assert self.znz_listening_task is None, "BPCDecoderMonitor.start() was probably called twice - znz_listening_task is not None!" 71 | self.znz_listening_task = cocotb.fork(self.bpc_listener()) 72 | assert self.out_listening_task is None, "BPCDecoderMonitor.start() was probably called twice - out_listening_task is not None!" 73 | self.out_listening_task = cocotb.fork(self.output_listener()) 74 | assert self.clk_listening_task is None, "BPCDecoderMonitor.start() was probably called twice - clk_listening_task is not None!" 75 | self.clk_listening_task = cocotb.fork(self.clk_listener()) 76 | 77 | def stop(self): 78 | assert self.bpc_listening_task is not None and self.znz_listening_task is not None and self.out_listening_task is not None and self.clk_listening_task is not None, "Tried to stop a non-running BPCEncoderMonitor!" 79 | self.bpc_listening_task.kill() 80 | self.bpc_listening_task = None 81 | self.znz_listening_task.kill() 82 | self.znz_listening_task = None 83 | self.out_listening_task.kill() 84 | self.out_listening_task = None 85 | self.clk_listening_task.kill() 86 | self.clk_listening_task = None 87 | -------------------------------------------------------------------------------- /src/decoder/bpc_decoder.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | module bpc_decoder 13 | import ebpc_pkg::*; 14 | ( 15 | input logic clk_i, 16 | input logic rst_ni, 17 | input logic [DATA_W-1:0] bpc_i, 18 | input logic bpc_vld_i, 19 | output logic bpc_rdy_o, 20 | output logic [DATA_W-1:0] data_o, 21 | output logic vld_o, 22 | input logic rdy_i, 23 | input logic clr_i 24 | ); 25 | 26 | // signals between unpacker and decoder 27 | logic [DATA_W-1:0] data_unpacker_to_decoder; 28 | logic [LOG_DATA_W:0] fill_state_unpacker_to_decoder; 29 | logic [LOG_DATA_W:0] len_decoder_to_unpacker; 30 | logic vld_unpacker_to_decoder, rdy_decoder_to_unpacker; 31 | // signals between decoder and buffer 32 | logic [DATA_W-1:0] data_decoder_to_buffer; 33 | logic push_decoder_to_buffer; 34 | logic vld_decoder_to_buffer, rdy_buffer_to_decoder; 35 | 36 | // signals between buffer and delta reverse 37 | dbp_block_t data_buffer_to_dr; 38 | logic vld_buffer_to_dr, rdy_dr_to_buffer; 39 | 40 | unpacker 41 | unpacker_i ( 42 | .clk_i(clk_i), 43 | .rst_ni(rst_ni), 44 | .data_i(bpc_i), 45 | .vld_i(bpc_vld_i), 46 | .rdy_o(bpc_rdy_o), 47 | .data_o(data_unpacker_to_decoder), 48 | .fill_state_o(fill_state_unpacker_to_decoder), 49 | .len_i(len_decoder_to_unpacker), 50 | .vld_o(vld_unpacker_to_decoder), 51 | .rdy_i(rdy_decoder_to_unpacker), 52 | .clr_i(clr_i) 53 | ); 54 | 55 | symbol_decoder 56 | decoder_i( 57 | .clk_i(clk_i), 58 | .rst_ni(rst_ni), 59 | .data_i(data_unpacker_to_decoder), 60 | .unpacker_fill_state_i(fill_state_unpacker_to_decoder), 61 | .len_o(len_decoder_to_unpacker), 62 | .data_vld_i(vld_unpacker_to_decoder), 63 | .data_rdy_o(rdy_decoder_to_unpacker), 64 | .data_o(data_decoder_to_buffer), 65 | .push_o(push_decoder_to_buffer), 66 | .vld_o(vld_decoder_to_buffer), 67 | .rdy_i(rdy_buffer_to_decoder), 68 | .clr_i(clr_i) 69 | ); 70 | 71 | buffer 72 | buffer_i( 73 | .clk_i(clk_i), 74 | .rst_ni(rst_ni), 75 | .data_i(data_decoder_to_buffer), 76 | .push_i(push_decoder_to_buffer), 77 | .vld_i(vld_decoder_to_buffer), 78 | .rdy_o(rdy_buffer_to_decoder), 79 | .data_o(data_buffer_to_dr), 80 | .vld_o(vld_buffer_to_dr), 81 | .rdy_i(rdy_dr_to_buffer), 82 | .clr_i(clr_i) 83 | ); 84 | 85 | delta_reverse 86 | dr_i( 87 | .clk_i(clk_i), 88 | .rst_ni(rst_ni), 89 | .data_i(data_buffer_to_dr), 90 | .vld_i(vld_buffer_to_dr), 91 | .rdy_o(rdy_dr_to_buffer), 92 | .data_o(data_o), 93 | .vld_o(vld_o), 94 | .rdy_i(rdy_i), 95 | .clr_i(clr_i) 96 | ); 97 | 98 | endmodule // bpc_decoder 99 | -------------------------------------------------------------------------------- /rtl_tb/ebpc_decoder/decoder_xcelium_commands.tcl: -------------------------------------------------------------------------------- 1 | set assert_stop_level never 2 | simvision -input decoder_simvision_commands.tcl 3 | database -open waves -into waves.shm -default 4 | probe -create -database waves ebpc_decoder_tb.dut_i.clk_i ebpc_decoder_tb.dut_i.rst_ni ebpc_decoder_tb.dut_i.num_words_i ebpc_decoder_tb.dut_i.num_words_vld_i ebpc_decoder_tb.dut_i.num_words_rdy_o ebpc_decoder_tb.dut_i.bpc_i ebpc_decoder_tb.dut_i.bpc_vld_i ebpc_decoder_tb.dut_i.bpc_rdy_o ebpc_decoder_tb.dut_i.znz_i ebpc_decoder_tb.dut_i.znz_vld_i ebpc_decoder_tb.dut_i.znz_rdy_o ebpc_decoder_tb.dut_i.data_o ebpc_decoder_tb.dut_i.last_o ebpc_decoder_tb.dut_i.vld_o ebpc_decoder_tb.dut_i.rdy_i ebpc_decoder_tb.dut_i.znz ebpc_decoder_tb.dut_i.word_cnt_q ebpc_decoder_tb.dut_i.word_cnt_d ebpc_decoder_tb.dut_i.vld_to_zrld ebpc_decoder_tb.dut_i.vld_from_zrld ebpc_decoder_tb.dut_i.vld_from_bpc ebpc_decoder_tb.dut_i.state_q ebpc_decoder_tb.dut_i.state_d ebpc_decoder_tb.dut_i.rdy_to_zrld ebpc_decoder_tb.dut_i.rdy_to_bpc ebpc_decoder_tb.dut_i.rdy_from_zrld ebpc_decoder_tb.dut_i.flush_to_zrld ebpc_decoder_tb.dut_i.data_from_bpc ebpc_decoder_tb.dut_i.clr_to_bpc ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.data_i ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.vld_i ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.rdy_o ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.data_o ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.fill_state_o ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.len_i ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.vld_o ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.rdy_i ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.clr_i ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.fill_state_d ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.fill_state_q ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.state_d ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.state_q ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.stream_reg_d ebpc_decoder_tb.dut_i.bpcd_i.unpacker_i.stream_reg_q ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.clr_i ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.data_i ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.unpacker_fill_state_i ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.len_o ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.data_vld_i ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.data_rdy_o ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.data_o ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.push_o ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.vld_o ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.rdy_i ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.dbp_reg_d ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.dbp_reg_q ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.dbx_cnt_d ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.dbx_cnt_q ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.expander_is_dbp ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.expander_len ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.expander_out ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.expander_zeros ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.state_d ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.state_q ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.zero_cnt_d ebpc_decoder_tb.dut_i.bpcd_i.decoder_i.zero_cnt_q ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.data_i ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.vld_i ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.push_i ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.rdy_o ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.data_o ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.vld_o ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.rdy_i ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.clr_i ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.base_d ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.base_q ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.dbp_block_to_fifo ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.rdy_from_slice ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.rdy_to_slice ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.shift_reg_d ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.shift_reg_q ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.state_d ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.state_q ebpc_decoder_tb.dut_i.bpcd_i.buffer_i.vld_to_slice ebpc_decoder_tb.dut_i.bpcd_i.dr_i.data_i ebpc_decoder_tb.dut_i.bpcd_i.dr_i.vld_i ebpc_decoder_tb.dut_i.bpcd_i.dr_i.rdy_o ebpc_decoder_tb.dut_i.bpcd_i.dr_i.data_o ebpc_decoder_tb.dut_i.bpcd_i.dr_i.vld_o ebpc_decoder_tb.dut_i.bpcd_i.dr_i.rdy_i ebpc_decoder_tb.dut_i.bpcd_i.dr_i.clr_i ebpc_decoder_tb.dut_i.bpcd_i.dr_i.acc_reg_d ebpc_decoder_tb.dut_i.bpcd_i.dr_i.acc_reg_q ebpc_decoder_tb.dut_i.bpcd_i.dr_i.diff_idx_d ebpc_decoder_tb.dut_i.bpcd_i.dr_i.diff_idx_q ebpc_decoder_tb.dut_i.bpcd_i.dr_i.diffs ebpc_decoder_tb.dut_i.bpcd_i.dr_i.state_d ebpc_decoder_tb.dut_i.bpcd_i.dr_i.state_q 5 | run 6 | -------------------------------------------------------------------------------- /rtl_tb/ebpc_encoder/encoder_xcelium_commands.tcl: -------------------------------------------------------------------------------- 1 | set assert_stop_level never 2 | simvision -input encoder_simvision_commands.tcl 3 | 4 | database -open waves -into waves.shm -default 5 | probe -create -database waves ebpc_encoder_tb.dut_i.clk_i ebpc_encoder_tb.dut_i.rst_ni ebpc_encoder_tb.dut_i.data_i ebpc_encoder_tb.dut_i.last_i ebpc_encoder_tb.dut_i.vld_i ebpc_encoder_tb.dut_i.rdy_o ebpc_encoder_tb.dut_i.bpc_data_o ebpc_encoder_tb.dut_i.bpc_vld_o ebpc_encoder_tb.dut_i.bpc_rdy_i ebpc_encoder_tb.dut_i.znz_data_o ebpc_encoder_tb.dut_i.znz_vld_o ebpc_encoder_tb.dut_i.znz_rdy_i ebpc_encoder_tb.dut_i.idle_o ebpc_encoder_tb.dut_i.block_cnt_d ebpc_encoder_tb.dut_i.block_cnt_q ebpc_encoder_tb.dut_i.bpc_idle ebpc_encoder_tb.dut_i.data_reg_d ebpc_encoder_tb.dut_i.data_reg_en ebpc_encoder_tb.dut_i.data_reg_q ebpc_encoder_tb.dut_i.data_to_bpc ebpc_encoder_tb.dut_i.flush ebpc_encoder_tb.dut_i.is_one_to_znz ebpc_encoder_tb.dut_i.last_d ebpc_encoder_tb.dut_i.last_q ebpc_encoder_tb.dut_i.rdy_from_bpc ebpc_encoder_tb.dut_i.rdy_from_znz ebpc_encoder_tb.dut_i.state_d ebpc_encoder_tb.dut_i.state_q ebpc_encoder_tb.dut_i.vld_to_bpc ebpc_encoder_tb.dut_i.vld_to_znz ebpc_encoder_tb.dut_i.wait_rdy_d ebpc_encoder_tb.dut_i.wait_rdy_q ebpc_encoder_tb.dut_i.znz_idle ebpc_encoder_tb.dut_i.bpc_encoder_i.data_i ebpc_encoder_tb.dut_i.bpc_encoder_i.vld_i ebpc_encoder_tb.dut_i.bpc_encoder_i.rdy_o ebpc_encoder_tb.dut_i.bpc_encoder_i.data_o ebpc_encoder_tb.dut_i.bpc_encoder_i.vld_o ebpc_encoder_tb.dut_i.bpc_encoder_i.rdy_i ebpc_encoder_tb.dut_i.bpc_encoder_i.flush_i ebpc_encoder_tb.dut_i.bpc_encoder_i.idle_o ebpc_encoder_tb.dut_i.bpc_encoder_i.vld_enc_to_coder ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_idle ebpc_encoder_tb.dut_i.bpc_encoder_i.rdy_coder_to_enc ebpc_encoder_tb.dut_i.bpc_encoder_i.flush_dbp_dbx_to_coder ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_enc_to_coder ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_idle ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.data_i ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.vld_i ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.rdy_o ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.dbp_block_o ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.vld_o ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.rdy_i ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.flush_i ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.flush_o ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.idle_o ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.dbp ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.diffs_d ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.diffs_q ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.fill_cnt_d ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.fill_cnt_q ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.last_item_d ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.last_item_q ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.shift ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.state_d ebpc_encoder_tb.dut_i.bpc_encoder_i.dbp_dbx_i.state_q ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.dbp_block_i ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.vld_i ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.rdy_o ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.data_o ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.vld_o ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.rdy_i ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.flush_i ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.idle_o ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.code_symb ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.data ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.dbp_block_from_fifo ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.dbx_cnt_d ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.dbx_cnt_q ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.flush ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.last_was_zero_d ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.last_was_zero_q ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.rdy_to_slice ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.shift ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.shift_rdy ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.shift_vld ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.state_d ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.state_q ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.streamer_idle ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.vld_from_slice ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.zero_cnt_d ebpc_encoder_tb.dut_i.bpc_encoder_i.seq_coder_i.zero_cnt_q 6 | run 7 | -------------------------------------------------------------------------------- /tb/ebpc_decoder/ebpc_decoder_scoreboard.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | from common import bpc 13 | from common.bpc import valuesToBinary 14 | 15 | from common.util import zero_pad_bitstr, zero_pad_list, split_str, int_list_to_binval_list, get_random_nonzero_in_range 16 | import numpy as np 17 | import random 18 | import math 19 | 20 | 21 | class EBPCDecoderScoreboard: 22 | def __init__(self, dut, block_size, data_w, max_zrle_len): 23 | self.dut = dut 24 | self.block_size = block_size 25 | self.data_w = data_w 26 | self.inputs = {'bpc':[], 'znz':[]} 27 | self.outputs_exp = [] 28 | self.outputs_act = [] 29 | self.mismatches = 0 30 | self.cycle_cnt = 0 31 | self.stalls = {'bpc': 0, 'znz': 0, 'out': 0} 32 | self.max_zrle_len = max_zrle_len 33 | self.curr_output = 0 34 | 35 | def clk_incr(self): 36 | self.cycle_cnt += 1 37 | 38 | def stall_incr(self, key): 39 | self.stalls[key] += 1 40 | 41 | def gen_rand_stimuli(self, n_inputs, max_zero_block, p_zero): 42 | # generate random stimuli and return them as a list of (data_i, last_i) tuples 43 | data_decoded = [] 44 | znz_stream = [] 45 | nz_data = [] 46 | i = 0 47 | while i < n_inputs: 48 | if (np.random.binomial(1, p_zero)): 49 | zero_len = min(random.randint(1, max_zero_block), n_inputs-i) 50 | znz_stream += [0] * zero_len 51 | i += zero_len 52 | data_decoded += [0]*zero_len 53 | else: 54 | znz_stream.append(1) 55 | dat = get_random_nonzero_in_range(-2**(self.data_w-1), 2**(self.data_w-1)-1) 56 | nz_data.append(dat) 57 | data_decoded.append(dat) 58 | i += 1 59 | nz_data = zero_pad_list(nz_data, self.block_size) 60 | self.inputs['bpc'] = bpc.BPC_words(np.array(nz_data),self.block_size,variant='paper',word_w=self.data_w) 61 | self.inputs['znz'] = bpc.ZRLE_words(data_decoded, max_burst_len=self.max_zrle_len, wordwidth=self.data_w) 62 | data_decoded_bin = valuesToBinary(np.array(data_decoded), wordwidth=self.data_w) 63 | self.outputs_exp += split_str(data_decoded_bin, self.data_w) 64 | 65 | # very pythonic. 66 | return int_list_to_binval_list(self.inputs['bpc'], self.data_w, True), int_list_to_binval_list(self.inputs['znz'], self.data_w, True) 67 | 68 | def push_output(self, out_act): 69 | self.outputs_act.append(out_act.binstr) 70 | try: 71 | if out_act.binstr != self.outputs_exp[self.curr_output]: 72 | self.mismatches += 1 73 | self.dut._log.warning("Output mismatch! Expected: {} Got: {}".format(self.outputs_exp[self.curr_output], out_act.binstr)) 74 | except IndexError: 75 | print("Warning: Tried to push output for which there is no expected value! Ignored") 76 | self.curr_output += 1 77 | 78 | def report(self): 79 | self.dut._log.info("Scoreboard: Fed {} bpc, {} znz inputs".format(len(self.inputs['bpc']), len(self.inputs['znz']))) 80 | self.dut._log.info("Scoreboard: Read {} outputs with {} mismatches - {} such outputs were expected".format(len(self.outputs_act), self.mismatches, len(self.outputs_exp))) 81 | self.dut._log.info("Scoreboard: Took {} cycles, bpc/znz inputs were stalled for {}/{} cycles respectively, output was stalled for {} cycles".format(self.cycle_cnt, self.stalls['bpc'], self.stalls['znz'], self.stalls['out'])) 82 | if (self.mismatches==0): 83 | self.dut._log.info("Scoreboard: GREAT SUCCESS! WE THE BEST!") 84 | r = self.mismatches > 0 85 | return r 86 | -------------------------------------------------------------------------------- /src/decoder/buffer.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | module buffer 13 | import ebpc_pkg::*; 14 | ( 15 | input logic clk_i, 16 | input logic rst_ni, 17 | input logic [DATA_W-1:0] data_i, 18 | //input logic base_i, 19 | input logic push_i, 20 | input logic vld_i, 21 | output logic rdy_o, 22 | output dbp_block_t data_o, 23 | output logic vld_o, 24 | input logic rdy_i, 25 | input logic clr_i 26 | ); 27 | 28 | typedef enum {wait_base, filling, full} state_t; 29 | state_t state_d, state_q; 30 | logic [DATA_W-1:0] base_d, base_q; 31 | // dbps are shifted in from [DATA_W] to [0] 32 | logic [BLOCK_SIZE-2:0] shift_reg_d [DATA_W:0], shift_reg_q [DATA_W:0]; 33 | logic vld_to_slice, rdy_from_slice; 34 | logic rdy_to_slice; 35 | 36 | dbp_block_t dbp_block_to_fifo; 37 | 38 | 39 | always_comb begin : dbp_block_assign 40 | dbp_block_to_fifo.base = base_q; 41 | for (int i=0; i=0; i--) 66 | shift_reg_d[i] = shift_reg_q[i+1]; 67 | end 68 | // if vld_i is high, the shift reg is full - we allow 69 | // the handshake to happen also in a cycle where no push is performed 70 | if (vld_i) begin 71 | state_d = full; 72 | end 73 | end // case: filling 74 | full : begin 75 | vld_to_slice = 1'b1; 76 | if (rdy_from_slice) begin 77 | rdy_o = 1'b1; 78 | if (push_i) begin 79 | base_d = data_i; 80 | state_d = filling; 81 | // TODO: what if vld_i is high here - this should never happen but... 82 | end else 83 | state_d = wait_base; 84 | end 85 | end 86 | endcase // case (state_q) 87 | // soft clear - reset state 88 | if (clr_i) begin 89 | for (int i=0; i>fill_state_d); 67 | fill_state_d = fill_state_d+DATA_W; 68 | end else 69 | state_d = filling; 70 | end 71 | end // if (rdy_i) 72 | end // case: full 73 | filling : begin 74 | rdy_o = 1'b1; 75 | if (vld_i) begin 76 | state_d = full; 77 | stream_reg_d = stream_reg_q | ({data_i, {DATA_W{1'b0}}}>>fill_state_d); 78 | fill_state_d = fill_state_q + DATA_W; 79 | end 80 | if (fill_state_q != 'd0) begin 81 | vld_o = 1'b1; 82 | if (rdy_i) begin 83 | shift = len_i; 84 | stream_reg_d = stream_reg_d << shift; 85 | fill_state_d = fill_state_d - shift; 86 | end 87 | end 88 | end 89 | endcase // case (state_q) 90 | // soft clear - we can complete an output (not input hence rdy_o=0) 91 | // transaction happening in this cycle, but the state is reset 92 | if (clr_i) begin 93 | rdy_o = 1'b0; 94 | stream_reg_d = 'd0; 95 | fill_state_d = 'd0; 96 | state_d = idle; 97 | end 98 | end 99 | 100 | always_ff @(posedge(clk_i) or negedge(rst_ni)) begin : sequential 101 | if (~rst_ni) begin 102 | state_q <= idle; 103 | stream_reg_q <= 'd0; 104 | fill_state_q <= 'd0; 105 | end else begin 106 | state_q <= state_d; 107 | stream_reg_q <= stream_reg_d; 108 | fill_state_q <= fill_state_d; 109 | end 110 | end // block: sequential 111 | 112 | endmodule 113 | -------------------------------------------------------------------------------- /rtl_tb/ebpc_encoder/ebpc_encoder_tb.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | `timescale 1ps/1fs 12 | 13 | module ebpc_encoder_tb; 14 | import hs_drv_pkg::*; 15 | import ebpc_pkg::*; 16 | 17 | localparam int unsigned MIN_IN_WAIT_CYCLES = 0; 18 | localparam int unsigned MAX_IN_WAIT_CYCLES = 0; 19 | localparam int unsigned MIN_OUT_WAIT_CYCLES = 0; 20 | localparam int unsigned MAX_OUT_WAIT_CYCLES = 0; 21 | 22 | localparam string INPUT_STIM_FILE = "/home/georgr/projects/stream-ebpc/simvectors/encoder/resnet34/resnet34_f0.05_bs1_nb4_ww8_input.stim"; 23 | localparam string BPC_EXPVAL_FILE = "/home/georgr/projects/stream-ebpc/simvectors/encoder/resnet34/resnet34_f0.05_bs1_nb4_ww8_bpc.expresp"; 24 | localparam string ZNZ_EXPVAL_FILE = "/home/georgr/projects/stream-ebpc/simvectors/encoder/resnet34/resnet34_f0.05_bs1_nb4_ww8_znz.expresp"; 25 | 26 | // localparam string INPUT_STIM_FILE = "/home/georgr/projects/stream-ebpc/simvectors/encoder/last_test/last_test_f0.05_bs1_nb4_ww8_input.stim"; 27 | // localparam string BPC_EXPVAL_FILE = "/home/georgr/projects/stream-ebpc/simvectors/encoder/last_test/last_test_f0.05_bs1_nb4_ww8_bpc.expresp"; 28 | // localparam string ZNZ_EXPVAL_FILE = "/home/georgr/projects/stream-ebpc/simvectors/encoder/last_test/last_test_f0.05_bs1_nb4_ww8_znz.expresp"; 29 | 30 | localparam time CLK_PERIOD = 1.8ns; 31 | localparam time RST_TIME = 10*CLK_PERIOD; 32 | localparam time TA = 0.2*CLK_PERIOD; 33 | localparam time TT = 0.85*CLK_PERIOD; 34 | 35 | logic clk; 36 | logic rst_n; 37 | 38 | 39 | HandshakeIf_t #(.DATA_W(DATA_W)) in_if(.clk_i(clk)); 40 | HandshakeIf_t #(.DATA_W(DATA_W)) bpc_if(.clk_i(clk)); 41 | HandshakeIf_t #(.DATA_W(DATA_W)) znz_if(.clk_i(clk)); 42 | 43 | HandshakeDrv #( 44 | .DATA_W(DATA_W), 45 | .TA(TA), 46 | .TT(TT), 47 | .MIN_WAIT(MIN_IN_WAIT_CYCLES), 48 | .MAX_WAIT(MAX_IN_WAIT_CYCLES), 49 | .HAS_LAST(1'b1), 50 | .NAME("Data Input") 51 | ) 52 | in_drv; 53 | 54 | HandshakeDrv #( 55 | .DATA_W(DATA_W), 56 | .TA(TA), 57 | .TT(TT), 58 | .MIN_WAIT(MIN_OUT_WAIT_CYCLES), 59 | .MAX_WAIT(MAX_OUT_WAIT_CYCLES), 60 | .HAS_LAST(1'b1), 61 | .NAME("BPC Output") 62 | ) 63 | bpc_drv; 64 | 65 | HandshakeDrv #( 66 | .DATA_W(DATA_W), 67 | .TA(TA), 68 | .TT(TT), 69 | .MIN_WAIT(MIN_OUT_WAIT_CYCLES), 70 | .MAX_WAIT(MAX_OUT_WAIT_CYCLES), 71 | .HAS_LAST(1'b1), 72 | .NAME("ZNZ Output") 73 | ) 74 | znz_drv; 75 | 76 | initial begin 77 | in_drv = new(in_if); 78 | in_drv.reset_out(); 79 | bpc_drv = new(bpc_if); 80 | bpc_drv.reset_in(); 81 | znz_drv = new(znz_if); 82 | znz_drv.reset_in(); 83 | #(2*RST_TIME); 84 | fork 85 | in_drv.feed_inputs(INPUT_STIM_FILE); 86 | bpc_drv.read_outputs(BPC_EXPVAL_FILE); 87 | znz_drv.read_outputs(ZNZ_EXPVAL_FILE); 88 | join 89 | $stop; 90 | end 91 | 92 | 93 | 94 | rst_clk_drv #( 95 | .CLK_PERIOD(CLK_PERIOD), 96 | .RST_TIME(RST_TIME) 97 | ) 98 | clk_drv 99 | ( 100 | .clk_o(clk), 101 | .rst_no(rst_n) 102 | ); 103 | 104 | ebpc_encoder dut_i ( 105 | .clk_i(clk), 106 | .rst_ni(rst_n), 107 | .data_i(in_if.data), 108 | .last_i(in_if.last), 109 | .vld_i(in_if.vld), 110 | .rdy_o(in_if.rdy), 111 | .znz_data_o(znz_if.data), 112 | .znz_last_o(znz_if.last), 113 | .znz_vld_o(znz_if.vld), 114 | .znz_rdy_i(znz_if.rdy), 115 | .bpc_data_o(bpc_if.data), 116 | .bpc_vld_o(bpc_if.vld), 117 | .bpc_rdy_i(bpc_if.rdy) 118 | ); 119 | 120 | endmodule 121 | -------------------------------------------------------------------------------- /tb/ebpc_encoder/ebpc_encoder_monitor.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.triggers import Timer, RisingEdge 14 | 15 | class EBPCEncoderMonitor: 16 | def __init__(self, dut, scoreboard, ta, tt): 17 | self.clk = dut.clk_i 18 | self.rst_n = dut.rst_ni 19 | self.last_i = dut.last_i 20 | self.vld_i = dut.vld_i 21 | self.rdy_o = dut.rdy_o 22 | self.idle_o = dut.idle_o 23 | self.scoreboard = scoreboard 24 | self.znz_data_o = dut.znz_data_o 25 | self.znz_vld_o = dut.znz_vld_o 26 | self.znz_rdy_i = dut.znz_rdy_i 27 | self.bpc_data_o = dut.bpc_data_o 28 | self.bpc_vld_o = dut.bpc_vld_o 29 | self.bpc_rdy_i = dut.bpc_rdy_i 30 | self.bpc_in_data = dut.data_to_bpc 31 | self.bpc_in_vld = dut.vld_to_bpc 32 | self.bpc_in_rdy = dut.rdy_from_bpc 33 | self.znz_in_data = dut.is_one_to_znz 34 | self.znz_in_vld = dut.vld_to_znz 35 | self.znz_in_rdy = dut.rdy_from_znz 36 | self.ta = ta 37 | self.tt = tt 38 | self.bpc_listening_task = None 39 | self.znz_listening_task = None 40 | self.bpc_in_listening_task = None 41 | self.znz_in_listening_task = None 42 | 43 | 44 | @cocotb.coroutine 45 | def clk_listener(self): 46 | while True: 47 | yield RisingEdge(self.clk) 48 | self.scoreboard.incr_clk_cnt() 49 | 50 | # this is just to detect stalls 51 | @cocotb.coroutine 52 | def in_listener(self): 53 | while True: 54 | yield RisingEdge(self.clk) 55 | yield Timer(self.tt) 56 | if self.vld_i.value and not self.rdy_o.value: 57 | self.scoreboard.incr_stall_cnt['in'] 58 | 59 | @cocotb.coroutine 60 | def bpc_in_listener(self): 61 | while True: 62 | yield RisingEdge(self.clk) 63 | yield Timer(self.tt) 64 | if self.bpc_in_vld.value and self.bpc_in_rdy.value: 65 | self.scoreboard.push_int_input('bpc', self.bpc_in_data.value) 66 | 67 | @cocotb.coroutine 68 | def znz_in_listener(self): 69 | while True: 70 | yield RisingEdge(self.clk) 71 | yield Timer(self.tt) 72 | if self.znz_in_vld.value and self.znz_in_rdy.value: 73 | self.scoreboard.push_int_input('znz', self.znz_in_data.value) 74 | 75 | @cocotb.coroutine 76 | def znz_listener(self): 77 | while True: 78 | yield RisingEdge(self.clk) 79 | yield Timer(self.tt) 80 | if self.znz_vld_o.value and self.znz_rdy_i.value: 81 | self.scoreboard.push_output('znz', self.znz_data_o.value) 82 | elif self.znz_vld_o.value and not self.znz_rdy_i.value: 83 | self.scoreboard.incr_stall_cnt('znz') 84 | 85 | @cocotb.coroutine 86 | def bpc_listener(self): 87 | while True: 88 | yield RisingEdge(self.clk) 89 | yield Timer(self.tt) 90 | if self.bpc_vld_o.value == 1 and self.bpc_rdy_i.value == 1: 91 | self.scoreboard.push_output('bpc', self.bpc_data_o.value) 92 | elif self.bpc_vld_o.value and not self.bpc_rdy_i.value: 93 | self.scoreboard.incr_stall_cnt('bpc') 94 | 95 | @cocotb.coroutine 96 | def wait_done(self): 97 | yield RisingEdge(self.idle_o) 98 | 99 | def start(self): 100 | self.bpc_listening_task = cocotb.fork(self.bpc_listener()) 101 | self.znz_listening_task = cocotb.fork(self.znz_listener()) 102 | self.bpc_in_listening_task = cocotb.fork(self.bpc_in_listener()) 103 | self.znz_in_listening_task = cocotb.fork(self.znz_in_listener()) 104 | self.clk_listening_task = cocotb.fork(self.clk_listener()) 105 | 106 | def stop(self): 107 | if self.bpc_listening_task is not None and self.znz_listening_task is not None and self.bpc_in_listening_task is not None and self.znz_in_listening_task is not None and self.clk_listening_task is not None: 108 | self.bpc_listening_task.kill() 109 | self.znz_listening_task.kill() 110 | self.clk_listening_task.kill() 111 | self.bpc_in_listening_task.kill() 112 | self.znz_in_listening_task.kill() 113 | else: 114 | assert(False), "Tried to stop non-running listening task!" 115 | 116 | -------------------------------------------------------------------------------- /tb/ebpc_decoder/ebpc_decoder_driver.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import random 13 | import cocotb 14 | from common.drivers import HandshakeInputDriver, HandshakeOutputDriver, wait_cycles 15 | 16 | class EBPCDecoderDriver: 17 | def __init__(self, dut, ta, tt): 18 | self.clk_i = dut.clk_i 19 | self.rst_ni = dut.rst_ni 20 | self.bpc_i = dut.bpc_i 21 | self.bpc_vld_i = dut.bpc_vld_i 22 | self.bpc_last_i = dut.bpc_last_i 23 | self.bpc_rdy_o = dut.bpc_rdy_o 24 | self.znz_i = dut.znz_i 25 | self.znz_vld_i = dut.znz_vld_i 26 | self.znz_last_i = dut.znz_last_i 27 | self.znz_rdy_o = dut.znz_rdy_o 28 | self.data_o = dut.data_o 29 | self.last_o = dut.last_o 30 | self.vld_o = dut.vld_o 31 | self.rdy_i = dut.rdy_i 32 | self.num_words_i = dut.num_words_i 33 | self.num_words_vld_i = dut.num_words_vld_i 34 | self.num_words_rdy_o = dut.num_words_rdy_o 35 | self.dut = dut 36 | self.ta = ta 37 | self.tt = tt 38 | self.bpc_i_drv = HandshakeInputDriver(clk=self.clk_i, signals=[self.bpc_i, self.bpc_last_i], vld=self.bpc_vld_i, 39 | rdy=self.bpc_rdy_o, ta=self.ta, tt=self.tt) 40 | self.num_words_drv = HandshakeInputDriver(clk=self.clk_i, signals=[self.num_words_i], vld=self.num_words_vld_i, 41 | rdy=self.num_words_rdy_o, ta=self.ta, tt=self.tt) 42 | self.znz_i_drv = HandshakeInputDriver(clk=self.clk_i, signals=[self.znz_i, self.znz_last_i], vld=self.znz_vld_i, rdy=self.znz_rdy_o, ta=self.ta, tt=self.tt) 43 | self.out_drv = HandshakeOutputDriver(clk=self.clk_i, vld=self.vld_o, rdy=self.rdy_i, ta=self.ta, tt=self.tt) 44 | 45 | def apply_defaults(self): 46 | self.bpc_i <= 0 47 | self.bpc_vld_i <= 0 48 | self.bpc_last_i <= 0 49 | self.znz_i <= 0 50 | self.znz_vld_i <= 0 51 | self.znz_last_i <= 0 52 | self.num_words_i <= 0 53 | self.num_words_vld_i <= 0 54 | self.rdy_i <= 0 55 | 56 | @cocotb.coroutine 57 | def drive_bpc(self, values, last, tmin, tmax): 58 | # values: list of values to feed 59 | # last: list of 'last' bits to feed 60 | # tmin: minimum # of clock cycles to wait between input applications 61 | # tmax: maximum # of clock cycles to wait between applications 62 | assert self.rst_ni.value == 1, "BPCDecoderDriver Error: rst_ni is not high in drive_bpc!" 63 | for inp in zip(values, last): 64 | yield wait_cycles(self.clk_i, random.randint(tmin,tmax)) 65 | yield self.bpc_i_drv.write_input(inp) 66 | 67 | @cocotb.coroutine 68 | def drive_znz(self, values, last, tmin, tmax): 69 | # values: list of values to feed 70 | # last: list of 'last' bits to feed 71 | # tmin: minimum # of clock cycles to wait between input applications 72 | # tmax: maximum # of clock cycles to wait between applications 73 | assert self.rst_ni.value == 1, "BPCDecoderDriver Error: rst_ni is not high in drive_znz!" 74 | for inp in zip(values, last): 75 | yield wait_cycles(self.clk_i, random.randint(tmin,tmax)) 76 | yield self.znz_i_drv.write_input(inp) 77 | 78 | @cocotb.coroutine 79 | def write_num_words(self, n, tmin, tmax): 80 | # values: list of values to feed 81 | # tmin: minimum # of clock cycles to wait between input applications 82 | # tmax: maximum # of clock cycles to wait between applications 83 | assert self.rst_ni.value == 1, "BPCDecoderDriver Error: rst_ni is not high in write_num_words!" 84 | yield wait_cycles(self.clk_i, random.randint(tmin,tmax)) 85 | yield self.num_words_drv.write_input(n) 86 | 87 | @cocotb.coroutine 88 | def read_outputs(self, num, tmin, tmax): 89 | #num: max number of output values to read 90 | #tmin: minimum # of clock cycles to wait between reads 91 | #tmax: maximum # of clock cycles to wait between reads 92 | assert self.rst_ni.value == 1, "BPCEncoderDriver Error: rst_ni is not high in read_outputs!" 93 | k = 0 94 | for i in range(num): 95 | yield wait_cycles(self.clk_i, random.randint(tmin, tmax)) 96 | yield self.out_drv.read_output() 97 | k+= 1 98 | if self.dut.last_o.value == 1: 99 | return k 100 | return k 101 | -------------------------------------------------------------------------------- /tb/ebpc_encoder/ebpc_encoder_tests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | import cocotb 13 | from cocotb.clock import Clock 14 | from cocotb.triggers import Timer 15 | from common.drivers import reset_dut, wait_cycles 16 | from ebpc_encoder_driver import EBPCEncoderDriver 17 | from ebpc_encoder_scoreboard import EBPCEncoderScoreboard 18 | from ebpc_encoder_monitor import EBPCEncoderMonitor 19 | import numpy as np 20 | import random 21 | import torchvision 22 | 23 | #import pydevd_pycharm 24 | #pydevd_pycharm.settrace('risa', port=9100, stdoutToServer=True, stderrToServer=True) 25 | 26 | random.seed(38) 27 | np.random.seed(79) 28 | 29 | CLOCK_PERIOD = 2500 30 | RESET_TIME = 15000 31 | DATA_W = 8 32 | BLOCK_SIZE = 8 33 | MAX_ZRLE_LEN = 16 34 | TA = 200 35 | TT = 2000 36 | # parameters for Fmap simulation 37 | # images need to be in a subfolder of the specified folder so the torchvision 38 | # image_folder dataset loader thinks it's in a category 39 | IMAGE_LOCATION = '/usr/scratch2/risa/georgr/imagenet/imgs' 40 | MODEL = 'vgg16' 41 | NUM_BATCHES = 1 42 | BATCHSIZE = 1 43 | # Feature maps are massive - use a only this fraction of all fmaps: 44 | FMAP_FRAC = 0.01 45 | 46 | @cocotb.test() 47 | def basic_bringup(dut): 48 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 49 | yield reset_dut(dut.rst_ni, RESET_TIME) 50 | yield Timer(10000) 51 | 52 | @cocotb.test() 53 | def random_inputs(dut): 54 | drv = EBPCEncoderDriver(dut, TA, TT) 55 | sb = EBPCEncoderScoreboard(dut, BLOCK_SIZE, DATA_W, MAX_ZRLE_LEN) 56 | mon = EBPCEncoderMonitor(dut, sb, TA, TT) 57 | inputs = sb.gen_rand_stimuli(10000, 1536, 0.25) 58 | drv.apply_defaults() 59 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 60 | yield reset_dut(dut.rst_ni, RESET_TIME) 61 | dut._log.info("Reset Done!") 62 | yield wait_cycles(dut.clk_i, 4) 63 | mon.start() 64 | bpc_read_task = cocotb.fork(drv.read_bpc_outputs(len(inputs)*2, tmin=0, tmax=0)) 65 | znz_read_task = cocotb.fork(drv.read_znz_outputs(len(inputs)*2, tmin=0, tmax=0)) 66 | cocotb.fork(drv.drive_input(inputs, tmin=0, tmax=0)) 67 | yield mon.wait_done() 68 | bpc_read_task.kill() 69 | znz_read_task.kill() 70 | mon.stop() 71 | yield wait_cycles(dut.clk_i, 4) 72 | if sb.report(): 73 | raise TestFailure("Scoreboard reported problems - check log!") 74 | 75 | 76 | @cocotb.test() 77 | def all_zeros(dut): 78 | drv = EBPCEncoderDriver(dut, TA, TT) 79 | sb = EBPCEncoderScoreboard(dut, BLOCK_SIZE, DATA_W, MAX_ZRLE_LEN) 80 | mon = EBPCEncoderMonitor(dut, sb, TA, TT) 81 | inputs = sb.gen_zero_stimuli(4000) 82 | drv.apply_defaults() 83 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 84 | yield reset_dut(dut.rst_ni, RESET_TIME) 85 | dut._log.info("Reset Done!") 86 | yield wait_cycles(dut.clk_i, 4) 87 | mon.start() 88 | bpc_read_task = cocotb.fork(drv.read_bpc_outputs(len(inputs)*2, tmin=0, tmax=0)) 89 | znz_read_task = cocotb.fork(drv.read_znz_outputs(len(inputs)*2, tmin=0, tmax=0)) 90 | cocotb.fork(drv.drive_input(inputs, tmin=0, tmax=0)) 91 | yield mon.wait_done() 92 | bpc_read_task.kill() 93 | znz_read_task.kill() 94 | mon.stop() 95 | yield wait_cycles(dut.clk_i, 4) 96 | if sb.report(): 97 | raise TestFailure("Scoreboard reported problems - check log!") 98 | 99 | @cocotb.test(skip=True) 100 | def fmap_inputs(dut): 101 | drv = EBPCEncoderDriver(dut, TA, TT) 102 | sb = EBPCEncoderScoreboard(dut, BLOCK_SIZE, DATA_W, MAX_ZRLE_LEN) 103 | mon = EBPCEncoderMonitor(dut, sb, TA, TT) 104 | inputs, bpc_len, znz_len = sb.gen_fmap_stimuli(model=MODEL, dataset_path=IMAGE_LOCATION, num_batches=NUM_BATCHES, batch_size=BATCHSIZE, fmap_frac=FMAP_FRAC) 105 | print('Compression Ratio: {}'.format((bpc_len+znz_len)/len(inputs))) 106 | drv.apply_defaults() 107 | cocotb.fork(Clock(dut.clk_i, CLOCK_PERIOD).start()) 108 | yield reset_dut(dut.rst_ni, RESET_TIME) 109 | dut._log.info("Reset Done!") 110 | yield wait_cycles(dut.clk_i, 4) 111 | mon.start() 112 | bpc_read_task = cocotb.fork(drv.read_bpc_outputs(len(inputs)*2, tmin=0, tmax=0)) 113 | znz_read_task = cocotb.fork(drv.read_znz_outputs(len(inputs)*2, tmin=0, tmax=0)) 114 | cocotb.fork(drv.drive_input(inputs, tmin=0, tmax=0)) 115 | yield mon.wait_done() 116 | bpc_read_task.kill() 117 | znz_read_task.kill() 118 | mon.stop() 119 | yield wait_cycles(dut.clk_i, 4) 120 | if sb.report(): 121 | raise TestFailure("Scoreboard reported problems - check log!") 122 | -------------------------------------------------------------------------------- /rtl_tb/hs_drivers.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | `timescale 1ps/1fs 12 | 13 | package hs_drv_pkg; 14 | 15 | class HandshakeDrv 16 | #( 17 | parameter int unsigned DATA_W = 8, 18 | parameter time TA = 0ns, 19 | parameter time TT = 0ns, 20 | parameter int unsigned MIN_WAIT = 0, 21 | parameter int unsigned MAX_WAIT = 2, 22 | parameter logic HAS_LAST = 1'b0, 23 | parameter string NAME = "" 24 | ); 25 | virtual HandshakeIf_t #(.DATA_W(DATA_W)) vif; 26 | 27 | function new(virtual HandshakeIf_t #(.DATA_W(DATA_W)) vif); 28 | this.vif = vif; 29 | endfunction // new 30 | 31 | task automatic feed_inputs(input string file); 32 | automatic int cycles_to_wait; 33 | automatic logic [DATA_W-1:0] dat; 34 | automatic int fh; 35 | automatic logic last; 36 | automatic logic signed [8:0] testchar; 37 | fh = $fopen(file, "r"); 38 | if (fh == 0) begin 39 | $display("fh: %d", fh); 40 | $info("File %s does not exist - aborting read_outputs task.", file); 41 | return; 42 | end 43 | testchar = $fgetc(fh); 44 | if (testchar < 1) begin 45 | $info("File %s is empty - aborting feed_inputs task.", file); 46 | return; 47 | end else 48 | $rewind(fh); 49 | while (!$feof(fh)) begin 50 | cycles_to_wait = $urandom_range(MIN_WAIT, MAX_WAIT); 51 | if (HAS_LAST) begin 52 | $fscanf(fh, "%b %b", dat, last); 53 | end else begin 54 | $fscanf(fh, "%b", dat); 55 | last = 1'b0; 56 | end 57 | write_output(cycles_to_wait, dat, last); 58 | end 59 | $fclose(fh); 60 | endtask // feed_inputs 61 | 62 | task automatic read_outputs(input string file); 63 | automatic int cycles_to_wait; 64 | automatic logic [DATA_W-1:0] dat_expected, dat_actual; 65 | automatic int fh; 66 | automatic logic last_expected, last_actual; 67 | automatic logic signed [8:0] testchar; 68 | fh = $fopen(file, "r"); 69 | if (fh == 0) begin 70 | $display("fh: %d", fh); 71 | $info("File %s does not exist - aborting read_outputs task.", file); 72 | return; 73 | end 74 | testchar = $fgetc(fh); 75 | if (testchar < 0) begin 76 | $info("File %s is empty - aborting read_outputs task.", file); 77 | return; 78 | end else 79 | $rewind(fh); 80 | while (!$feof(fh)) begin 81 | cycles_to_wait = $urandom_range(MIN_WAIT, MAX_WAIT); 82 | if (HAS_LAST) begin 83 | $fscanf(fh, "%b %b", dat_expected, last_expected); 84 | end else begin 85 | $fscanf(fh, "%b", dat_expected); 86 | last_expected = 1'b0; 87 | end 88 | read_input(cycles_to_wait, dat_actual, last_actual); 89 | if (dat_actual != dat_expected) 90 | $error("Output data mismatch on interface %s - expected: %b actual: %b", NAME, dat_expected, dat_actual); 91 | if ((last_expected != last_actual) && HAS_LAST) 92 | $error("Output last mismatch on interface %s - expected: %b actual %b", NAME, last_expected, last_actual); 93 | end 94 | $fclose(fh); 95 | endtask // read_outputs 96 | 97 | // to be called at clock edge 98 | task automatic write_output(input int wait_cycles, input logic [DATA_W-1:0] dat, input logic last_in); 99 | vif.vld <= #TA 1'b0; 100 | for (int i=0; i>shift_cnt_q); 59 | if (shift_i == DATA_W) // shift_i must never be larger than DATA_W 60 | st_d = full; 61 | else begin 62 | st_d = filling; 63 | shift_cnt_d = shift_cnt_q + shift_i; 64 | end 65 | end 66 | end // case: empty 67 | filling: begin 68 | rdy_o = 1'b1; 69 | if (vld_i) begin 70 | stream_reg_d = stream_reg_q | (data_i>>shift_cnt_q); 71 | shift_cnt_d = shift_cnt_q + shift_i; 72 | if (shift_cnt_d >= DATA_W) begin 73 | shift_cnt_d = shift_cnt_d - DATA_W; 74 | st_d = full; 75 | end 76 | end // if (vld_i) 77 | if (flush_i) 78 | st_d = flush; 79 | end // case: filling 80 | full: begin 81 | vld_o = 1'b1; 82 | // For this to work, the flush_i has to be applied until idle_o goes high (as it is done in ebpc_encoder)! 83 | last_o = flush_i && (shift_cnt_q == 'd0); 84 | if (rdy_i) begin 85 | rdy_o = 1'b1; 86 | if (vld_i) begin 87 | shift_cnt_d = shift_cnt_q + shift_i; 88 | stream_reg_d = {stream_reg_q[DATA_W-1:0], {DATA_W{1'b0}}} | (data_i>>shift_cnt_q); 89 | if (shift_cnt_d >= DATA_W) 90 | shift_cnt_d = shift_cnt_d - DATA_W; 91 | else 92 | st_d = filling; 93 | end else begin // if (vld_i) 94 | stream_reg_d = {stream_reg_q[DATA_W-1:0], {DATA_W{1'b0}}}; 95 | if (shift_cnt_q == 'd0) 96 | st_d = empty; 97 | else if (flush_i) 98 | st_d = flush; 99 | else 100 | st_d = filling; 101 | end // else: !if(vld_i) 102 | end // if (rdy_i) 103 | end // case: full 104 | flush: begin 105 | assert(shift_cnt_q >0) else $warning("Assertion failed in shift_streamer - shift_cnt is 0 in flush state!"); 106 | vld_o = 1'b1; 107 | last_o = 1'b1; 108 | if (rdy_i) begin 109 | stream_reg_d = {stream_reg_q[DATA_W-1:0], {DATA_W{1'b0}}}; 110 | if (shift_cnt_q > DATA_W) begin 111 | shift_cnt_d = shift_cnt_q - DATA_W; 112 | end else begin 113 | shift_cnt_d = 'd0; 114 | st_d = empty; 115 | end 116 | end 117 | end // case: flush 118 | endcase // case (st_q) 119 | end // block: fsm 120 | 121 | always @(posedge clk_i or negedge rst_ni) begin : sequential 122 | if (~rst_ni) begin 123 | st_q <= empty; 124 | stream_reg_q <= '0; 125 | shift_cnt_q <= '0; 126 | end else begin 127 | st_q <= st_d; 128 | stream_reg_q <= stream_reg_d; 129 | shift_cnt_q <= shift_cnt_d; 130 | end 131 | end 132 | 133 | endmodule // shift_streamer 134 | -------------------------------------------------------------------------------- /src/encoder/dbp_dbx_enc.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | import ebpc_pkg::*; 13 | module dbp_dbx_enc 14 | ( 15 | input logic clk_i, 16 | input logic rst_ni, 17 | //input data interface - one word at a time 18 | input logic signed [DATA_W-1:0] data_i, 19 | input logic flush_i, 20 | input logic vld_i, 21 | output logic rdy_o, 22 | //output data interface - (BLOCK_SIZE-1)*(DATA_W+1) DBPs plus the base word 23 | output dbp_block_t dbp_block_o, 24 | output logic vld_o, 25 | input logic rdy_i, 26 | output logic flush_o, 27 | output logic idle_o, 28 | output logic waiting_for_data_o 29 | ); 30 | 31 | logic signed [DATA_W:0] diffs_d [0:BLOCK_SIZE-1], diffs_q [0:BLOCK_SIZE-1]; 32 | logic signed [DATA_W-1:0] last_item_d, last_item_q; 33 | 34 | // fsm 35 | typedef enum {idle, fill, dbx_comp, wait_out} state_t; 36 | state_t state_d, state_q; 37 | logic [$clog2(BLOCK_SIZE)-1:0] fill_cnt_d, fill_cnt_q; 38 | logic shift; 39 | logic [0:DATA_W] [BLOCK_SIZE-2:0] dbp; 40 | 41 | always_comb begin : dbp_assignment 42 | for (int i=0; i= DATA_W && rdy_i) begin 73 | data_rdy_o = 1'b1; 74 | if (data_vld_i) begin 75 | push_o = 1'b1; 76 | state_d = dbx_decode; 77 | end 78 | end 79 | end 80 | dbx_decode : begin 81 | assert (rdy_i) else $warning("Assertion failed in symbol_decoder - rdy_i not high in dbx_decode state!", $time); 82 | if (unpacker_fill_state_i >= get_shift(expander_len)) begin 83 | data_rdy_o = 1'b1; 84 | if (data_vld_i) begin 85 | dbx_cnt_d = dbx_cnt_q + 1; 86 | push_o = 1'b1; 87 | if (expander_is_dbp) begin 88 | dbp_reg_d = expander_out; 89 | data_o = expander_out; 90 | end else 91 | dbp_reg_d = expander_out ^ dbp_reg_q; 92 | if (|expander_zeros) begin 93 | assert(dbx_cnt_q != DATA_W) else $warning("Assertion failed in symbol_decoder - dbx_cnt is DATA_W and expander_zeros !=0 in dbx_decode state!", $time); 94 | zero_cnt_d = expander_zeros - 1; 95 | state_d = zeros; 96 | data_rdy_o = 1'b0; 97 | end // else: !if(dbx_cnt_q == 0) 98 | if (dbx_cnt_q == DATA_W) begin 99 | dbp_reg_d = 'd0; 100 | state_d = idle; 101 | dbx_cnt_d = 'd0; 102 | vld_o = 1'b1; 103 | //state_d = last; 104 | end 105 | end // else: !if(dbx_cnt_q == 0) 106 | end // if (unpacker_fill_state_i >= expander_len) 107 | end // case: dbx_decode 108 | zeros : begin 109 | push_o = 1'b1; 110 | dbx_cnt_d = dbx_cnt_q + 1; 111 | assert (data_vld_i) else $warning("Assertion failed in symbol_decoder - data_vld_i not 1 in zeros state!", $time); 112 | assert (rdy_i) else $warning("Assertion failed in symbol_decoder - rdy_i not high in zeros state!", $time); 113 | if (zero_cnt_q == 'd0) begin 114 | data_rdy_o = 1'b1; 115 | if (dbx_cnt_q == DATA_W) begin 116 | //state_d = last; 117 | dbp_reg_d = 'd0; 118 | state_d = idle; 119 | dbx_cnt_d = 'd0; 120 | vld_o = 1'b1; 121 | end else 122 | state_d = dbx_decode; 123 | end else begin // case: zeros 124 | assert(dbx_cnt_q != DATA_W) else $warning("Assertion failed in symbol_decoder - dbx_cnt is DATA_W and zero_cnt !=0 ! in zeros state"); 125 | zero_cnt_d = zero_cnt_q-1; 126 | end // else: !if(zero_cnt_q == 'd0) 127 | end // case: zeros 128 | endcase // case (state_qp) 129 | // soft clear - reset state in next cycle 130 | if (clr_i) begin 131 | dbx_cnt_d = 'd0; 132 | zero_cnt_d = 'd0; 133 | dbp_reg_d = 'd0; 134 | state_d = idle; 135 | end 136 | end // block: fsm_in 137 | 138 | expander 139 | expander_i( 140 | .data_i(data_i), 141 | .zeros_o(expander_zeros), 142 | .len_o(expander_len), 143 | .dbx_dbp_o(expander_out), 144 | .is_dbp_o(expander_is_dbp) 145 | ); 146 | 147 | always_ff @(posedge clk_i or negedge rst_ni) begin 148 | if (~rst_ni) begin 149 | dbx_cnt_q <= 'd0; 150 | zero_cnt_q <= 'd0; 151 | dbp_reg_q <= 'd0; 152 | state_q <= idle; 153 | end else begin 154 | dbx_cnt_q <= dbx_cnt_d; 155 | zero_cnt_q <= zero_cnt_d; 156 | dbp_reg_q <= dbp_reg_d; 157 | state_q <= state_d; 158 | end 159 | end 160 | 161 | endmodule // symbol_decoder 162 | -------------------------------------------------------------------------------- /tb/bpc_decoder/wave.do: -------------------------------------------------------------------------------- 1 | onerror {resume} 2 | quietly WaveActivateNextPane {} 0 3 | add wave -noupdate /bpc_decoder/clk_i 4 | add wave -noupdate /bpc_decoder/rst_ni 5 | add wave -noupdate -group Top /bpc_decoder/bpc_i 6 | add wave -noupdate -group Top /bpc_decoder/bpc_vld_i 7 | add wave -noupdate -group Top /bpc_decoder/bpc_rdy_o 8 | add wave -noupdate -group Top -radix decimal /bpc_decoder/data_o 9 | add wave -noupdate -group Top /bpc_decoder/vld_o 10 | add wave -noupdate -group Top /bpc_decoder/rdy_i 11 | add wave -noupdate -group Top /bpc_decoder/clr_i 12 | add wave -noupdate -group Top /bpc_decoder/data_unpacker_to_decoder 13 | add wave -noupdate -group Top /bpc_decoder/fill_state_unpacker_to_decoder 14 | add wave -noupdate -group Top /bpc_decoder/len_decoder_to_unpacker 15 | add wave -noupdate -group Top /bpc_decoder/vld_unpacker_to_decoder 16 | add wave -noupdate -group Top /bpc_decoder/rdy_decoder_to_unpacker 17 | add wave -noupdate -group Top /bpc_decoder/data_decoder_to_buffer 18 | add wave -noupdate -group Top /bpc_decoder/vld_decoder_to_buffer 19 | add wave -noupdate -group Top /bpc_decoder/rdy_buffer_to_decoder 20 | add wave -noupdate -group Top /bpc_decoder/data_buffer_to_dr 21 | add wave -noupdate -group Top /bpc_decoder/vld_buffer_to_dr 22 | add wave -noupdate -group Top /bpc_decoder/rdy_dr_to_buffer 23 | add wave -noupdate -group unpacker -radix binary /bpc_decoder/unpacker_i/data_i 24 | add wave -noupdate -group unpacker /bpc_decoder/unpacker_i/vld_i 25 | add wave -noupdate -group unpacker /bpc_decoder/unpacker_i/rdy_o 26 | add wave -noupdate -group unpacker /bpc_decoder/unpacker_i/data_o 27 | add wave -noupdate -group unpacker -radix unsigned /bpc_decoder/unpacker_i/fill_state_o 28 | add wave -noupdate -group unpacker -radix unsigned /bpc_decoder/unpacker_i/len_i 29 | add wave -noupdate -group unpacker /bpc_decoder/unpacker_i/vld_o 30 | add wave -noupdate -group unpacker /bpc_decoder/unpacker_i/rdy_i 31 | add wave -noupdate -group unpacker -radix binary /bpc_decoder/unpacker_i/stream_reg_d 32 | add wave -noupdate -group unpacker -radix binary /bpc_decoder/unpacker_i/stream_reg_q 33 | add wave -noupdate -group unpacker -radix unsigned /bpc_decoder/unpacker_i/fill_state_d 34 | add wave -noupdate -group unpacker -radix unsigned /bpc_decoder/unpacker_i/fill_state_q 35 | add wave -noupdate -group unpacker /bpc_decoder/unpacker_i/state_d 36 | add wave -noupdate -group unpacker /bpc_decoder/unpacker_i/state_q 37 | add wave -noupdate -group decoder -radix binary /bpc_decoder/decoder_i/data_i 38 | add wave -noupdate -group decoder -radix unsigned /bpc_decoder/decoder_i/unpacker_fill_state_i 39 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/len_o 40 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/data_vld_i 41 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/data_rdy_o 42 | add wave -noupdate -group decoder -radix binary /bpc_decoder/decoder_i/data_o 43 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/push_o 44 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/vld_o 45 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/rdy_i 46 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/dbx_cnt_d 47 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/dbx_cnt_q 48 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/zero_cnt_d 49 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/zero_cnt_q 50 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/base 51 | add wave -noupdate -group decoder -radix binary /bpc_decoder/decoder_i/expander_out 52 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/expander_is_dbp 53 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/expander_zeros 54 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/expander_len 55 | add wave -noupdate -group decoder -radix binary /bpc_decoder/decoder_i/dbp_reg_d 56 | add wave -noupdate -group decoder -radix binary /bpc_decoder/decoder_i/dbp_reg_q 57 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/state_d 58 | add wave -noupdate -group decoder /bpc_decoder/decoder_i/state_q 59 | add wave -noupdate -group buffer -radix binary /bpc_decoder/buffer_i/data_i 60 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/push_i 61 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/vld_i 62 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/rdy_o 63 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/data_o 64 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/vld_o 65 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/rdy_i 66 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/state_d 67 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/state_q 68 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/shift_reg_d 69 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/shift_reg_q 70 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/base_d 71 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/base_q 72 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/vld_to_slice 73 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/rdy_from_slice 74 | add wave -noupdate -group buffer /bpc_decoder/buffer_i/dbp_block_to_fifo 75 | add wave -noupdate -group delta_reverse -expand /bpc_decoder/dr_i/data_i 76 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/vld_i 77 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/rdy_o 78 | add wave -noupdate -group delta_reverse -radix decimal /bpc_decoder/dr_i/data_o 79 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/vld_o 80 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/rdy_i 81 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/state_d 82 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/state_q 83 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/diffs 84 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/acc_reg_d 85 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/acc_reg_q 86 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/diff_idx_d 87 | add wave -noupdate -group delta_reverse /bpc_decoder/dr_i/diff_idx_q 88 | TreeUpdate [SetDefaultTree] 89 | WaveRestoreCursors {{Cursor 1} {156419 ps} 0} 90 | quietly wave cursor active 1 91 | configure wave -namecolwidth 150 92 | configure wave -valuecolwidth 211 93 | configure wave -justifyvalue left 94 | configure wave -signalnamewidth 1 95 | configure wave -snapdistance 10 96 | configure wave -datasetprefix 0 97 | configure wave -rowmargin 4 98 | configure wave -childrowmargin 2 99 | configure wave -gridoffset 0 100 | configure wave -gridperiod 1 101 | configure wave -griddelta 40 102 | configure wave -timeline 0 103 | configure wave -timelineunits ns 104 | update 105 | WaveRestoreZoom {148105 ps} {172675 ps} 106 | -------------------------------------------------------------------------------- /src/decoder/zrle_decoder.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | module zrle_decoder 13 | import ebpc_pkg::*; 14 | ( 15 | input logic clk_i, 16 | input logic rst_ni, 17 | input logic [DATA_W-1:0] znz_i, 18 | input logic vld_i, 19 | output logic rdy_o, 20 | output logic znz_o, 21 | output logic vld_o, 22 | input logic flush_i, 23 | input logic rdy_i 24 | ); 25 | 26 | logic [2*DATA_W-1:0] stream_reg_d, stream_reg_q; 27 | logic [LOG_DATA_W:0] fill_state_d, fill_state_q; 28 | logic [LOG_MAX_ZRLE_LEN-1:0] zero_cnt_d, zero_cnt_q; 29 | typedef enum {empty, filling, full, zeros} state_t; 30 | state_t state_d, state_q; 31 | 32 | always_comb begin : fsm 33 | stream_reg_d = stream_reg_q; 34 | fill_state_d = fill_state_q; 35 | zero_cnt_d = zero_cnt_q; 36 | state_d = state_q; 37 | vld_o = 1'b0; 38 | rdy_o = 1'b0; 39 | znz_o = stream_reg_q[2*DATA_W-1]; 40 | case (state_q) 41 | empty : begin 42 | rdy_o = 1'b1; 43 | if (vld_i) begin 44 | state_d = full; 45 | fill_state_d = DATA_W; 46 | stream_reg_d = {znz_i, {DATA_W{1'b0}}}; 47 | end 48 | end 49 | full : begin 50 | vld_o = 1'b1; 51 | assert(zero_cnt_q == 'd0) else $warning("Assertion failed in zrle_decoder: zero_cnt_q not 0 in full state"); 52 | if (rdy_i) begin 53 | if (~stream_reg_q[2*DATA_W-1]) begin 54 | stream_reg_d = (stream_reg_q << 1+LOG_MAX_ZRLE_LEN); 55 | fill_state_d = fill_state_q - 1-LOG_MAX_ZRLE_LEN; 56 | if (stream_reg_q[2*DATA_W-2:2*DATA_W-LOG_MAX_ZRLE_LEN-1] > 'd0) begin 57 | state_d = zeros; 58 | zero_cnt_d = stream_reg_q[2*DATA_W-2:2*DATA_W-LOG_MAX_ZRLE_LEN-1] - 1; 59 | end 60 | end else begin 61 | stream_reg_d = stream_reg_q << 1; 62 | fill_state_d = fill_state_q - 1; 63 | end // else: !if(~stream_reg_q[2*DATA_W-1]) 64 | if (fill_state_d < DATA_W) begin 65 | rdy_o = 1'b1; 66 | if (vld_i) begin 67 | stream_reg_d = stream_reg_d | ({znz_i, {DATA_W{1'b0}}} >> fill_state_d); 68 | fill_state_d = fill_state_d + DATA_W; 69 | // this is a bit ugly... 70 | end else if (state_d != zeros) 71 | state_d = filling; 72 | end 73 | if (flush_i) begin 74 | // assumption: the zrle decoder cannot start reading input words from the next transmission before 75 | // the current one has been flushed - this needs to be ensured externally! 76 | stream_reg_d = 'd0; 77 | state_d = empty; 78 | fill_state_d = 'd0; 79 | zero_cnt_d = 'd0; 80 | rdy_o = 1'b0; 81 | end 82 | end // if (rdy_i) 83 | end // case: full 84 | filling : begin 85 | rdy_o = 1'b1; 86 | assert(zero_cnt_q == 'd0) else $warning("Assertion failed in zrle_decoder: zero_cnt_q not 0 in full state"); 87 | if (vld_i) begin 88 | stream_reg_d = stream_reg_q | ({znz_i, {DATA_W{1'b0}}} >> fill_state_q); 89 | fill_state_d = fill_state_q + DATA_W; 90 | state_d = full; 91 | end 92 | if (stream_reg_q[2*DATA_W-1] || fill_state_q > LOG_MAX_ZRLE_LEN) begin 93 | vld_o = 1'b1; 94 | if (rdy_i) begin 95 | if (flush_i) begin 96 | assert (stream_reg_q[2*DATA_W-1] || (stream_reg_q[2*DATA_W-2:2*DATA_W-LOG_MAX_ZRLE_LEN-1]=='d0)) else $warning("Assertion failed in zrle_decoder: stream_reg_q's uppermost bit is zero but the runlength is not zero in state filling when flush is high!"); 97 | rdy_o = 1'b0; 98 | stream_reg_d = 'd0; 99 | state_d = empty; 100 | fill_state_d = 'd0; 101 | end else if (stream_reg_q[2*DATA_W-1]) begin 102 | stream_reg_d = stream_reg_d << 1; 103 | fill_state_d = fill_state_d-1; 104 | if (fill_state_d == 'd0) 105 | state_d = empty; 106 | end else begin 107 | stream_reg_d = stream_reg_d << (LOG_MAX_ZRLE_LEN + 1); 108 | fill_state_d = fill_state_d - LOG_MAX_ZRLE_LEN - 1; 109 | if (stream_reg_q[2*DATA_W-2:2*DATA_W-LOG_MAX_ZRLE_LEN-1] > 'd0) begin 110 | state_d = zeros; 111 | zero_cnt_d = stream_reg_q[2*DATA_W-2:2*DATA_W-LOG_MAX_ZRLE_LEN-1] - 1; 112 | end else if (fill_state_d == 'd0) 113 | state_d = empty; 114 | end 115 | end 116 | end 117 | end // case: filling 118 | zeros : begin 119 | znz_o = 1'b0; 120 | vld_o = 1'b1; 121 | if (rdy_i) begin 122 | if (flush_i) begin 123 | assert (zero_cnt_q == 'd0) else $warning("Assertion failed in zrle_decoder: zero_cnt_q not 0 in zeros state when flush is high - likely encoding problem!"); 124 | zero_cnt_d = 'd0; 125 | state_d = empty; 126 | stream_reg_d = 'd0; 127 | end else if (zero_cnt_q == 'd0) begin 128 | if (fill_state_q < DATA_W) 129 | state_d = filling; 130 | else if (fill_state_q == 'd0) 131 | state_d = empty; 132 | else 133 | state_d = full; 134 | end else 135 | zero_cnt_d = zero_cnt_q - 1; 136 | end // if (rdy_i) 137 | end // case: zeros 138 | endcase // case (state_q) 139 | end 140 | 141 | 142 | always_ff @(posedge clk_i or negedge rst_ni) begin : sequential 143 | if (~rst_ni) begin 144 | state_q <= empty; 145 | zero_cnt_q <= 'd0; 146 | stream_reg_q <= 'd0; 147 | fill_state_q <= 'd0; 148 | end else begin 149 | state_q <= state_d; 150 | zero_cnt_q <= zero_cnt_d; 151 | stream_reg_q <= stream_reg_d; 152 | fill_state_q <= fill_state_d; 153 | end // else: !if(~rst_ni) 154 | end 155 | endmodule // zrle_decoder 156 | 157 | -------------------------------------------------------------------------------- /src/decoder/ebpc_decoder.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | module ebpc_decoder 13 | import ebpc_pkg::*; 14 | ( 15 | input logic clk_i, 16 | input logic rst_ni, 17 | input logic [DATA_W-1:0] bpc_i, 18 | input logic bpc_last_i, 19 | input logic bpc_vld_i, 20 | output logic bpc_rdy_o, 21 | input logic [DATA_W-1:0] znz_i, 22 | input logic znz_last_i, 23 | input logic znz_vld_i, 24 | output logic znz_rdy_o, 25 | // num_words must be (#unencoded words - 1) 26 | input logic [LOG_MAX_WORDS-1:0] num_words_i, 27 | input logic num_words_vld_i, 28 | output logic num_words_rdy_o, 29 | output logic [DATA_W-1:0] data_o, 30 | output logic last_o, 31 | output logic vld_o, 32 | input logic rdy_i 33 | ); 34 | 35 | logic [LOG_MAX_WORDS-1:0] word_cnt_d, word_cnt_q; 36 | 37 | // after bpc_last_i/znz_last_i have been asserted, the respective decoder 38 | // input handshakes will be blocked to prevent corruption of subsequent streams 39 | logic vld_to_bpc, rdy_from_bpc; 40 | logic block_bpc_inp_d, block_bpc_inp_q; 41 | assign bpc_rdy_o = (block_bpc_inp_q) ? 1'b0 : rdy_from_bpc; 42 | assign vld_to_bpc = (block_bpc_inp_q) ? 1'b0 : bpc_vld_i; 43 | 44 | logic vld_to_zrld, rdy_from_zrld; 45 | 46 | logic znz; 47 | logic flush_to_zrld; 48 | logic vld_from_zrld, rdy_to_zrld; 49 | logic block_znz_inp_d, block_znz_inp_q; 50 | assign znz_rdy_o = (block_znz_inp_q) ? 1'b0 : rdy_from_zrld; 51 | assign vld_to_zrld = (block_znz_inp_q) ? 1'b0 : znz_vld_i; 52 | 53 | logic [DATA_W-1:0] data_from_bpc; 54 | logic clr_to_bpc; 55 | logic vld_from_bpc, rdy_to_bpc; 56 | 57 | typedef enum {idle, running, flush} state_t; 58 | state_t state_d, state_q; 59 | 60 | 61 | always_comb begin : fsm 62 | num_words_rdy_o = 1'b0; 63 | vld_o = 1'b0; 64 | last_o = 1'b0; 65 | word_cnt_d = word_cnt_q; 66 | state_d = state_q; 67 | flush_to_zrld = 1'b0; 68 | clr_to_bpc = 1'b0; 69 | data_o = 'd0; 70 | rdy_to_bpc = 1'b0; 71 | rdy_to_zrld = 1'b0; 72 | block_bpc_inp_d = block_bpc_inp_q; 73 | block_znz_inp_d = block_znz_inp_q; 74 | 75 | case (state_q) 76 | idle : begin 77 | assert (block_bpc_inp_q == 1'b0) else $warning("Assertion failed in ebpc_decoder - block_bpc_inp_q not low in idle state!"); 78 | assert (block_znz_inp_q == 1'b0) else $warning("Assertion failed in ebpc_decoder - block_znz_inp_q not low in idle state!"); 79 | num_words_rdy_o = 1'b1; 80 | if (num_words_vld_i) begin 81 | block_bpc_inp_d = 1'b0; 82 | block_znz_inp_d = 1'b0; 83 | word_cnt_d = num_words_i; 84 | state_d = running; 85 | end 86 | end 87 | running : begin 88 | if (bpc_vld_i & bpc_last_i & bpc_rdy_o) 89 | block_bpc_inp_d = 1'b1; 90 | if (znz_vld_i & znz_last_i & znz_rdy_o) 91 | block_znz_inp_d = 1'b1; 92 | if (vld_from_zrld) begin 93 | if (word_cnt_q == 'd0) 94 | last_o = 1'b1; 95 | if (znz) begin 96 | vld_o = vld_from_bpc; 97 | data_o = data_from_bpc; 98 | rdy_to_zrld = vld_from_bpc && rdy_i; 99 | rdy_to_bpc = vld_from_bpc && rdy_i; 100 | if (vld_from_bpc && rdy_i) begin 101 | if (word_cnt_q == 'd0) begin 102 | flush_to_zrld = 1'b1; 103 | clr_to_bpc = 1'b1; 104 | state_d = idle; 105 | block_znz_inp_d = 1'b0; 106 | block_bpc_inp_d = 1'b0; 107 | end else 108 | word_cnt_d = word_cnt_q-1; 109 | end 110 | end else begin 111 | vld_o = 1'b1; 112 | rdy_to_zrld = rdy_i; 113 | data_o = 'd0; 114 | if (rdy_i) begin 115 | if (word_cnt_q == 'd0) begin 116 | flush_to_zrld = 1'b1; 117 | clr_to_bpc = 1'b1; 118 | state_d = idle; 119 | block_znz_inp_d = 1'b0; 120 | block_bpc_inp_d = 1'b0; 121 | end else 122 | word_cnt_d = word_cnt_q - 1; 123 | end 124 | end // else: !if(znz) 125 | end // if (vld_from_zrle) 126 | end // case: running 127 | endcase 128 | end // block: fsm 129 | 130 | always_ff @(posedge clk_i or negedge rst_ni) begin : sequential 131 | if (~rst_ni) begin 132 | state_q <= idle; 133 | word_cnt_q <= 'd0; 134 | block_znz_inp_q <= 1'b0; 135 | block_bpc_inp_q <= 1'b0; 136 | end else begin 137 | state_q <= state_d; 138 | word_cnt_q <= word_cnt_d; 139 | block_znz_inp_q <= block_znz_inp_d; 140 | block_bpc_inp_q <= block_bpc_inp_d; 141 | end 142 | end 143 | 144 | zrle_decoder 145 | zrld_i ( 146 | .clk_i(clk_i), 147 | .rst_ni(rst_ni), 148 | .znz_i(znz_i), 149 | .vld_i(vld_to_zrld), 150 | .rdy_o(rdy_from_zrld), 151 | .znz_o(znz), 152 | .vld_o(vld_from_zrld), 153 | .rdy_i(rdy_to_zrld), 154 | .flush_i(flush_to_zrld) 155 | ); 156 | 157 | bpc_decoder 158 | bpcd_i ( 159 | .clk_i(clk_i), 160 | .rst_ni(rst_ni), 161 | .bpc_i(bpc_i), 162 | .bpc_vld_i(vld_to_bpc), 163 | .bpc_rdy_o(rdy_from_bpc), 164 | .data_o(data_from_bpc), 165 | .vld_o(vld_from_bpc), 166 | .rdy_i(rdy_to_bpc), 167 | .clr_i(clr_to_bpc) 168 | ); 169 | 170 | endmodule // ebpc_decoder 171 | -------------------------------------------------------------------------------- /tb/ebpc_encoder/ebpc_encoder_scoreboard.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | 12 | from common import bpc 13 | from common.util import * 14 | from common import data 15 | import torch 16 | import numpy as np 17 | import random 18 | import math 19 | 20 | class EBPCEncoderScoreboard: 21 | 22 | def __init__(self, dut, block_size, data_w, max_zrle_len): 23 | self.dut = dut 24 | self.block_size = block_size 25 | self.data_w = data_w 26 | self.inputs = {'data': [], 'last': []} 27 | self.outputs_exp = {'znz': [], 'bpc': []} 28 | self.outputs_act = {'znz': [], 'bpc': []} 29 | self.mismatches = {'znz': 0, 'bpc': 0, 'znz_in': 0, 'bpc_in': 0} 30 | self.internal_inputs_exp = {'znz': [], 'bpc': []} 31 | self.internal_inputs_act = {'znz': [], 'bpc': []} 32 | self.curr_internal_input = {'znz': 0, 'bpc': 0} 33 | self.clk_cnt = 0 34 | self.stall_cnt = {'in': 0, 'bpc': 0, 'znz': 0} 35 | self.max_zrle_len = max_zrle_len 36 | self.curr_output = {'bpc': 0, 'znz': 0} 37 | 38 | 39 | def gen_zero_stimuli(self, n_inputs): 40 | data_in = [0] * n_inputs 41 | last_in = ([0]*(len(data_in)-1)+[1]) 42 | self.internal_inputs_exp['znz'] += int_list_to_binval_list(data_in, n_bits=1, signed=False) 43 | self.outputs_exp['bpc'] = [] 44 | self.outputs_exp['znz'] = bpc.ZRLE_words(data_in, max_burst_len=self.max_zrle_len, wordwidth=self.data_w) 45 | self.inputs['data'] = int_list_to_binval_list(data_in, n_bits=self.data_w, signed=True) 46 | self.inputs['last'] = int_list_to_binval_list(last_in, n_bits=1, signed=False) 47 | return list(zip(*self.inputs.values())) 48 | 49 | 50 | def gen_rand_stimuli(self, n_inputs, max_zero_block, p_zero): 51 | # generate random stimuli and return them as a list of (data_i, last_i) tuples 52 | data_in = [] 53 | last_in = [] 54 | znz_stream = [] 55 | nz_data = [] 56 | i = 0 57 | while i < n_inputs: 58 | if (np.random.binomial(1, p_zero)): 59 | zero_len = min(random.randint(1, max_zero_block), n_inputs-i) 60 | znz_stream += [0] * zero_len 61 | i += zero_len 62 | data_in += [0]*zero_len 63 | else: 64 | znz_stream.append(1) 65 | dat = get_random_nonzero_in_range(-2**(self.data_w-1), 2**(self.data_w-1)-1) 66 | nz_data.append(dat) 67 | data_in.append(dat) 68 | i += 1 69 | last_in += ([0]*(len(data_in)-1)+[1]) 70 | nz_data = zero_pad_list(nz_data, self.block_size) 71 | self.internal_inputs_exp['bpc'] += int_list_to_binval_list(nz_data, n_bits=self.data_w, signed=True) 72 | self.internal_inputs_exp['znz'] += int_list_to_binval_list(znz_stream, n_bits=1, signed=False) 73 | self.outputs_exp['bpc'] = bpc.BPC_words(np.array(nz_data), self.block_size, variant='paper', word_w=self.data_w) 74 | self.outputs_exp['znz'] = bpc.ZRLE_words(data_in, max_burst_len=self.max_zrle_len, wordwidth=self.data_w) 75 | self.inputs['data'] = int_list_to_binval_list(data_in, n_bits=self.data_w, signed=True) 76 | self.inputs['last'] = int_list_to_binval_list(last_in, n_bits=1, signed=False) 77 | 78 | # very pythonic. 79 | return list(zip(*self.inputs.values())) 80 | 81 | def gen_fmap_stimuli(self, model, dataset_path, num_batches, batch_size, signed=True, fmap_frac=0.01): 82 | assert self.data_w in [8, 16, 32] 83 | fms_q = data.getStimuli(model=model, dataset_path=dataset_path, data_w=self.data_w, num_batches=num_batches, batch_size=batch_size, signed=signed, fmap_frac=fmap_frac) 84 | nz_data = [el for el in fms_q if el != 0] 85 | nz_data = zero_pad_list(nz_data, self.block_size) 86 | znz_stream = [0 if el==0 else 1 for el in fms_q] 87 | last_in = ([0]*(len(fms_q)-1)+[1]) 88 | self.inputs['data'] = int_list_to_binval_list(fms_q, self.data_w, signed=signed) 89 | self.inputs['last'] = int_list_to_binval_list(last_in, n_bits=1, signed=False) 90 | self.internal_inputs_exp['bpc'] += int_list_to_binval_list(nz_data, n_bits=self.data_w, signed=True) 91 | self.internal_inputs_exp['znz'] += int_list_to_binval_list(znz_stream, n_bits=1, signed=False) 92 | self.outputs_exp['bpc'] = bpc.BPC_words(np.array(nz_data),self.block_size,variant='paper',word_w=self.data_w) 93 | self.outputs_exp['znz'] = bpc.ZRLE_words(fms_q, max_burst_len=self.max_zrle_len, wordwidth=self.data_w) 94 | return list(zip(*self.inputs.values())), len(self.outputs_exp['bpc']), len(self.outputs_exp['znz']) 95 | 96 | def incr_clk_cnt(self): 97 | self.clk_cnt += 1; 98 | 99 | def incr_stall_cnt(self, key): 100 | self.stall_cnt[key] += 1 101 | 102 | def push_int_input(self, key, in_act): 103 | self.internal_inputs_act[key].append(in_act) 104 | if in_act.binstr != self.internal_inputs_exp[key][self.curr_internal_input[key]].binstr: 105 | self.mismatches[key+'_in'] += 1 106 | self.dut._log.warning("{} input mismatch! Expected: {} Got: {}".format(key, self.internal_inputs_exp[key][self.curr_internal_input[key]], in_act.binstr)) 107 | self.curr_internal_input[key] += 1 108 | 109 | def push_output(self, key, out_act): 110 | self.outputs_act[key].append(out_act.binstr) 111 | if out_act.binstr != self.outputs_exp[key][self.curr_output[key]]: 112 | self.mismatches[key] += 1 113 | self.dut._log.warning("{} mismatch! Expected: {} Got: {}".format(key, self.outputs_exp[key][self.curr_output[key]], out_act.binstr)) 114 | self.curr_output[key] += 1 115 | 116 | def report(self): 117 | self.dut._log.info("Scoreboard: Fed {} inputs in {} cycles - input was stalled for {} cycles, bpc output was stalled for {} cycles and znz output was stalled for {} cycles. Throughput: {} words/cycle".format(len(self.inputs['last']), self.clk_cnt, self.stall_cnt['in'], self.stall_cnt['bpc'], self.stall_cnt['znz'], len(self.inputs['data'])/self.clk_cnt)) 118 | for k in ['znz', 'bpc']: 119 | self.dut._log.info("Scoreboard: Read {} {} outputs with {} mismatches - {} such outputs were expected".format(len(self.outputs_act[k]), k, self.mismatches[k], len(self.outputs_exp[k]))) 120 | 121 | r = any(self.mismatches.values()) 122 | return r 123 | -------------------------------------------------------------------------------- /src/encoder/zrle.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | import ebpc_pkg::*; 13 | module zrle 14 | ( 15 | input logic clk_i, 16 | input logic rst_ni, 17 | input logic is_one_i, 18 | input logic flush_i, 19 | input logic vld_i, 20 | output logic rdy_o, 21 | output logic [DATA_W-1:0] data_o, 22 | output logic last_o, 23 | output logic vld_o, 24 | input logic rdy_i, 25 | output logic idle_o 26 | ); 27 | 28 | 29 | typedef enum {empty, filling, full, flush, flush_zeros} state_t; 30 | state_t state_d, state_q; 31 | logic [LOG_MAX_ZRLE_LEN-1:0] zero_cnt_d, zero_cnt_q; 32 | logic [2*DATA_W-1:0] stream_reg_d, stream_reg_q; 33 | logic [$clog2(DATA_W):0] shift_cnt_d, shift_cnt_q; 34 | assign data_o = stream_reg_q[2*DATA_W-1:DATA_W]; 35 | 36 | always_comb begin : fsm 37 | state_d = state_q; 38 | zero_cnt_d = zero_cnt_q; 39 | stream_reg_d = stream_reg_q; 40 | shift_cnt_d = shift_cnt_q; 41 | rdy_o = 1'b0; 42 | vld_o = 1'b0; 43 | idle_o = 1'b0; 44 | last_o = 1'b0; 45 | 46 | case (state_q) 47 | empty : begin 48 | assert (stream_reg_q == 'd0) else $warning("Assertion failed in zrle: stream_reg not empty in empty state"); 49 | assert (shift_cnt_q == 'd0) else $warning("Assertion failed in zrle: shift_cnt not 0 in empty state"); 50 | assert (zero_cnt_q == 'd0) else $warning("Assertion failed in zrle: zero_cnt not 0 in empty state"); 51 | rdy_o = 1'b1; 52 | idle_o = 1'b1; 53 | if (vld_i) begin 54 | idle_o = 1'b0; 55 | state_d = filling; 56 | if (is_one_i) begin 57 | shift_cnt_d = shift_cnt_q + 1; 58 | stream_reg_d = stream_reg_q | ({1'b1, {2*DATA_W-1{1'b0}}} >> shift_cnt_q); 59 | end else begin 60 | zero_cnt_d = zero_cnt_q + 1; 61 | end 62 | if (flush_i) begin 63 | if (is_one_i) 64 | state_d = flush; 65 | else 66 | state_d = flush_zeros; 67 | end 68 | end 69 | end // case: empty 70 | filling : begin 71 | rdy_o = 1'b1; 72 | if (vld_i) begin 73 | if (is_one_i) begin 74 | zero_cnt_d = 'd0; 75 | if (zero_cnt_q != 'd0) begin 76 | stream_reg_d = stream_reg_q 77 | | ({1'b0, LOG_MAX_ZRLE_LEN'(zero_cnt_q-1), 1'b1, {2*DATA_W-LOG_MAX_ZRLE_LEN-2{1'b0}}} 78 | >> shift_cnt_q); 79 | shift_cnt_d = shift_cnt_q + 1 + 1 + LOG_MAX_ZRLE_LEN; 80 | end else begin 81 | shift_cnt_d = shift_cnt_q + 1; 82 | stream_reg_d = stream_reg_q | ({1'b1, {2*DATA_W-1{1'b0}}} >> shift_cnt_q); 83 | end 84 | end else if (zero_cnt_q == MAX_ZRLE_LEN-1) begin 85 | stream_reg_d = stream_reg_q | 86 | ({1'b0, LOG_MAX_ZRLE_LEN'(MAX_ZRLE_LEN-1), {2*DATA_W-LOG_MAX_ZRLE_LEN-1{1'b0}}} 87 | >> shift_cnt_q); 88 | shift_cnt_d = shift_cnt_q + 1 + LOG_MAX_ZRLE_LEN; 89 | zero_cnt_d = 'd0; 90 | end else 91 | zero_cnt_d = zero_cnt_q + 1; 92 | if (flush_i) begin 93 | if (zero_cnt_d != 'd0) 94 | state_d = flush_zeros; 95 | else 96 | state_d = flush; 97 | end else if (shift_cnt_d >= DATA_W) begin 98 | state_d = full; 99 | shift_cnt_d = shift_cnt_d - DATA_W; 100 | end 101 | end // if (vld_i) 102 | end // case: filling 103 | flush_zeros : begin 104 | assert (zero_cnt_q != 0) else $warning("Assertion failed in ZRLE: zero_cnt == 0 in flush_zeros!"); 105 | zero_cnt_d = 'd0; 106 | stream_reg_d = stream_reg_q 107 | | ({1'b0, LOG_MAX_ZRLE_LEN'(zero_cnt_q-1), {2*DATA_W-LOG_MAX_ZRLE_LEN-1{1'b0}}} 108 | >> shift_cnt_q); 109 | shift_cnt_d = shift_cnt_q + LOG_MAX_ZRLE_LEN + 1; 110 | state_d = flush; 111 | end 112 | full : begin 113 | assert (shift_cnt_q < DATA_W) else $warning("Assertion failed in zrle: shift_cnt >= DATA_W in full state"); 114 | assert (zero_cnt_q == 0) else $warning("Assertion failed in zrle: zero_cnt is not 0 in full state (should it really?)"); 115 | vld_o = 1'b1; 116 | if (rdy_i) begin 117 | rdy_o = 1'b1; 118 | stream_reg_d = {stream_reg_q[DATA_W-1:0], {DATA_W{1'b0}}}; 119 | state_d = filling; 120 | if (vld_i) begin 121 | if (is_one_i) begin 122 | shift_cnt_d = shift_cnt_q+1; 123 | stream_reg_d = stream_reg_d | ({1'b1, {2*DATA_W-1{1'b0}}} >> shift_cnt_q); 124 | end else if (zero_cnt_q == MAX_ZRLE_LEN - 1) begin 125 | shift_cnt_d = shift_cnt_q + 1 + LOG_MAX_ZRLE_LEN; 126 | stream_reg_d = stream_reg_d | 127 | ({1'b0, LOG_MAX_ZRLE_LEN'(MAX_ZRLE_LEN-1), {2*DATA_W-LOG_MAX_ZRLE_LEN-1{1'b0}}} 128 | >> shift_cnt_q); 129 | end else 130 | zero_cnt_d = zero_cnt_q + 1; 131 | if (flush_i) begin 132 | if (zero_cnt_d != 'd0) 133 | state_d = flush_zeros; 134 | else 135 | state_d = flush; 136 | end else if (shift_cnt_d >= DATA_W) begin 137 | state_d = full; 138 | shift_cnt_d = shift_cnt_d - DATA_W; 139 | end 140 | end else begin // if (vld_i) 141 | if (shift_cnt_q == 'd0) 142 | state_d = empty; 143 | else 144 | state_d = filling; 145 | end // else: !if(vld_i) 146 | end 147 | end // case: full 148 | flush : begin 149 | vld_o = 1'b1; 150 | if (shift_cnt_q <= DATA_W) 151 | last_o = 1'b1; 152 | if (rdy_i) begin 153 | stream_reg_d = {stream_reg_q[DATA_W-1:0], {DATA_W{1'b0}}}; 154 | if (shift_cnt_q > DATA_W) 155 | shift_cnt_d = shift_cnt_q-DATA_W; 156 | else begin 157 | shift_cnt_d = 'd0; 158 | state_d = empty; 159 | end 160 | end 161 | end // case: flush 162 | endcase // case (state_q) 163 | end 164 | 165 | always @(posedge clk_i or negedge rst_ni) begin 166 | if (~rst_ni) begin 167 | state_q <= empty; 168 | zero_cnt_q <= 'd0; 169 | stream_reg_q <= 'd0; 170 | shift_cnt_q <= 'd0; 171 | //last_was_zero_q <= 1'b0; 172 | end else begin 173 | state_q <= state_d; 174 | zero_cnt_q <= zero_cnt_d; 175 | stream_reg_q <= stream_reg_d; 176 | shift_cnt_q <= shift_cnt_d; 177 | //last_was_zero_q <= last_was_zero_d; 178 | end 179 | end 180 | 181 | endmodule // zrle 182 | -------------------------------------------------------------------------------- /src/encoder/seq_coder.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ETH Zurich, Lukas Cavigelli and Georg Rutishauser 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | 12 | import ebpc_pkg::*; 13 | 14 | module seq_coder 15 | ( 16 | input logic clk_i, 17 | input logic rst_ni, 18 | input dbp_block_t dbp_block_i, 19 | input logic flush_i, 20 | input logic vld_i, 21 | output logic rdy_o, 22 | // data out interface 23 | output logic [DATA_W-1:0] data_o, 24 | output logic last_o, 25 | output logic vld_o, 26 | input logic rdy_i, 27 | output logic idle_o, 28 | output logic waiting_for_data_o 29 | ); 30 | 31 | 32 | function logic[$clog2(BLOCK_SIZE+1)-1:0] get_shift(symb_len_t s); 33 | if (s == FIVE) 34 | return 5; 35 | else if (s == FIVE_PLUS_LOGN) 36 | return 5 + $clog2(BLOCK_SIZE-1); 37 | else 38 | return BLOCK_SIZE; 39 | endfunction // 40 | 41 | //dbp/dbx output from FIFO slice 42 | dbp_block_t dbp_block_from_fifo; 43 | 44 | //dbx encoder result 45 | encoding_t code_symb; 46 | 47 | //fsm 48 | typedef enum {idle, fill, flush_st} state_t; 49 | state_t state_d, state_q; 50 | // counter to keep track on which dbx we're working 51 | logic [$clog2(DATA_W+1)-1:0] dbx_cnt_d, dbx_cnt_q; 52 | // keep track of running zeros 53 | logic [$clog2(DATA_W+1)-1:0] zero_cnt_d, zero_cnt_q; 54 | logic vld_from_slice, rdy_to_slice; 55 | logic last_was_zero_d, last_was_zero_q; 56 | logic flush; 57 | logic [DATA_W-1:0] data; 58 | logic [$clog2(DATA_W+1)-1:0] shift; 59 | logic shift_vld, shift_rdy; 60 | logic streamer_idle; 61 | 62 | initial begin 63 | assert (!(BLOCK_SIZE > DATA_W)) else $error("Error: BLOCK_SIZE can't be larger than DATA_W in seq_coder"); 64 | end 65 | 66 | assign waiting_for_data_o = !vld_from_slice; 67 | 68 | always_comb begin : fsm 69 | automatic logic [$clog2(DATA_W):0] zeros; 70 | automatic logic write_zero = 1'b0, stall = 1'b0; 71 | zeros = zero_cnt_q; 72 | state_d = state_q; 73 | dbx_cnt_d = dbx_cnt_q; 74 | zero_cnt_d = zero_cnt_q; 75 | last_was_zero_d = last_was_zero_q; 76 | flush = 1'b0; 77 | rdy_to_slice = 1'b0; 78 | data = 'd0; 79 | shift_vld = 1'b0; 80 | shift = 'd0; 81 | stall = 1'b0; 82 | idle_o = 1'b0; 83 | 84 | case (state_q) 85 | idle: begin 86 | assert (zero_cnt_q == 0) else $warning("Assertion failed in seq_coder: zero_cnt_q not 0 in state idle"); 87 | assert (dbx_cnt_q == DATA_W) else $warning("Assertion failed in seq_coder: dbx_cnt_q not DATA_W in state idle"); 88 | zero_cnt_d = 'd0; 89 | dbx_cnt_d = DATA_W; 90 | idle_o = streamer_idle; 91 | flush = flush_i; 92 | if (vld_from_slice) begin 93 | idle_o = 1'b0; 94 | flush = 1'b0; 95 | shift = DATA_W; 96 | data = dbp_block_from_fifo.base; 97 | shift_vld = 1'b1; 98 | if (shift_rdy) 99 | state_d = fill; 100 | end 101 | end 102 | fill: begin 103 | assert (vld_from_slice == 1'b1) else $warning("Assertion failed in seq_coder: vld_from_slice is not high in state 'fill'!"); 104 | //if (code_symb.len == N) 105 | shift_vld = 1'b1; 106 | if (code_symb.zero) begin 107 | if (dbx_cnt_q != 'd0) begin 108 | zero_cnt_d = zero_cnt_q+1; 109 | dbx_cnt_d = dbx_cnt_q-1; 110 | last_was_zero_d = 1'b1; 111 | shift_vld = 1'b0; 112 | end else begin 113 | zeros += 1; 114 | write_zero = 1'b1; 115 | end 116 | end else begin // if (code_symb.zero) 117 | if (last_was_zero_q) begin// if (code_symb.zero) 118 | write_zero = 1'b1; 119 | stall = 1'b1; 120 | end else begin 121 | shift = get_shift(code_symb.len); 122 | data = {code_symb.symb, {(DATA_W-MAX_SYMB_LEN){1'b0}}}; 123 | end 124 | end // else: !if(code_symb.zero) 125 | if (write_zero) begin 126 | if (zeros == 'd1) begin 127 | shift = 2; 128 | data = {2'b01, {DATA_W-2{1'b0}}}; 129 | end else begin 130 | shift = 3+$clog2(DATA_W); 131 | data = {3'b001, $clog2(DATA_W)'(zeros-2), {DATA_W-3-$clog2(DATA_W){1'b0}}}; 132 | end 133 | end 134 | if (shift_rdy) begin 135 | if (write_zero) begin 136 | last_was_zero_d = 1'b0; 137 | zero_cnt_d = 'd0; 138 | end // if (write_zero) 139 | if (~stall) begin 140 | if (dbx_cnt_q != 'd0) begin 141 | dbx_cnt_d = dbx_cnt_q-1; 142 | end else begin 143 | rdy_to_slice = 1'b1; 144 | state_d = idle; 145 | dbx_cnt_d = DATA_W; 146 | end 147 | end // if (~stall) 148 | end // if (shift_rdy) 149 | end // case: fill 150 | default: 151 | state_d = idle; 152 | endcase // case (state_q) 153 | end // block: fsm 154 | 155 | 156 | 157 | always @(posedge clk_i or negedge rst_ni) begin : sequential 158 | if (~rst_ni) begin 159 | state_q <= idle; 160 | dbx_cnt_q <= DATA_W; 161 | zero_cnt_q <= 'd0; 162 | last_was_zero_q <= 1'b0; 163 | end else begin 164 | state_q <= state_d; 165 | dbx_cnt_q <= dbx_cnt_d; 166 | zero_cnt_q <= zero_cnt_d; 167 | last_was_zero_q <= last_was_zero_d; 168 | end // else: !if(~rst_ni) 169 | end // block: sequential 170 | 171 | 172 | fifo_slice #( 173 | .t(dbp_block_t) 174 | ) 175 | slice_i ( 176 | .clk_i(clk_i), 177 | .rst_ni(rst_ni), 178 | .din_i(dbp_block_i), 179 | .vld_i(vld_i), 180 | .rdy_o(rdy_o), 181 | .dout_o(dbp_block_from_fifo), 182 | .vld_o(vld_from_slice), 183 | .rdy_i(rdy_to_slice) 184 | ); 185 | 186 | 187 | 188 | dbx_compressor 189 | compressor_i ( 190 | .dbp(dbp_block_from_fifo.dbp), 191 | .dbp_cnt(dbx_cnt_q), 192 | .code_symb(code_symb) 193 | ); 194 | 195 | shift_streamer 196 | streamer_i ( 197 | .clk_i(clk_i), 198 | .rst_ni(rst_ni), 199 | .data_i({data,{DATA_W{1'b0}}}), 200 | .shift_i(shift), 201 | .flush_i(flush), 202 | .vld_i(shift_vld), 203 | .rdy_o(shift_rdy), 204 | .data_o(data_o), 205 | .last_o(last_o), 206 | .vld_o(vld_o), 207 | .rdy_i(rdy_i), 208 | .idle_o(streamer_idle) 209 | ); 210 | 211 | endmodule : seq_coder 212 | -------------------------------------------------------------------------------- /py/common/data.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 ETH Zurich, Lukas Cavigelli, Georg Rutishauser, Luca Benini 2 | # This file, licensed under Apache 2.0 was adapted from 3 | # https://github.com/lukasc-ch/ExtendedBitPlaneCompression, 4 | # commit 0b175d0d35937b3f2724fe59b0c6cc82802556f6 5 | 6 | import torch 7 | import torchvision as tv 8 | import numpy as np 9 | import random 10 | import math 11 | from tqdm import tqdm 12 | import common.bpc as bpc 13 | from common.util import zero_pad_list, write_sim_file, split_str 14 | 15 | 16 | import os 17 | import glob 18 | import csv 19 | 20 | import sys 21 | sys.path.append('./quantLab') 22 | 23 | def getModel(modelName, epoch=None): 24 | # import torchvision as tv 25 | # import quantlab.ImageNet.topology as topo 26 | 27 | # loss_func = torch.nn.CrossEntropyLoss() 28 | model = None 29 | 30 | if modelName == 'alexnet': 31 | model = tv.models.alexnet(pretrained=True) 32 | elif modelName == 'squeezenet': 33 | model = tv.models.squeezenet1_1(pretrained=True) 34 | elif modelName == 'resnet34': 35 | model = tv.models.resnet34(pretrained=True) 36 | elif modelName == 'vgg16': 37 | model = tv.models.vgg16_bn(pretrained=True) 38 | elif modelName == 'mobilenet2': 39 | model = tv.models.mobilenet_v2(pretrained=True) 40 | 41 | device = torch.cuda.current_device() if torch.cuda.is_available() else torch.device('cpu') 42 | model = model.to(device) 43 | assert(model != None) 44 | return model, device 45 | 46 | 47 | def getFMs(model, device, datasetPath, numBatches=1, batchSize=10, safetyFactor=1.0, frac=0.01, dataLoader=None): 48 | #dataLoader can be passed to allow feature map collection in multiple 49 | #iterations - otherwise the RAM usage gets humongous for larger experiments 50 | 51 | # CREATE DATASET LOADERS 52 | #import quantLab.quantlab.ImageNet.preprocess as pp 53 | #datasetTrain, datasetVal, _ = 54 | #pp.load_datasets('/scratch/datasets/ilsvrc12/', augment=False) 55 | if dataLoader is None: 56 | transform = tv.transforms.Compose([ 57 | tv.transforms.Resize(256), 58 | tv.transforms.RandomCrop(224), 59 | tv.transforms.ToTensor() 60 | ]) 61 | dataset = tv.datasets.ImageFolder(datasetPath, transform=transform) 62 | dataLoader = torch.utils.data.DataLoader(dataset, batch_size=batchSize, shuffle=True) 63 | model.eval() 64 | 65 | 66 | 67 | # SELECT MODULES 68 | msReLU = list(filter(lambda m: type(m) == torch.nn.modules.ReLU or type(m) == torch.nn.modules.ReLU6, model.modules())) 69 | 70 | def filter_fmaps(tens4, frac): 71 | # select filt proportion of random channels of 4dim tensor 72 | if (tens4.dim()==4): 73 | num_chans = list(tens4.size())[1] 74 | indices = random.sample(range(num_chans), math.ceil(frac * num_chans)) 75 | return tens4[:, indices, :, :] 76 | return tens4 77 | 78 | #register hooks to get intermediate outputs: 79 | def setupFwdHooks(modules): 80 | outputs = [] 81 | outputs_f = [] 82 | def hook(module, input, output): 83 | fm = output.detach().contiguous().clone().to('cpu') 84 | #fm_q, _, dtype = bpc.quantize(fm, quant='fixed{}'.format(quant), safetyFactor=safetyFactor, normalize=True) 85 | outputs_f.append(filter_fmaps(fm, frac)) 86 | outputs.append(fm) 87 | for m in modules: 88 | m.register_forward_hook(hook) 89 | return outputs_f, outputs 90 | 91 | outputsReLU, outputsReLU_complete = setupFwdHooks(msReLU) 92 | 93 | # PASS IMAGES THROUGH NETWORK 94 | dataIterator = iter(dataLoader) 95 | 96 | for _ in range(numBatches): 97 | (image, target) = next(dataIterator) 98 | image, target = image.to(device), target.to(device) 99 | model.eval() 100 | outp = model(image) 101 | 102 | outputs_max = [outp.max().item() for outp in outputsReLU_complete] 103 | for op, opmax in zip(outputsReLU, outputs_max): 104 | op.mul_(safetyFactor/opmax) 105 | 106 | return outputsReLU 107 | 108 | 109 | 110 | def getStimuli(model, dataset_path, data_w, num_batches, batch_size, signed=True, safety_factor=0.75, fmap_frac=0.01, num_stims=10000, block_size=8): 111 | assert data_w in [8, 16, 32] 112 | def get_dtype(q): 113 | dt_dict = {8:np.int8, 16:np.int16, 32:np.int32} 114 | return dt_dict[q] 115 | if model not in ['all_zeros', 'random', 'last_test']: 116 | model, device = getModel(model) 117 | fms = getFMs(model, numBatches=num_batches, batchSize=batch_size, datasetPath=dataset_path, device=device, frac=fmap_frac, safetyFactor=safety_factor) 118 | fms_flat = torch.tensor([]) 119 | for fm in fms: 120 | #safety_factor is already applied in getFMs 121 | fm_quant, _, dtype = bpc.quantize(fm, safetyFactor=1.0, normalize=False, quant='fixed{}'.format(data_w)) 122 | fms_flat = torch.cat([fms_flat, fm_quant.to(torch.device('cpu')).view(-1)]) 123 | 124 | #fms_q, _, dtype = bpc.quantize(fms_flat, normalize=True, quant='fixed{}'.format(data_w)) 125 | #data = fms_q.numpy().astype(dtype).tolist() 126 | data = fms_flat.numpy().astype(get_dtype(data_w)).tolist() 127 | elif model == 'all_zeros': 128 | data = [0] * num_stims 129 | elif model == 'random': 130 | data = [np.random.randint(-2**(data_w-1), 2**(data_w-1)) for l in range(num_stims)] 131 | elif model == 'last_test': 132 | num_stims = num_stims + (-num_stims % block_size) 133 | # construct a case where there are no zeros, an integer number of 134 | # blocks of nonzero values and a bunch of zeros afterwards 135 | data = [np.random.randint(1, 2**(data_w-1)) for l in range(num_stims)] + 100 * [0] 136 | return data 137 | 138 | def write_stats(f, stat_dict): 139 | with open(f, 'w') as fh: 140 | for stat in stat_dict: 141 | fh.write("{:<20}:{:>20.2f}\n".format(stat, stat_dict[stat])) 142 | 143 | def genStimFiles(file_prefixes, data_w, *args, modules=['encoder', 'decoder'], max_zrle_len=16, block_size=8, debug_file=None, num_words_w=24, **kwargs): 144 | fms_q = getStimuli(*args, data_w=data_w, **kwargs) 145 | fms_q = np.array(fms_q) 146 | fms_bin = bpc.valuesToBinary(fms_q, data_w) 147 | fms_bin = split_str(fms_bin, data_w) 148 | last_bin = ['0']*(len(fms_q)-1) + ['1'] 149 | nz_data = [el for el in fms_q if el != 0] 150 | nz_data_padded = zero_pad_list(nz_data, block_size) 151 | bpc_vals, mod_bpc_len = bpc.BPC_words(np.array(nz_data_padded), block_size=block_size, variant='paper', word_w=8, dbg_fn=debug_file, return_mod_len=True) 152 | while kwargs['model']=='last_test' and mod_bpc_len != 0: 153 | fms_q = getStimuli(*args, data_w=data_w, **kwargs) 154 | fms_q = np.array(fms_q) 155 | fms_bin = bpc.valuesToBinary(fms_q, data_w) 156 | fms_bin = split_str(fms_bin, data_w) 157 | last_bin = ['0']*(len(fms_q)-1) + ['1'] 158 | nz_data = [el for el in fms_q if el != 0] 159 | nz_data_padded = zero_pad_list(nz_data, block_size) 160 | bpc_vals, mod_bpc_len = bpc.BPC_words(np.array(nz_data_padded), block_size=block_size, variant='paper', word_w=8, dbg_fn=debug_file, return_mod_len=True) 161 | 162 | znz_vals = bpc.ZRLE_words(fms_q, max_burst_len=max_zrle_len, wordwidth=data_w) 163 | nw_bin = bpc.valuesToBinary(np.array([len(fms_q)-1]), num_words_w) 164 | compr_ratio = len(fms_q)/(len(bpc_vals)+len(znz_vals)) 165 | sparsity = 1 - len(nz_data)/len(fms_q) 166 | znz_last = ['0']*(len(znz_vals)-1) + ['1'] 167 | bpc_last = ['0']*(len(bpc_vals)-1) + ['1'] 168 | stat_dict = {'Compression Ratio':compr_ratio, 'Sparsity':sparsity, '# of Stimuli':len(fms_q)} 169 | if 'encoder' in modules: 170 | write_sim_file(data=zip(fms_bin, last_bin), filename=file_prefixes['encoder']+'_input.stim', length=len(fms_bin)) 171 | write_sim_file(zip(bpc_vals, bpc_last), file_prefixes['encoder']+'_bpc.expresp', len(bpc_vals)) 172 | write_sim_file(zip(znz_vals, znz_last), file_prefixes['encoder']+'_znz.expresp', len(znz_vals)) 173 | write_stats(file_prefixes['encoder']+'_stats.log', stat_dict) 174 | if 'decoder' in modules: 175 | write_sim_file(data=zip([nw_bin]), filename=file_prefixes['decoder']+'_num_words_input.stim', length=1) 176 | write_sim_file(zip(bpc_vals, bpc_last), file_prefixes['decoder']+'_bpc_input.stim', len(bpc_vals)) 177 | write_sim_file(zip(znz_vals, znz_last), file_prefixes['decoder']+'_znz_input.stim', len(znz_vals)) 178 | write_sim_file(zip(fms_bin, last_bin), file_prefixes['decoder']+'_data_output.expresp', len(fms_q)) 179 | write_stats(file_prefixes['decoder']+'_stats.log', stat_dict) 180 | --------------------------------------------------------------------------------