├── Doubly_Linked_List ├── README.md ├── src │ ├── doubly_linked_list.sv │ └── rtl_list.f └── tb │ ├── cocotb │ ├── Makefile │ └── tb.py │ └── sv │ ├── Makefile │ └── tb.sv ├── FIFO ├── README.md ├── src │ ├── fifo.sv │ └── rtl_list.f ├── tb │ ├── cocotb │ │ ├── Makefile │ │ └── tb.py │ └── sv │ │ ├── Makefile │ │ └── tb.sv └── vip │ ├── pyuvm │ └── README.md │ └── uvm │ ├── README.md │ ├── agent │ ├── fifo_vip_agent.sv │ ├── fifo_vip_driver.sv │ ├── fifo_vip_monitor.sv │ └── fifo_vip_sequencer.sv │ ├── common │ ├── fifo_vip_config.sv │ ├── fifo_vip_pkg.sv │ └── fifo_vip_seq_item.sv │ ├── env │ ├── fifo_vip_env.sv │ └── fifo_vip_scoreboard.sv │ ├── interface │ └── fifo_vip_if.sv │ ├── sequences │ ├── fifo_vip_base_seq.sv │ ├── fifo_vip_read_req_seq.sv │ └── fifo_vip_write_req_seq.sv │ └── sim │ ├── tb_top.sv │ └── tests │ ├── fifo_vip_base_test.sv │ └── fifo_vip_simple_test.sv ├── Hash_Table ├── README.md ├── src │ ├── hash_table.sv │ └── rtl_list.f └── tb │ ├── cocotb │ ├── Makefile │ └── tb.py │ └── sv │ ├── Makefile │ └── tb.sv ├── LICENSE ├── LIFO ├── README.md ├── src │ ├── lifo.sv │ └── rtl_list.f └── tb │ ├── cocotb │ ├── Makefile │ └── tb.py │ └── sv │ ├── Makefile │ └── tb.sv ├── List ├── README.md ├── src │ ├── adder.sv │ ├── list.sv │ ├── rtl_src.f │ └── sorter.sv └── tb │ └── sv │ └── tb.sv ├── README.md ├── Singly_Linked_List ├── README.md ├── src │ ├── rtl_file.f │ └── singly_linked_list.sv └── tb │ ├── cocotb │ ├── Makefile │ └── tb.py │ └── sv │ ├── Makefile │ └── tb.sv ├── Systolic_Array ├── README.md ├── src │ └── systolic_array.sv └── tb │ └── sv │ └── tb.sv ├── Table ├── README.md ├── src │ ├── rtl_file.f │ └── table.sv └── tb │ ├── cocotb │ ├── Makefile │ └── tb.py │ └── sv │ ├── Makefile │ └── tb.sv ├── Utils └── tb │ └── sv │ └── Makefile └── assets └── RTLStructLib_logo.png /Doubly_Linked_List/README.md: -------------------------------------------------------------------------------- 1 | # Doubly Linked List 2 | 3 | A configurable hardware implementation of a doubly linked list data structure in Verilog. The module supports bidirectional traversal with both forward and backward pointers, providing efficient insertion, deletion, and reading operations at specific addresses or indices. 4 | 5 | ## Features 6 | 7 | - Configurable data width and maximum node count 8 | - Bidirectional traversal support 9 | - Multiple insertion and deletion operations 10 | - Hardware-based node management 11 | - Full status monitoring (full, empty, length) 12 | - Fault detection 13 | - Head and tail pointer tracking 14 | - Synchronous operation with start/done handshaking 15 | 16 | ## Parameters 17 | 18 | | Parameter | Description | Default | 19 | |-------------|--------------------------------------------------|---------| 20 | | DATA_WIDTH | Width of data stored in each node | 8 | 21 | | MAX_NODE | Maximum number of nodes supported | 8 | 22 | 23 | ## Interface Signals 24 | 25 | | Signal | Direction | Width | Description | 26 | |-----------------|-----------|---------------|------------------------------------------------| 27 | | clk | Input | 1 | System clock | 28 | | rst | Input | 1 | Active high reset | 29 | | data_in | Input | DATA_WIDTH | Input data for insertion | 30 | | addr_in | Input | ADDR_WIDTH | Target address/index for operations | 31 | | op | Input | 3 | Operation code | 32 | | op_start | Input | 1 | Start signal for operation | 33 | | op_done | Output | 1 | Operation completion signal | 34 | | data_out | Output | DATA_WIDTH | Output data from read operations | 35 | | pre_node_addr | Output | ADDR_WIDTH | Address of previous node in list | 36 | | next_node_addr | Output | ADDR_WIDTH | Address of next node in list | 37 | | length | Output | ADDR_WIDTH | Current number of nodes in list | 38 | | head | Output | ADDR_WIDTH | Address of first node | 39 | | tail | Output | ADDR_WIDTH | Address of last node | 40 | | full | Output | 1 | List full indicator | 41 | | empty | Output | 1 | List empty indicator | 42 | | fault | Output | 1 | Error indicator | 43 | 44 | ## Supported Operations 45 | 46 | | Op Code | Operation | Description | 47 | |---------|------------------|---------------------------------------------------------| 48 | | 0 | Read_Addr | Read data at specified address | 49 | | 1 | Insert_Addr | Insert new node at specified address | 50 | | 2 | Delete_Value | Delete first occurrence of specified value | 51 | | 3 | Delete_Addr | Delete node at specified address | 52 | | 5 | Insert_Index | Insert new node at specified index | 53 | | 7 | Delete_Index | Delete node at specified index | 54 | 55 | ## State Machine 56 | 57 | The module implements a FSM with the following states: 58 | 59 | 1. **IDLE (3'b000)** 60 | - Initial state, waits for operation start 61 | - Validates operation parameters 62 | - Handles direct head/tail operations 63 | 64 | 2. **FIND_ADDR (3'b001)** 65 | - Traverses list to find target address 66 | - Used for address-based operations 67 | 68 | 3. **FIND_VALUE (3'b010)** 69 | - Traverses list to find target value 70 | - Used for value-based deletion 71 | 72 | 4. **FIND_INDEX (3'b110)** 73 | - Traverses list to find target index 74 | - Supports both ascending and descending search 75 | 76 | 5. **INSERT_STG1 (3'b011)** 77 | - First stage of node insertion 78 | - Updates bidirectional links 79 | 80 | 6. **EXECUTE (3'b100)** 81 | - Completes operation 82 | - Updates status signals 83 | 84 | 7. **FAULT (3'b101)** 85 | - Handles error conditions 86 | 87 | ## Memory Organization 88 | 89 | The implementation uses three separate memory arrays: 90 | 1. Data storage (`node_data`) 91 | 2. Forward pointers (`node_next_node_addr`) 92 | 3. Backward pointers (`node_pre_node_addr`) 93 | 94 | This organization allows for: 95 | - Independent access to data and pointers 96 | - Efficient pointer updates 97 | - Parallel validity checking 98 | 99 | ## Usage Example 100 | 101 | ```verilog 102 | // Instantiate a doubly linked list with 16 nodes and 32-bit data 103 | doubly_linked_list #( 104 | .DATA_WIDTH(32), 105 | .MAX_NODE(16) 106 | ) list_inst ( 107 | .clk(system_clock), 108 | .rst(reset), 109 | .data_in(input_data), 110 | .addr_in(target_addr), 111 | .op(operation_code), 112 | .op_start(start_signal), 113 | .op_done(done_signal), 114 | .data_out(output_data), 115 | .pre_node_addr(prev_addr), 116 | .next_node_addr(next_addr), 117 | .length(list_length), 118 | .head(head_addr), 119 | .tail(tail_addr), 120 | .full(list_full), 121 | .empty(list_empty), 122 | .fault(error_signal) 123 | ); 124 | ``` 125 | ## Reset Behavior 126 | 127 | On assertion of reset: 128 | - All nodes are invalidated 129 | - Head and tail pointers set to NULL 130 | - Length cleared to zero 131 | - All node data cleared 132 | - All status signals deasserted 133 | 134 | ## Implementation Notes 135 | 136 | 1. **Memory Organization**: 137 | - Next node pointers stored separately 138 | - Valid bits for node tracking 139 | 140 | 2. **Address Handling**: 141 | - NULL address = MAX_NODE 142 | - Valid addresses: 0 to (MAX_NODE-1) 143 | 144 | 3. **Error Conditions**: 145 | - List full during insertion 146 | - List empty during deletion/read 147 | - Invalid address/index 148 | - Address overflow 149 | 150 | ## Limitations 151 | 152 | - Fixed maximum size defined by MAX_NODE 153 | - Sequential search for operations 154 | - Single operation at a time 155 | - No concurrent access support 156 | 157 | -------------------------------------------------------------------------------- /Doubly_Linked_List/src/rtl_list.f: -------------------------------------------------------------------------------- 1 | doubly_linked_list.sv 2 | -------------------------------------------------------------------------------- /Doubly_Linked_List/tb/cocotb/Makefile: -------------------------------------------------------------------------------- 1 | SIM ?= icarus 2 | TOPLEVEL_LANG ?= verilog 3 | WAVES ?= 1 #set 1 to enable waveform dump. 4 | 5 | PWD=$(shell pwd) 6 | 7 | #export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) 8 | 9 | VERILOG_SOURCES = $(PWD)/../../src/doubly_linked_list.sv 10 | 11 | # DUT Top 12 | TOPLEVEL = doubly_linked_list 13 | # top python file name 14 | MODULE = tb 15 | 16 | #use , separtor to run multiple TESTCASE, by default all @cocotb.test will be run 17 | #TESTCASE = index_op_test , addr_op_test 18 | 19 | COMPILE_ARGS = -Ptable_top.DATA_WIDTH=8 # DUT parameter #"-p" (parameter) iverilog command flags 20 | COMPILE_ARGS += -Ptable_top.MAX_NODE=8 # DUT parameter #"-p" (parameter) iverilog command flags 21 | #run make clean before running with new parameter. 22 | 23 | #Set RANDOM_SEED number 24 | #PLUSARGS = +seed=1716033254 25 | COCOTB_HDL_TIMEUNIT = 1ns 26 | COCOTB_HDL_TIMEPRECISION = 1ps 27 | 28 | ifeq ($(SIM), icarus) 29 | $(shell echo 'module iverilog_dump();' > iverilog_dump.v) 30 | $(shell echo 'initial begin' >> iverilog_dump.v) 31 | $(shell echo ' $$dumpfile("$(TOPLEVEL).vcd");' >> iverilog_dump.v) 32 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 33 | $(shell echo 'end' >> iverilog_dump.v) 34 | $(shell echo 'endmodule' >> iverilog_dump.v) 35 | VERILOG_SOURCES += $(PWD)/iverilog_dump.v 36 | COMPILE_ARGS += -s iverilog_dump 37 | endif 38 | 39 | include $(shell cocotb-config --makefiles)/Makefile.sim 40 | 41 | 42 | -------------------------------------------------------------------------------- /Doubly_Linked_List/tb/sv/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DUT ?= doubly_linked_list 3 | SIM_OPTS ?= ../../src/doubly_linked_list.sv 4 | SEED ?= $$(shuf -i 1-10000 -n 1) 5 | 6 | # DUT parameter #"-p" iverilog command flags 7 | # COMPILE_ARGS = -P tb.DUT_DATA_WIDTH=8 8 | # COMPILE_ARGS += -P tb.DUT_MAX_NODE=8 9 | # COMPILE_ARGS += -P tb.TB_CLK_PERIOD=25 10 | # COMPILE_ARGS += -P tb.TB_TEST_WEIGHT=1 11 | # COMPILE_ARGS += -P tb.TB_CLK_PERIOD=100 12 | # COMPILE_ARGS += -P tb.SIM_TIMEOUT=500000 13 | 14 | XILINX_LIB_URL ?= https://github.com/Xilinx/XilinxUnisimLibrary.git 15 | XILINX_LIB_DIR ?= XilinxUnisimLibrary/verilog/src 16 | 17 | sim: gen_sim 18 | ./${DUT}.sim +VCDFILE=sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee sim.log 19 | 20 | gen_sim: tb.sv ${SIM_OPTS} # to generate executable file by using iverilator 21 | iverilog -g2012 -s tb ${COMPILE_ARGS} -o ${DUT}.sim $^ 22 | 23 | synth: ${SIM_OPTS} 24 | yosys -p synth_xilinx ${SIM_OPTS} -L synth.log -o ${DUT}.netlist.v 25 | #timeout 26 | 27 | netlist_sim: gen_netlist_sim 28 | ./${DUT}.netlist.sim +VCDFILE=netlist_sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee netlist_sim.log 29 | 30 | gen_netlist_sim: tb.sv ${DUT}.netlist.v | ${XILINX_LIBS_DIR}/.got 31 | iverilog -g2012 -s tb -y ${XILINX_LIB_DIR} -y ${XILINX_LIB_DIR}/unisims -DXILINX_GLS ${COMPILE_ARGS} -o ${DUT}.netlist.sim $^ 32 | 33 | ${XILINX_LIBS_DIR}/.git: 34 | git clone ${XILINX_LIB_URL} 35 | 36 | clean: 37 | rm -f ${DUT}_sim sim.log sim.vcd 38 | rm -f ${DUT}.netlist.v synth.log 39 | -------------------------------------------------------------------------------- /FIFO/README.md: -------------------------------------------------------------------------------- 1 | # FIFO (Queue) 2 | 3 | A configurable FIFO (First-In-First-Out) buffer implementation in Verilog with support for both synchronous and asynchronous clock domains. The module uses Gray code for pointer synchronization in asynchronous mode to prevent metastability issues. 4 | 5 | ## Features 6 | 7 | - Configurable data width and depth 8 | - Support for both synchronous and asynchronous clock domains 9 | - Optional registered read output 10 | - Gray code synchronization for clock domain crossing 11 | - Full and empty status flags 12 | - Automatic pointer wraparound 13 | - Parameterized design for easy customization 14 | 15 | ## Parameters 16 | 17 | | Parameter | Description | Default | 18 | |-------------|--------------------------------------------------|---------| 19 | | DEPTH | Number of entries in the FIFO | 12 | 20 | | DATA_WIDTH | Width of each data entry | 8 | 21 | | ASYNC | Enable asynchronous mode (1) or synchronous (0) | 1 | 22 | | RD_BUFFER | Enable registered read output (1) or bypass (0) | 1 | 23 | 24 | ## Interface Signals 25 | 26 | | Signal | Direction | Width | Description | 27 | |-------------|-----------|---------------|------------------------------------------------| 28 | | rd_clk | Input | 1 | Read clock domain | 29 | | wr_clk | Input | 1 | Write clock domain | 30 | | rst | Input | 1 | Active high reset | 31 | | data_wr | Input | DATA_WIDTH | Data input for writing | 32 | | wr_en | Input | 1 | Write enable signal | 33 | | fifo_full | Output | 1 | FIFO full indicator | 34 | | data_rd | Output | DATA_WIDTH | Data output from reading | 35 | | rd_en | Input | 1 | Read enable signal | 36 | | fifo_empty | Output | 1 | FIFO empty indicator | 37 | 38 | ## Usage Example 39 | 40 | ```verilog 41 | // Instantiate a 16-deep, 32-bit wide asynchronous FIFO with registered output 42 | fifo #( 43 | .DEPTH(16), 44 | .DATA_WIDTH(32), 45 | .ASYNC(1), 46 | .RD_BUFFER(1) 47 | ) fifo_inst ( 48 | .rd_clk(read_clock), 49 | .wr_clk(write_clock), 50 | .rst(reset), 51 | .data_wr(write_data), 52 | .wr_en(write_enable), 53 | .fifo_full(full_flag), 54 | .data_rd(read_data), 55 | .rd_en(read_enable), 56 | .fifo_empty(empty_flag) 57 | ); 58 | ``` 59 | 60 | ## Design Notes 61 | 62 | 1. **Clock Domain Crossing**: In asynchronous mode (ASYNC=1), the design uses Gray code encoding for pointer synchronization to prevent metastability issues when crossing clock domains. 63 | 64 | 2. **Read Buffer**: When RD_BUFFER=1, the read data output is registered, adding one clock cycle of latency but improving timing. When RD_BUFFER=0, the read data is combinatorial. 65 | 66 | 3. **Full/Empty Flags**: 67 | - The empty flag is asserted when read and write pointers are equal 68 | - The full flag is asserted when the next write pointer would equal the current read pointer 69 | 70 | 4. **Reset Behavior**: 71 | - All pointers are reset to zero 72 | - All memory locations are cleared 73 | - All flags are deasserted 74 | 75 | ## Implementation Details 76 | 77 | The module uses separate read and write pointers for tracking FIFO occupancy. In asynchronous mode, these pointers are converted to Gray code before being synchronized across clock domains. The implementation includes: 78 | 79 | - Binary counters for read/write operation 80 | - Gray code conversion for pointer synchronization 81 | - Two-stage synchronizers for clock domain crossing 82 | - Configurable output registration 83 | - Automatic pointer wraparound at FIFO boundaries 84 | 85 | ## Timing Considerations 86 | 87 | 1. In asynchronous mode, allow at least 2 clock cycles for pointer synchronization 88 | 2. When RD_BUFFER=1, read data is available one clock cycle after rd_en assertion 89 | 3. Full and empty flags are registered outputs 90 | 91 | ## Limitations 92 | 93 | - The DEPTH parameter must be a power of 2 for proper wraparound behavior 94 | - Simultaneous read/write at full/empty conditions should be managed by external logic 95 | - The reset signal must be synchronized to both clock domains 96 | -------------------------------------------------------------------------------- /FIFO/src/fifo.sv: -------------------------------------------------------------------------------- 1 | module fifo #( 2 | parameter DEPTH = 12, 3 | parameter DATA_WIDTH = 8, 4 | parameter ASYNC = 1, 5 | parameter RD_BUFFER = 1 6 | )( 7 | input wire rd_clk, 8 | input wire wr_clk, 9 | input wire rst, 10 | input wire [DATA_WIDTH-1:0] data_wr, 11 | input wire wr_en, 12 | output reg fifo_full, 13 | output logic [DATA_WIDTH-1:0] data_rd, 14 | input wire rd_en, 15 | output wire fifo_empty 16 | ); 17 | localparam CNTR_WIDTH = clogb2(DEPTH); 18 | 19 | reg [CNTR_WIDTH-1:0] rd_gray_pointer, rd_binary_pointer; 20 | wire [CNTR_WIDTH-1:0] rd_binary_pointer_next; 21 | reg [CNTR_WIDTH-1:0] wr_gray_pointer, wr_binary_pointer; 22 | wire [CNTR_WIDTH-1:0] wr_binary_pointer_next; 23 | reg [DATA_WIDTH-1:0] fifo_stored [DEPTH-1:0]; 24 | reg [CNTR_WIDTH-1:0] rd_gray_pointer_sync[1:0]; 25 | reg [CNTR_WIDTH-1:0] wr_gray_pointer_sync[1:0]; 26 | wire rdptr_eq_next_wrptr; 27 | 28 | generate //: POINTER_SYNCHRONIZER 29 | if (ASYNC == 1) begin 30 | always @ (posedge rd_clk, posedge rst) begin 31 | if(rst) begin 32 | {wr_gray_pointer_sync[1], wr_gray_pointer_sync[0]} <= {(2*CNTR_WIDTH){1'b0}}; 33 | end else begin 34 | {wr_gray_pointer_sync[1], wr_gray_pointer_sync[0]} <= {wr_gray_pointer_sync[0], wr_gray_pointer}; 35 | end 36 | end 37 | 38 | always @ (posedge wr_clk, posedge rst) begin 39 | if(rst) begin 40 | {rd_gray_pointer_sync[1], rd_gray_pointer_sync[0]} <= {(2*CNTR_WIDTH){1'b0}}; 41 | end else begin 42 | {rd_gray_pointer_sync[1], rd_gray_pointer_sync[0]} <= {rd_gray_pointer_sync[0], rd_gray_pointer}; 43 | end 44 | end 45 | end 46 | endgenerate 47 | 48 | // data_wr, wr_binary_pointer, wr_gray_pointer 49 | assign wr_binary_pointer_next = (wr_binary_pointer == DEPTH-1) ? {CNTR_WIDTH{1'b0}} : wr_binary_pointer + 1; 50 | 51 | integer i; 52 | 53 | always @ (posedge wr_clk, posedge rst) begin 54 | if(rst) begin 55 | wr_binary_pointer <= {CNTR_WIDTH{1'b0}}; 56 | end else if((wr_en & !rdptr_eq_next_wrptr) | (fifo_full & !rdptr_eq_next_wrptr)) begin 57 | // 1. When next write pointer == read pointer AND wr_en, it means last entry of FIFO is being filled, do not update pointer, else update pointer. 58 | // 2. Update when fifo_full flag is cleared. 59 | wr_binary_pointer <= wr_binary_pointer_next; 60 | end 61 | end 62 | 63 | always @ (posedge wr_clk, posedge rst) begin 64 | if(rst) begin 65 | for (i = 0; i < DEPTH; i = i + 1) 66 | fifo_stored[i] <= {DATA_WIDTH{1'b0}}; 67 | end else if (wr_en & !fifo_full) begin 68 | fifo_stored[wr_binary_pointer] <= data_wr; 69 | end 70 | end 71 | 72 | generate 73 | if(ASYNC == 1) begin 74 | always @ (posedge wr_clk, posedge rst) begin 75 | if(rst) begin 76 | wr_gray_pointer <= {CNTR_WIDTH{1'b0}}; 77 | end else if ((wr_en & !rdptr_eq_next_wrptr) | (fifo_full & !rdptr_eq_next_wrptr)) begin 78 | // 1. When next write pointer == read pointer AND wr_en, it means last entry of FIFO is being filled, do not update pointer, else update pointer. 79 | // 2. Update when fifo_full flag is cleared. 80 | wr_gray_pointer <= bin_to_gray(wr_binary_pointer_next); 81 | end 82 | end 83 | end 84 | endgenerate 85 | 86 | 87 | // data_rd, rd_binary_pointer, rd_gray_pointer 88 | assign rd_binary_pointer_next = (rd_binary_pointer == DEPTH-1) ? {CNTR_WIDTH{1'b0}} : rd_binary_pointer + 1; 89 | 90 | always @ (posedge rd_clk, posedge rst) begin 91 | if(rst) begin 92 | rd_binary_pointer <= {CNTR_WIDTH{1'b0}}; 93 | end else if (rd_en & !fifo_empty) begin 94 | rd_binary_pointer <= rd_binary_pointer_next; 95 | end 96 | end 97 | 98 | generate 99 | if(RD_BUFFER == 1) begin 100 | always @ (posedge rd_clk, posedge rst) begin 101 | if(rst) 102 | data_rd <= {DATA_WIDTH{1'b0}}; 103 | else if (rd_en & !fifo_empty) 104 | data_rd <= fifo_stored[rd_binary_pointer]; 105 | end 106 | end else begin 107 | assign data_rd = (rd_en & !fifo_empty) ? fifo_stored[rd_binary_pointer] : {DATA_WIDTH{1'b0}}; 108 | end 109 | endgenerate 110 | 111 | generate 112 | if(ASYNC == 1) begin 113 | always @ (posedge rd_clk, posedge rst) begin 114 | if(rst) begin 115 | rd_gray_pointer <= {CNTR_WIDTH{1'b0}}; 116 | end else if(rd_en & !fifo_empty) begin 117 | rd_gray_pointer <= bin_to_gray(rd_binary_pointer_next); 118 | end 119 | end 120 | end 121 | endgenerate 122 | 123 | // flag 124 | generate 125 | if (ASYNC == 1) begin 126 | assign rdptr_eq_next_wrptr = (rd_gray_pointer_sync[1] == bin_to_gray(wr_binary_pointer_next)); 127 | assign fifo_empty = wr_gray_pointer_sync[1] == bin_to_gray(rd_binary_pointer); 128 | //assign fifo_full = rd_gray_pointer_sync[1] == bin_to_gray(wr_binary_pointer_next); 129 | end else begin 130 | assign rdptr_eq_next_wrptr = (rd_binary_pointer == wr_binary_pointer_next); 131 | assign fifo_empty = wr_binary_pointer == rd_binary_pointer; 132 | //assign fifo_full = rd_binary_pointer == wr_binary_pointer_next; 133 | end 134 | endgenerate 135 | 136 | always@(posedge wr_clk, posedge rst) begin 137 | if(rst) begin 138 | fifo_full <= 1'b0; 139 | end else if(wr_en & rdptr_eq_next_wrptr) begin 140 | // When next write pointer == read pointer AND wr_en, it means last entry of FIFO is being filled, do not update pointer, 141 | fifo_full <= 1'b1; 142 | end else if(!rdptr_eq_next_wrptr) begin 143 | // Deassert when any read operation is completed: write pointer != read pointer 144 | fifo_full <= 1'b0; 145 | end 146 | end 147 | 148 | function integer clogb2; 149 | input integer value; 150 | value = value - (value > 1); 151 | for(clogb2=0; value>0; value = value>>1) 152 | clogb2 = clogb2 + 1; 153 | endfunction 154 | 155 | function [CNTR_WIDTH-1:0] bin_to_gray; 156 | input [CNTR_WIDTH-1:0] bin; 157 | bin_to_gray = bin[CNTR_WIDTH-1:0] ^ (bin[CNTR_WIDTH-1:0] >> 1); 158 | endfunction 159 | 160 | endmodule 161 | -------------------------------------------------------------------------------- /FIFO/src/rtl_list.f: -------------------------------------------------------------------------------- 1 | fifo.sv 2 | -------------------------------------------------------------------------------- /FIFO/tb/cocotb/Makefile: -------------------------------------------------------------------------------- 1 | SIM ?= icarus 2 | TOPLEVEL_LANG ?= verilog 3 | WAVES ?= 1 #set 1 to enable waveform dump. 4 | 5 | PWD=$(shell pwd) 6 | 7 | #export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) 8 | 9 | VERILOG_SOURCES = $(PWD)/../../src/fifo.sv 10 | 11 | TOPLEVEL = fifo # DUT Top 12 | MODULE = tb # top python file 13 | 14 | #use , separtor to run multiple TESTCASE, by default all @cocotb.test will be run 15 | #TESTCASE = fifo_rand_read_write_test,fifo_rand_write_then_read_test,fifo_rand_read_write_simul_test 16 | #TESTCASE = fifo_rand_read_write_simul_test 17 | 18 | COMPILE_ARGS = -Pfifo.DEPTH=12 # DUT parameter #"-p" iverilog command flags 19 | COMPILE_ARGS += -Pfifo.DATA_WIDTH=8 # DUT parameter #"-p" iverilog command flags 20 | COMPILE_ARGS += -Pfifo.ASYNC=1 # DUT paramter #"-p" iverilog command flags 21 | COMPILE_ARGS += -Pfifo.RD_BUFFER=1 # DUT paramter #"-p" iverilog command flags 22 | #run make clean before running with new parameter. 23 | 24 | #Set RANDOM_SEED number 25 | #PLUSARGS = +seed=1 26 | 27 | COCOTB_HDL_TIMEUNIT = 1ns 28 | COCOTB_HDL_TIMEPRECISION = 1ps 29 | 30 | ifeq ($(SIM), icarus) 31 | $(shell echo 'module iverilog_dump();' > iverilog_dump.v) 32 | $(shell echo 'initial begin' >> iverilog_dump.v) 33 | $(shell echo ' $$dumpfile("$(TOPLEVEL).vcd");' >> iverilog_dump.v) 34 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 35 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 36 | $(shell echo 'end' >> iverilog_dump.v) 37 | $(shell echo 'endmodule' >> iverilog_dump.v) 38 | VERILOG_SOURCES += $(PWD)/iverilog_dump.v 39 | COMPILE_ARGS += -s iverilog_dump 40 | endif 41 | 42 | include $(shell cocotb-config --makefiles)/Makefile.sim 43 | -------------------------------------------------------------------------------- /FIFO/tb/cocotb/tb.py: -------------------------------------------------------------------------------- 1 | import random 2 | #import asyncio 3 | import cocotb 4 | from cocotb.triggers import Timer, RisingEdge 5 | from cocotb.clock import Clock 6 | from cocotb.result import TestFailure 7 | 8 | DEPTH = 12 # DUT parameter 9 | DATA_WIDTH = 8 # DUT paramter 10 | ASYNC = 1 # DUT parameter 11 | RD_BUFFER = 1 # DUT parameter 12 | TEST_WEIGHT = 1 # TB multiplier for stimulus injected 13 | WR_CLK_PERIOD = 20 # TB wr_clk generator 14 | RD_CLK_PERIOD = 32 # TB rd_clk generator 15 | #SIM_TIMEOUT = 100000; // TB simulation time out 16 | BURST_LENGHT = DEPTH 17 | MAX_DATA = 2**DATA_WIDTH - 1 18 | err_cnt = 0 19 | 20 | # fifo #( 21 | # .DEPTH(DEPTH), 22 | # .DATA_WIDTH(DATA_WIDTH), 23 | # .ASYNC(ASYNC), 24 | # .RD_BUFFER(RD_BUFFER)) DUT ( 25 | # /*input wire*/ .rd_clk(rd_clk), 26 | # /*input wire*/ .wr_clk(wr_clk), 27 | # /*input wire*/ .rst(rst), 28 | # /*input wire [DATA_WIDTH-1:0]*/ .data_wr(data_wr), 29 | # /*input wire*/ .wr_en(wr_en), 30 | # /*output wire*/ .fifo_full(fifo_full), 31 | # /*output logic [DATA_WIDTH-1:0]*/ .data_rd(data_rd), 32 | # /*input wire*/ .rd_en(rd_en), 33 | # /*output wire*/ .fifo_empty(fifo_empty)); 34 | 35 | async def dut_init(dut): 36 | global DEPTH 37 | global DATA_WIDTH 38 | global ASYNC 39 | global RD_BUFFER 40 | global MAX_DATA 41 | global BURST_LENGHT 42 | DEPTH = dut.DEPTH.value 43 | DATA_WIDTH = dut.DATA_WIDTH.value 44 | ASYNC = dut.ASYNC.value 45 | RD_BUFFER = dut.RD_BUFFER.value 46 | MAX_DATA = 2**DATA_WIDTH - 1 47 | BURST_LENGHT = DEPTH 48 | await cocotb.start(Clock(dut.wr_clk, WR_CLK_PERIOD, units="ns").start()) 49 | await cocotb.start(Clock(dut.rd_clk, RD_CLK_PERIOD, units="ns").start()) 50 | dut.rst.value = 1 51 | await(Timer(1000,'ns')) 52 | dut.rst.value = 0 53 | await(Timer(1000,'ns')) 54 | 55 | async def fifo_write_burst_rand(count,dut,fifo_expected): 56 | global err_cnt 57 | for i in range(count): 58 | await RisingEdge(dut.wr_clk) 59 | await(Timer(1,'ns')) 60 | if(len(fifo_expected) < DEPTH and dut.fifo_full.value == 1): 61 | dut._log.error("FIFO is not full but fifo_full flag is asserted") 62 | dut.wr_en.value = 1 63 | data_wr_rand = random.randint(0,MAX_DATA) 64 | dut.data_wr.value = data_wr_rand 65 | if(len(fifo_expected) < DEPTH): 66 | fifo_expected.append(data_wr_rand) 67 | dut._log.info("Data written = %d, FIFO entry = %d", data_wr_rand, len(fifo_expected)) 68 | else: 69 | await(Timer(1,'ns')) 70 | if(dut.fifo_full.value == 1): 71 | dut._log.info("FIFO is full, fifo_full flag is asserted correctly") 72 | else: 73 | dut._log.error("FIFO is full but fifo_full flag is not asserted") 74 | err_cnt += 1 75 | await RisingEdge(dut.wr_clk) 76 | await(Timer(1,'ns')) 77 | dut.wr_en.value = 0 78 | 79 | 80 | async def fifo_read_burst(count,dut,fifo_expected): 81 | global err_cnt 82 | rd_init = 0 83 | await RisingEdge(dut.rd_clk) 84 | await Timer(1,'ns') 85 | dut.rd_en.value = 1 86 | if(RD_BUFFER == 0): 87 | await Timer(1,'ns') 88 | if(len(fifo_expected)>0): 89 | if(dut.fifo_empty.value == 1): 90 | dut._log.error("FIFO is not empty but fifo_empty flag is asserted") 91 | err_cnt += 1 92 | data_rd_exp = fifo_expected.pop(0) 93 | data_rd_act = dut.data_rd.value.integer 94 | if(data_rd_exp == data_rd_act): 95 | dut._log.info("Data read = %d, FIFO entry = %d", data_rd_act, len(fifo_expected)) 96 | else: 97 | dut._log.error("Data read mismatch, ACT = %d, EXP = %d", data_rd_act, data_rd_exp) 98 | err_cnt += 1 99 | else: 100 | if(dut.fifo_empty.value == 1): 101 | dut._log.info("FIFO is empty, fifo_empty flag is asserted correctly") 102 | else: 103 | dut._log.error("FIFO is empty but fifo_empty flag is not asserted") 104 | err_cnt += 1 105 | for i in range(count): 106 | await RisingEdge(dut.rd_clk) 107 | await Timer(1,'ns') 108 | if(len(fifo_expected)>0): 109 | if(dut.fifo_empty.value == 1): 110 | if(len(fifo_expected) == 1): 111 | dut._log.info("FIFO is empty, fifo_empty flag is asserted correctly") 112 | else: 113 | dut._log.error("FIFO is not empty but fifo_empty flag is asserted") 114 | err_cnt += 1 115 | data_rd_exp = fifo_expected.pop(0) 116 | data_rd_act = dut.data_rd.value.integer 117 | if(data_rd_exp == data_rd_act): 118 | dut._log.info("Data read = %d, FIFO entry = %d", data_rd_act, len(fifo_expected)) 119 | else: 120 | dut._log.error("Data read mismatch, ACT = %d, EXP = %d", data_rd_act, data_rd_exp) 121 | err_cnt += 1 122 | else: 123 | if(dut.fifo_empty.value == 1): 124 | dut._log.info("FIFO is empty, fifo_empty flag is asserted correctly") 125 | else: 126 | dut._log.error("FIFO is empty but fifo_empty flag is not asserted") 127 | err_cnt += 1 128 | dut.rd_en.value = 0 129 | 130 | async def fifo_burst_write(dut,fifo_wr_stream,fifo_expected): 131 | for data_wr in fifo_wr_stream: 132 | await RisingEdge(dut.wr_clk) 133 | await Timer(1,'ns') 134 | dut.wr_en.value = 1 135 | dut.data_wr.value = data_wr 136 | fifo_expected.append(data_wr) 137 | dut._log.info("Data written = %d, FIFO entry = %d", data_wr, len(fifo_expected)) 138 | await RisingEdge(dut.wr_clk) 139 | await Timer(1,'ns') 140 | dut.wr_en.value = 0 141 | 142 | async def fifo_burst_read_return_stream(dut,count,fifo_expected): 143 | fifo_rd_stream = [] 144 | rd_init = 0 145 | while (rd_init == 0): 146 | await RisingEdge(dut.rd_clk) 147 | if(dut.fifo_empty.value != 1): 148 | if(rd_init == 0): 149 | await Timer(1,'ns') 150 | dut.rd_en.value = 1 151 | if(RD_BUFFER == 0): 152 | await Timer(1,'ns') 153 | data_rd = dut.data_rd.value.integer 154 | fifo_rd_stream.append(data_rd) 155 | fifo_expected.pop(0) 156 | dut._log.info("Data read = %d, FIFO entry = %d", data_rd,len(fifo_expected)) 157 | rd_init = 1 158 | while (len(fifo_rd_stream) < count): 159 | await RisingEdge(dut.rd_clk) 160 | await Timer(1,'ns') 161 | data_rd = dut.data_rd.value.integer 162 | fifo_rd_stream.append(data_rd) 163 | fifo_expected.pop(0) 164 | dut._log.info("Data read = %d, FIFO entry = %d", data_rd,len(fifo_expected)) 165 | dut.rd_en.value = 0 166 | return fifo_rd_stream 167 | 168 | async def fifo_read_write_rand_simul(count,dut): 169 | global err_cnt 170 | fifo_wr_stream = [] 171 | fifo_rd_stream = [] 172 | for i in range(BURST_LENGHT): 173 | data_wr_rand = random.randint(0,MAX_DATA) 174 | fifo_wr_stream.append(data_wr_rand) 175 | fifo_expected = [] 176 | await cocotb.start(fifo_burst_write(dut,fifo_wr_stream,fifo_expected)) 177 | fifo_rd_stream = await fifo_burst_read_return_stream(dut,BURST_LENGHT,fifo_expected) 178 | for i in range(len(fifo_wr_stream)): 179 | if(fifo_wr_stream[i] != fifo_rd_stream[i]): 180 | dut._log.error("Data rd %d does not match data wr %d", fifo_rd_stream[i],fifo_wr_stream[i]) 181 | err_cnt += 1 182 | 183 | @cocotb.test() 184 | async def fifo_rand_write_then_read_test(dut): 185 | await dut_init(dut) 186 | dut._log.info("\nFIFO WRITE BURST SEQ") 187 | fifo_expected = [] 188 | await fifo_write_burst_rand(DEPTH+3,dut,fifo_expected) 189 | await(Timer(1000,'ns')) 190 | dut._log.info("\nFIFO READ BURST SEQ") 191 | await fifo_read_burst(DEPTH+3,dut,fifo_expected) 192 | await(Timer(1000,'ns')) 193 | if (err_cnt > 0): 194 | cocotb.log.error("Errors count = %d",err_cnt) 195 | cocotb.result.test_fail() 196 | 197 | 198 | @cocotb.test() 199 | async def fifo_rand_read_write_test(dut): 200 | await dut_init(dut) 201 | dut._log.info("\nFIFO RANDOM READ WRITE SEQ") 202 | fifo_expected = [] 203 | i = DEPTH 204 | while(i >= 0): 205 | op_sel = random.randint(0,1) 206 | op_count = random.randint(1,5) 207 | i = i - op_count 208 | match (op_sel): 209 | case 1: 210 | await fifo_read_burst(op_count,dut,fifo_expected) 211 | await Timer(RD_CLK_PERIOD,'ns') 212 | await Timer(3*WR_CLK_PERIOD,'ns') 213 | case 0: 214 | await fifo_write_burst_rand(op_count,dut,fifo_expected) 215 | await Timer(WR_CLK_PERIOD,'ns') 216 | await Timer(3*RD_CLK_PERIOD,'ns') 217 | if (err_cnt > 0): 218 | cocotb.log.error("Errors count = %d",err_cnt) 219 | cocotb.result.test_fail() 220 | 221 | @cocotb.test() 222 | async def fifo_rand_read_write_simul_test(dut): 223 | cocotb.log.info("Seed = %d",cocotb.RANDOM_SEED) 224 | await dut_init(dut) 225 | dut._log.info("\nFIFO SIMULTANEOUS RANDOM READ WRITE SEQ") 226 | await fifo_read_write_rand_simul(1,dut) 227 | if (err_cnt > 0): 228 | cocotb.log.error("Errors count = %d",err_cnt) 229 | cocotb.result.test_fail() 230 | -------------------------------------------------------------------------------- /FIFO/tb/sv/Makefile: -------------------------------------------------------------------------------- 1 | DUT = fifo 2 | SIM_OPTS ?= ../../src/fifo.sv 3 | SEED ?= $$(shuf -i 1-10000 -n 1) 4 | XILINX_LIB_URL ?= https://github.com/Xilinx/XilinxUnisimLibrary.git 5 | XILINX_LIB_DIR ?= XilinxUnisimLibrary/verilog/src 6 | 7 | sim: gen_sim 8 | ./${DUT}.sim +VCDFILE=sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee sim.log 9 | 10 | gen_sim: tb.sv ${SIM_OPTS} # to generate executable file by using iverilator 11 | iverilog -g2012 -s tb -o ${DUT}.sim $^ 12 | 13 | synth: ${SIM_OPTS} 14 | yosys -p synth_xilinx ${SIM_OPTS} -L synth.log -o ${DUT}.netlist.v 15 | #timeout 16 | 17 | netlist_sim: gen_netlist_sim 18 | ./${DUT}.netlist.sim +VCDFILE=netlist_sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee netlist_sim.log 19 | 20 | gen_netlist_sim: tb.sv ${DUT}.netlist.v | ${XILINX_LIB_DIR}/.git 21 | iverilog -g2012 -DXILINX_GLS -s tb -y ${XILINX_LIB_DIR} -y ${XILINX_LIB_DIR}/unisims -DXILINX_GLS -o ${DUT}.netlist.sim tb.sv ${DUT}.netlist.v 22 | 23 | ${XILINX_LIB_DIR}/.git: 24 | git clone ${XILINX_LIB_URL} 25 | 26 | clean: 27 | rm -f ${DUT}_sim sim.log sim.vcd 28 | rm -f ${DUT}.netlist.v synth.log 29 | -------------------------------------------------------------------------------- /FIFO/tb/sv/tb.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // 4 | // Create Date: 05/05/2024 03:37:34 PM 5 | // Last Update Date: 05/11/2024 10:37:12 AM 6 | // Module Name: tb 7 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 8 | // Description: 1. fifo_write_burst_rand 9 | // 2. fifo_read_burst 10 | // 3. fifo_burst_rand 11 | // 4. fifo_read_write_rand_simul 12 | // Additional Comments: 13 | // 14 | ////////////////////////////////////////////////////////////////////////////////// 15 | 16 | 17 | module tb( 18 | ); 19 | parameter DEPTH = 12; // DUT parameter 20 | parameter DATA_WIDTH = 8; // DUT paramter 21 | parameter ASYNC = 1; // DUT parameter 22 | parameter RD_BUFFER = 1; // DUT parameter 23 | parameter TEST_WEIGHT = 1; // TB multiplier for stimulus injected 24 | parameter WR_CLK_PERIOD = 20; // TB wr_clk generator 25 | parameter RD_CLK_PERIOD = 32; // TB rd_clk generator 26 | parameter SIM_TIMEOUT = 100000; // TB simulation time out 27 | parameter BURST_LENGHT = DEPTH; 28 | localparam MAX_DATA = 2**DATA_WIDTH - 1; 29 | integer fifo_expected [$]; 30 | integer i; 31 | integer err_cnt = 0; 32 | reg rd_clk = 0; 33 | reg wr_clk = 0; 34 | reg rst = 1; 35 | reg [DATA_WIDTH-1:0] data_wr = 0; 36 | reg wr_en = 0; 37 | reg rd_en = 0; 38 | wire [DATA_WIDTH-1:0] data_rd; 39 | wire fifo_empty; 40 | wire fifo_full; 41 | 42 | 43 | `ifdef XILINX_GLS 44 | glbl glbl (); // for Xilinx GLS 45 | `endif 46 | 47 | fifo #( 48 | .DEPTH(DEPTH), 49 | .DATA_WIDTH(DATA_WIDTH), 50 | .ASYNC(ASYNC), 51 | .RD_BUFFER(RD_BUFFER)) DUT ( 52 | /*input wire*/ .rd_clk(rd_clk), 53 | /*input wire*/ .wr_clk(wr_clk), 54 | /*input wire*/ .rst(rst), 55 | /*input wire [DATA_WIDTH-1:0]*/ .data_wr(data_wr), 56 | /*input wire*/ .wr_en(wr_en), 57 | /*output wire*/ .fifo_full(fifo_full), 58 | /*output logic [DATA_WIDTH-1:0]*/ .data_rd(data_rd), 59 | /*input wire*/ .rd_en(rd_en), 60 | /*output wire*/ .fifo_empty(fifo_empty)); 61 | 62 | always #(RD_CLK_PERIOD>>1) rd_clk = ~rd_clk; 63 | always #(WR_CLK_PERIOD>>1) wr_clk = ~wr_clk; 64 | 65 | 66 | integer data_wr_rand = 0; 67 | 68 | task fifo_write_burst_rand (input integer count); 69 | for (i=0; i 0) begin 105 | if(fifo_empty) begin 106 | $error("%0t FIFO is not empty but fifo_empty flag is asserted", $realtime); 107 | err_cnt = err_cnt + 1; 108 | end 109 | if (RD_BUFFER == 1) begin 110 | #1; 111 | end 112 | data_rd_exp = fifo_expected.pop_front(); 113 | data_rd_act <= data_rd; 114 | #1; //to make sure data_rd_act capture data_rd signal. 115 | if(data_rd_exp == data_rd_act) 116 | $display("%0t Data read = %d, FIFO entry = %d",$realtime, data_rd_act, $size(fifo_expected)); 117 | else begin 118 | $error("%0t Data read mismatch, ACT = %d, EXP =%d, FIFO entry = %d", $realtime, data_rd_act, data_rd_exp, $size(fifo_expected)); 119 | err_cnt = err_cnt + 1; 120 | end 121 | end else begin // check FIFO EMPTY flag 122 | #1; 123 | if(fifo_empty) 124 | $display("%0t FIFO is empty, fifo_empty flag is asserted correctly", $realtime); 125 | else begin 126 | $error("%0t FIFO is empty but fifo_empty flag is not asserted", $realtime); 127 | err_cnt = err_cnt + 1; 128 | end 129 | end 130 | end 131 | end 132 | // @(posedge(rd_clk)) 133 | rd_en <= 0; 134 | endtask 135 | 136 | integer op_count = 0; 137 | integer j = 0; 138 | bit op_sel = 0; 139 | 140 | task fifo_burst_rand(int count); 141 | j = count; 142 | while(j >= 0) begin 143 | op_sel = $urandom(); 144 | op_count = $urandom_range(1,j); // to have continuous request 145 | j = j - op_count; 146 | case(op_sel) 147 | 1: begin // read 148 | fifo_read_burst(op_count); 149 | #RD_CLK_PERIOD; 150 | #(3*WR_CLK_PERIOD); // It might take extra cycle for rd_pointer synchronization to deassert fifo_full, used for write operation 151 | end 152 | 0: begin // write 153 | fifo_write_burst_rand(op_count); 154 | #WR_CLK_PERIOD; 155 | #(3*RD_CLK_PERIOD); // It might take extra cycle for wr_pointer synchronization to deassert fifo_empty, used for read operation 156 | end 157 | endcase 158 | end 159 | endtask 160 | 161 | integer fifo_wr_stream [$]; 162 | integer fifo_rd_stream [$]; 163 | 164 | 165 | task fifo_read_write_rand_simul(); 166 | fifo_read_burst($size(fifo_expected)); // to make sure FIFO is empty 167 | fifo_wr_stream = {}; 168 | fifo_rd_stream = {}; 169 | for(i = 0; i < DEPTH; i = i+1) begin 170 | data_wr_rand = $urandom_range(0, MAX_DATA+1); 171 | fifo_wr_stream.push_back(data_wr_rand); 172 | end 173 | fork 174 | begin 175 | for (i=0; i 0) begin 247 | $display("\n%0t TEST FAILED",$realtime); 248 | $display("Error count = %d\n", err_cnt); 249 | end else 250 | $display("\n%0t TEST PASSED\n", $realtime); 251 | $finish; 252 | end 253 | 254 | initial begin 255 | #(SIM_TIMEOUT); 256 | $display("\n%0t TEST FAILED", $realtime); 257 | $display("SIM TIMEOUT!\n"); 258 | $finish; 259 | end 260 | 261 | endmodule 262 | -------------------------------------------------------------------------------- /FIFO/vip/pyuvm/README.md: -------------------------------------------------------------------------------- 1 | Development in Progress 2 | -------------------------------------------------------------------------------- /FIFO/vip/uvm/README.md: -------------------------------------------------------------------------------- 1 | # Development In Progress!! 2 | 3 | # FIFO (Queue) UVM VIP (Verification IP) User Guide 4 | ## 📁 Directory Structure 5 | 6 | ``` 7 | uvm/ 8 | ├── src/ # Core VIP source files 9 | │ ├── fifo_vip_pkg.sv # Main package + types/enums 10 | │ ├── fifo_vip_config.sv # Configuration class 11 | │ └── fifo_vip_seq_item.sv # Transaction definitions 12 | ├── agent/ # Agent layer components 13 | │ ├── fifo_vip_driver.sv # Driver implementation 14 | │ ├── fifo_vip_monitor.sv # Monitor implementation 15 | │ ├── fifo_vip_sequencer.sv # Sequencer (simple) 16 | │ └── fifo_vip_agent.sv # Agent wrapper 17 | ├── env/ # Environment layer 18 | │ ├── fifo_vip_env.sv # Environment 19 | │ └── fifo_vip_scoreboard.sv # Checking components 20 | ├── sequences/ # Test sequences 21 | │ ├── fifo_vip_base_seq.sv # Base sequence 22 | │ ├── fifo_vip_write_req_seq.sv # Write sequences 23 | │ └── fifo_vip_read_req_seq.sv # Read sequences 24 | ├── interface/ # Interface definition 25 | │ └── fifo_vip_if.sv # Virtual interface 26 | └── tb/ # Testbench 27 | ├── tests/ 28 | │ └── base_test.sv # Base test + simple_test 29 | └── tb_top.sv # Testbench top module 30 | ``` 31 | 32 | ## 🚀 Quick Start 33 | 34 | **Step 1:** Update Agent Interface with correct internal signals (refer to `fifo_vip_if.sv`) in your top level testbench: 35 | ```systemverilog 36 | // Update interface signal widths and connections in tb_top.sv 37 | fifo_vip_if dut_if; 38 | assign dut_if.rst = u_fifo.rst; 39 | assign dut_if.rd_clk = u_fifo.rd_clk; 40 | assign dut_if.wr_clk = u_fifo.wr_clk; 41 | assign dut_if.data_wr = u_fifo.data_wr; 42 | assign dut_if.wr_en = u_fifo.wr_en; 43 | assign dut_if.fifo_full = u_fifo.fifo_full; 44 | assign dut_if.data_rd = u_fifo.data_rd; 45 | assign dut_if.rd_en = u_fifo.rd_en; 46 | assign dut_if.fifo_empty = u_fifo.fifo_empty; 47 | 48 | // Set interface in config DB 49 | uvm_config_db#(virtual fifo_vip_if)::set(null, "*", "fifo_vip_vif", dut_if); 50 | ``` 51 | 52 | **Step 2:** Update DUT parameters/configuration (refer to `fifo_vip_config.sv`) in your test: 53 | ```systemverilog 54 | // In your test's build_phase() 55 | cfg = fifo_vip_config::type_id::create("cfg"); 56 | cfg.DEPTH = 12; // Match your FIFO depth 57 | cfg.DATA_WIDTH = 8; // Match your data width 58 | cfg.ASYNC = 1; // 1=async clocks, 0=sync 59 | cfg.RD_BUFFER = 1; // 1=buffered read, 0=combinational 60 | 61 | // Set config in database 62 | uvm_config_db#(fifo_vip_config)::set(this, "*", "fifo_vip_cfg", cfg); 63 | ``` 64 | 65 | **Step 3:** Agent instantiation in your environment (refer to `fifo_vip_env.sv`): 66 | ```systemverilog 67 | // Create FIFO VIP environment 68 | fifo_env = fifo_vip_env::type_id::create("fifo_env", this); 69 | ```` 70 | 71 | ##🚀 Available Sequences 72 | 73 | **Write Sequence:** 74 | ```systemverilog 75 | fifo_vip_write_req_seq wr_seq = fifo_vip_write_req_seq::type_id::create("wr_seq"); 76 | wr_seq.num_writes = 10; 77 | wr_seq.start(env.get_wr_sequencer()); 78 | ``` 79 | 80 | **Read Sequence:** 81 | ```systemverilog 82 | fifo_vip_read_req_seq rd_seq = fifo_vip_read_req_seq::type_id::create("rd_seq"); 83 | rd_seq.num_reads = 10; 84 | rd_seq.start(env.get_rd_sequencer()); 85 | ``` 86 | 87 | ## ✅ Self-Checking Features 88 | - ✅ Data integrity through FIFO 89 | - ✅ Write when full behavior 90 | - ✅ Read when empty behavior 91 | - ✅ FIFO flag correctness 92 | - ✅ Transaction success/failure 93 | 94 | ## 🚨 Common Issues & Solutions 95 | 96 | ### Future Work 97 | Add assertions to `fifo_vip_if.sv` or create a separate checker component. 98 | Add coverage 99 | 100 | **Happy Verifying! 🚀** 101 | 102 | *This VIP follows UVM best practices while keeping complexity minimal for ease of use and learning.* 103 | -------------------------------------------------------------------------------- /FIFO/vip/uvm/agent/fifo_vip_agent.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 08:57 PM 5 | // Module Name: fifo_vip_agent 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the FIFO VIP agent. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class fifo_vip_agent extends uvm_agent; 12 | `uvm_component_utils(fifo_vip_agent) 13 | 14 | fifo_vip_driver driver; 15 | fifo_vip_monitor monitor; 16 | fifo_vip_sequencer sequencer; 17 | 18 | uvm_analysis_port #(fifo_vip_seq_item) ap; 19 | 20 | function new(string name = "fifo_vip_agent", uvm_component parent = null); 21 | super.new(name, parent); 22 | endfunction 23 | 24 | function void build_phase(uvm_phase phase); 25 | super.build_phase(phase); 26 | 27 | monitor = fifo_vip_monitor::type_id::create("monitor", this); 28 | 29 | if (is_active == UVM_ACTIVE) begin 30 | driver = fifo_vip_driver::type_id::create("driver", this); 31 | sequencer = fifo_vip_sequencer::type_id::create("sequencer", this); 32 | end 33 | endfunction 34 | 35 | function void connect_phase(uvm_phase phase); 36 | super.connect_phase(phase); 37 | 38 | ap = monitor.ap; 39 | 40 | if (is_active == UVM_ACTIVE) begin 41 | driver.seq_item_port.connect(sequencer.seq_item_export); 42 | end 43 | endfunction 44 | 45 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/agent/fifo_vip_driver.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 08:45 PM 5 | // Module Name: fifo_vip_driver 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the FIFO VIP driver. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | class fifo_vip_driver extends uvm_driver #(fifo_vip_seq_item); 13 | `uvm_component_utils(fifo_vip_driver) 14 | 15 | virtual fifo_vip_if vif; 16 | fifo_vip_config cfg; 17 | string driver_type; // "WR" or "RD" 18 | 19 | function new(string name = "fifo_vip_driver", uvm_component parent = null); 20 | super.new(name, parent); 21 | endfunction 22 | 23 | function void build_phase(uvm_phase phase); 24 | if (!uvm_config_db#(virtual fifo_vip_if)::get(this, "", "vif", vif)) 25 | `uvm_fatal("DRV", "No virtual interface") 26 | if (!uvm_config_db#(fifo_vip_config)::get(this, "", "cfg", cfg)) 27 | `uvm_fatal("DRV", "No config") 28 | 29 | // Figure out if this is write or read driver 30 | driver_type = (get_name().substr(0,1) == "w") ? "WR" : "RD"; 31 | endfunction 32 | 33 | task run_phase(uvm_phase phase); 34 | fifo_vip_seq_item item; 35 | 36 | // Initialize 37 | if (driver_type == "WR") begin 38 | vif.wr_cb.wr_en <= 0; 39 | vif.wr_cb.data_wr <= 0; 40 | end else begin 41 | vif.rd_cb.rd_en <= 0; 42 | end 43 | 44 | // Wait for reset 45 | @(negedge vif.rst); 46 | @(posedge vif.wr_clk); 47 | 48 | forever begin 49 | seq_item_port.get_next_item(item); 50 | drive_item(item); 51 | seq_item_port.item_done(); 52 | end 53 | endtask 54 | 55 | task drive_item(fifo_vip_seq_item item); 56 | // Set config on item 57 | item.set_config(cfg); 58 | 59 | case (item.op) 60 | WRITE: if (driver_type == "WR") drive_write(item); 61 | READ: if (driver_type == "RD") drive_read(item); 62 | IDLE: repeat(2) @(vif.wr_cb); 63 | endcase 64 | endtask 65 | 66 | task drive_write(fifo_vip_seq_item item); 67 | @(vif.wr_cb); 68 | vif.wr_cb.data_wr <= item.data[cfg.DATA_WIDTH-1:0]; 69 | vif.wr_cb.wr_en <= 1; 70 | @(vif.wr_cb); 71 | item.full = vif.wr_cb.fifo_full; 72 | item.success = !vif.wr_cb.fifo_full; 73 | vif.wr_cb.wr_en <= 0; 74 | `uvm_info("WR_DRV", $sformatf("Write: %s", item.convert2string()), UVM_HIGH) 75 | endtask 76 | 77 | task drive_read(fifo_vip_seq_item item); 78 | @(vif.rd_cb); 79 | vif.rd_cb.rd_en <= 1; 80 | @(vif.rd_cb); 81 | item.empty = vif.rd_cb.fifo_empty; 82 | item.success = !vif.rd_cb.fifo_empty; 83 | if (cfg.RD_BUFFER) @(vif.rd_cb); // Wait extra cycle for buffered read 84 | item.read_data = vif.rd_cb.data_rd; 85 | vif.rd_cb.rd_en <= 0; 86 | `uvm_info("RD_DRV", $sformatf("Read: %s", item.convert2string()), UVM_HIGH) 87 | endtask 88 | 89 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/agent/fifo_vip_monitor.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 08:56 PM 5 | // Module Name: fifo_vip_monitor 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the FIFO VIP monitor. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class fifo_vip_monitor extends uvm_monitor; 12 | `uvm_component_utils(fifo_vip_monitor) 13 | 14 | virtual fifo_vip_if vif; 15 | fifo_vip_config cfg; 16 | uvm_analysis_port #(fifo_vip_seq_item) ap; 17 | string monitor_type; // "WR" or "RD" 18 | 19 | function new(string name = "fifo_vip_monitor", uvm_component parent = null); 20 | super.new(name, parent); 21 | ap = new("ap", this); 22 | endfunction 23 | 24 | function void build_phase(uvm_phase phase); 25 | if (!uvm_config_db#(virtual fifo_vip_if)::get(this, "", "fifo_vip_vif", vif)) 26 | `uvm_fatal("MON", "No virtual interface") 27 | if (!uvm_config_db#(fifo_vip_config)::get(this, "", "fifo_vip_cfg", cfg)) 28 | `uvm_fatal("MON", "No config") 29 | 30 | // Figure out monitor type 31 | monitor_type = (get_name().substr(0,1) == "w") ? "WR" : "RD"; 32 | endfunction 33 | 34 | task run_phase(uvm_phase phase); 35 | @(negedge vif.rst); 36 | 37 | if (monitor_type == "WR") begin 38 | monitor_writes(); 39 | end else begin 40 | monitor_reads(); 41 | end 42 | endtask 43 | 44 | task monitor_writes(); 45 | fifo_vip_seq_item item; 46 | forever begin 47 | @(posedge vif.wr_clk); 48 | if (vif.wr_en && !vif.rst) begin 49 | item = fifo_vip_seq_item::type_id::create("wr_item"); 50 | item.op = WRITE; 51 | item.data = vif.data_wr; 52 | item.full = vif.fifo_full; 53 | item.success = !vif.fifo_full; 54 | ap.write(item); 55 | `uvm_info("WR_MON", $sformatf("Monitored: %s", item.convert2string()), UVM_HIGH) 56 | end 57 | end 58 | endtask 59 | 60 | task monitor_reads(); 61 | fifo_vip_seq_item item; 62 | forever begin 63 | @(posedge vif.rd_clk); 64 | if (vif.rd_en && !vif.rst) begin 65 | item = fifo_vip_seq_item::type_id::create("rd_item"); 66 | item.op = READ; 67 | item.empty = vif.fifo_empty; 68 | item.success = !vif.fifo_empty; 69 | if (cfg.RD_BUFFER) @(posedge vif.rd_clk); // Wait for buffered read 70 | item.read_data = vif.data_rd; 71 | ap.write(item); 72 | `uvm_info("RD_MON", $sformatf("Monitored: %s", item.convert2string()), UVM_HIGH) 73 | end 74 | end 75 | endtask 76 | 77 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/agent/fifo_vip_sequencer.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 09:25 PM 5 | // Module Name: fifo_vip_sequencer 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains all the components of the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | class fifo_vip_sequencer extends uvm_sequencer #(fifo_vip_seq_item); 13 | `uvm_component_utils(fifo_vip_sequencer) 14 | 15 | function new(string name = "fifo_vip_sequencer", uvm_component parent = null); 16 | super.new(name, parent); 17 | endfunction 18 | 19 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/common/fifo_vip_config.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 08:41 PM 5 | // Module Name: fifo_vip_config 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the configuration class for the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class fifo_vip_config extends uvm_object; 12 | `uvm_object_utils(fifo_vip_config) 13 | 14 | // DUT parameters - CHANGE THESE FOR YOUR FIFO 15 | int DEPTH = 12; 16 | int DATA_WIDTH = 8; 17 | bit ASYNC = 1; 18 | bit RD_BUFFER = 1; 19 | 20 | // VIP control 21 | bit has_wr_agent = 1; 22 | bit has_rd_agent = 1; 23 | bit enable_scoreboard = 1; 24 | 25 | // Agent modes 26 | fifo_agent_mode_e wr_agent_mode = MASTER; 27 | fifo_agent_mode_e rd_agent_mode = MASTER; 28 | 29 | function new(string name = "fifo_vip_config"); 30 | super.new(name); 31 | endfunction 32 | 33 | function void print_config(); 34 | `uvm_info("CFG", $sformatf("DEPTH=%0d, DATA_WIDTH=%0d, ASYNC=%0b, RD_BUFFER=%0b", 35 | DEPTH, DATA_WIDTH, ASYNC, RD_BUFFER), UVM_LOW) 36 | endfunction 37 | 38 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/common/fifo_vip_pkg.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 10:37 PM 5 | // Module Name: fifo_vip_pkg 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains all the components of the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | package fifo_vip_pkg; 12 | 13 | import uvm_pkg::*; 14 | `include "uvm_macros.svh" 15 | 16 | // Types and enums defined directly in package 17 | typedef enum { 18 | WRITE, 19 | READ, 20 | IDLE 21 | } fifo_op_e; 22 | 23 | typedef enum { 24 | MASTER, 25 | SLAVE, 26 | MONITOR_ONLY 27 | } fifo_agent_mode_e; 28 | 29 | // Include files in order 30 | `include "../src/fifo_vip_config.sv" 31 | `include "../src/fifo_vip_seq_item.sv" 32 | `include "../agent/fifo_vip_driver.sv" 33 | `include "../agent/fifo_vip_monitor.sv" 34 | `include "../agent/fifo_vip_sequencer.sv" 35 | `include "../agent/fifo_vip_agent.sv" 36 | `include "../env/fifo_vip_scoreboard.sv" 37 | `include "../env/fifo_vip_env.sv" 38 | `include "../sequences/fifo_vip_base_seq.sv" 39 | `include "../sequences/fifo_vip_write_req_seq.sv" 40 | `include "../sequences/fifo_vip_read_req_seq.sv" 41 | 42 | endpackage -------------------------------------------------------------------------------- /FIFO/vip/uvm/common/fifo_vip_seq_item.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 08:37 PM 5 | // Module Name: fifo_vip_seq_item 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the sequence item for the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class fifo_vip_seq_item extends uvm_sequence_item; 12 | `uvm_object_utils(fifo_vip_seq_item) 13 | 14 | // Transaction fields 15 | rand fifo_op_e op; 16 | rand bit [31:0] data; 17 | 18 | // Response fields 19 | bit [31:0] read_data; 20 | bit full; 21 | bit empty; 22 | bit success; 23 | 24 | // Config reference 25 | fifo_vip_config cfg; 26 | 27 | // Simple constraints 28 | constraint op_dist { 29 | op dist {WRITE := 50, READ := 50}; 30 | } 31 | 32 | constraint data_c { 33 | if (cfg != null) { 34 | data < (1 << cfg.DATA_WIDTH); 35 | } else { 36 | data < 256; // 8-bit default 37 | } 38 | } 39 | 40 | function new(string name = "fifo_vip_seq_item"); 41 | super.new(name); 42 | success = 1; 43 | endfunction 44 | 45 | function string convert2string(); 46 | return $sformatf("Op:%s Data:0x%0h ReadData:0x%0h Full:%0b Empty:%0b Success:%0b", 47 | op.name(), data, read_data, full, empty, success); 48 | endfunction 49 | 50 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/env/fifo_vip_env.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 08:57 PM 5 | // Module Name: fifo_vip_env 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the FIFO VIP environment. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | class fifo_vip_env extends uvm_env; 11 | `uvm_component_utils(fifo_vip_env) 12 | 13 | fifo_vip_config cfg; 14 | fifo_vip_agent wr_agent; 15 | fifo_vip_agent rd_agent; 16 | fifo_vip_scoreboard sb; 17 | 18 | function new(string name = "fifo_vip_env", uvm_component parent = null); 19 | super.new(name, parent); 20 | endfunction 21 | 22 | function void build_phase(uvm_phase phase); 23 | super.build_phase(phase); 24 | 25 | if (!uvm_config_db#(fifo_vip_config)::get(this, "", "fifo_vip_cfg", cfg)) 26 | `uvm_fatal("ENV", "No config") 27 | 28 | // Set config for all components 29 | uvm_config_db#(fifo_vip_config)::set(this, "*", "fifo_vip_cfg", cfg); 30 | 31 | // Create agents 32 | if (cfg.has_wr_agent) begin 33 | wr_agent = fifo_vip_agent::type_id::create("wr_agent", this); 34 | end 35 | 36 | if (cfg.has_rd_agent) begin 37 | rd_agent = fifo_vip_agent::type_id::create("rd_agent", this); 38 | end 39 | 40 | // Create scoreboard 41 | if (cfg.enable_scoreboard) begin 42 | sb = fifo_vip_scoreboard::type_id::create("sb", this); 43 | end 44 | endfunction 45 | 46 | function void connect_phase(uvm_phase phase); 47 | super.connect_phase(phase); 48 | 49 | if (cfg.enable_scoreboard && sb != null) begin 50 | if (wr_agent != null) wr_agent.ap.connect(sb.wr_imp); 51 | if (rd_agent != null) rd_agent.ap.connect(sb.rd_imp); 52 | end 53 | endfunction 54 | 55 | // Helper functions for tests 56 | function fifo_vip_sequencer get_wr_sequencer(); 57 | return (wr_agent != null) ? wr_agent.sequencer : null; 58 | endfunction 59 | 60 | function fifo_vip_sequencer get_rd_sequencer(); 61 | return (rd_agent != null) ? rd_agent.sequencer : null; 62 | endfunction 63 | 64 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/env/fifo_vip_scoreboard.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 09:01 PM 5 | // Module Name: fifo_vip_scoreboard.sv 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the FIFO VIP scoreboard. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class fifo_vip_scoreboard extends uvm_scoreboard; 12 | `uvm_component_utils(fifo_vip_scoreboard) 13 | 14 | uvm_analysis_imp_wr #(fifo_vip_seq_item, fifo_vip_scoreboard) wr_imp; 15 | uvm_analysis_imp_rd #(fifo_vip_seq_item, fifo_vip_scoreboard) rd_imp; 16 | 17 | // Simple queue model 18 | int fifo_model[$]; 19 | int errors = 0; 20 | 21 | function new(string name = "fifo_vip_scoreboard", uvm_component parent = null); 22 | super.new(name, parent); 23 | wr_imp = new("wr_imp", this); 24 | rd_imp = new("rd_imp", this); 25 | endfunction 26 | 27 | function void write_wr(fifo_vip_seq_item item); 28 | if (item.op == WRITE && item.success) begin 29 | fifo_model.push_back(item.data); 30 | `uvm_info("SB", $sformatf("Write: data=0x%0h, queue_size=%0d", item.data, fifo_model.size()), UVM_MEDIUM) 31 | end 32 | endfunction 33 | 34 | function void write_rd(fifo_vip_seq_item item); 35 | if (item.op == READ && item.success) begin 36 | if (fifo_model.size() > 0) begin 37 | int expected = fifo_model.pop_front(); 38 | if (item.read_data == expected) begin 39 | `uvm_info("SB", $sformatf("Read OK: data=0x%0h, queue_size=%0d", item.read_data, fifo_model.size()), UVM_MEDIUM) 40 | end else begin 41 | `uvm_error("SB", $sformatf("Data mismatch! Expected:0x%0h Got:0x%0h", expected, item.read_data)) 42 | errors++; 43 | end 44 | end else begin 45 | `uvm_error("SB", "Read from empty FIFO model") 46 | errors++; 47 | end 48 | end 49 | endfunction 50 | 51 | function void report_phase(uvm_phase phase); 52 | if (errors == 0) 53 | `uvm_info("SB", "*** TEST PASSED ***", UVM_LOW) 54 | else 55 | `uvm_error("SB", $sformatf("*** TEST FAILED - %0d errors ***", errors)) 56 | endfunction 57 | 58 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/interface/fifo_vip_if.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 09:28 PM 5 | // Module Name: fifo_vip_if 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the FIFO VIP interface. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | interface fifo_vip_if(input logic wr_clk, input logic rd_clk); 13 | 14 | // Signals matching your FIFO 15 | logic rst; 16 | logic [7:0] data_wr; 17 | logic wr_en; 18 | logic fifo_full; 19 | logic [7:0] data_rd; 20 | logic rd_en; 21 | logic fifo_empty; 22 | 23 | // Simple clocking blocks 24 | clocking wr_cb @(posedge wr_clk); 25 | output data_wr, wr_en; 26 | input fifo_full; 27 | endclocking 28 | 29 | clocking rd_cb @(posedge rd_clk); 30 | output rd_en; 31 | input data_rd, fifo_empty; 32 | endclocking 33 | 34 | // Modports 35 | modport wr_drv (clocking wr_cb, input rst); 36 | modport rd_drv (clocking rd_cb, input rst); 37 | 38 | endinterface -------------------------------------------------------------------------------- /FIFO/vip/uvm/sequences/fifo_vip_base_seq.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 09:27 PM 5 | // Module Name: fifo_vip_base_seq 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This sequence serves as a base class for FIFO VIP sequences. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class fifo_vip_base_seq extends uvm_sequence #(fifo_vip_seq_item); 12 | `uvm_object_utils(fifo_vip_base_seq) 13 | 14 | fifo_vip_config cfg; 15 | 16 | function new(string name = "fifo_vip_base_seq"); 17 | super.new(name); 18 | endfunction 19 | 20 | task pre_body(); 21 | if (!uvm_config_db#(fifo_vip_config)::get(m_sequencer, "", "fifo_vip_cfg", cfg)) begin 22 | `uvm_warning("SEQ", "No config found") 23 | end 24 | endtask 25 | 26 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/sequences/fifo_vip_read_req_seq.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 09:04 PM 5 | // Module Name: fifo_vip_read_req_seq 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This sequence generates read requests for the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class fifo_vip_read_req_seq extends fifo_vip_base_seq; 12 | `uvm_object_utils(fifo_vip_read_req_seq) 13 | 14 | rand int num_reads; 15 | 16 | constraint num_reads_c { 17 | num_reads inside {[1:20]}; 18 | } 19 | 20 | function new(string name = "fifo_vip_read_req_seq"); 21 | super.new(name); 22 | endfunction 23 | 24 | task body(); 25 | fifo_vip_seq_item item; 26 | 27 | `uvm_info("RD_SEQ", $sformatf("Starting %0d reads", num_reads), UVM_MEDIUM) 28 | 29 | repeat(num_reads) begin 30 | item = fifo_vip_seq_item::type_id::create("item"); 31 | start_item(item); 32 | assert(item.randomize() with {op == READ;}); 33 | finish_item(item); 34 | end 35 | endtask 36 | 37 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/sequences/fifo_vip_write_req_seq.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 09:25 PM 5 | // Module Name: fifo_vip_write_req_seq 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This sequence generates write requests for the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | class fifo_vip_write_req_seq extends fifo_vip_base_seq; 13 | `uvm_object_utils(fifo_vip_write_req_seq) 14 | 15 | rand int num_writes; 16 | 17 | constraint num_writes_c { 18 | num_writes inside {[1:20]}; 19 | } 20 | 21 | function new(string name = "fifo_vip_write_req_seq"); 22 | super.new(name); 23 | endfunction 24 | 25 | task body(); 26 | fifo_vip_seq_item item; 27 | 28 | `uvm_info("WR_SEQ", $sformatf("Starting %0d writes", num_writes), UVM_MEDIUM) 29 | 30 | repeat(num_writes) begin 31 | item = fifo_vip_seq_item::type_id::create("item"); 32 | start_item(item); 33 | assert(item.randomize() with {op == WRITE;}); 34 | finish_item(item); 35 | end 36 | endtask 37 | 38 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/sim/tb_top.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 09:04 PM 5 | // Module Name: tb_top 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This is the top-level testbench for the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | 12 | `timescale 1ns/1ps 13 | 14 | module tb_top; 15 | import uvm_pkg::*; 16 | import fifo_vip_pkg::*; 17 | 18 | // Clocks 19 | logic wr_clk = 0; 20 | logic rd_clk = 0; 21 | 22 | // Clock generation - MODIFY PERIODS AS NEEDED 23 | always #10 wr_clk = ~wr_clk; // 50MHz 24 | always #16 rd_clk = ~rd_clk; // 31.25MHz 25 | 26 | // Interface 27 | fifo_vip_if dut_if(wr_clk, rd_clk); 28 | 29 | // Reset 30 | initial begin 31 | dut_if.rst = 1; 32 | repeat(5) @(posedge wr_clk); 33 | dut_if.rst = 0; 34 | end 35 | 36 | // DUT instantiation - MODIFY FOR YOUR FIFO 37 | fifo #( 38 | .DEPTH(12), 39 | .DATA_WIDTH(8), 40 | .ASYNC(1), 41 | .RD_BUFFER(1) 42 | ) dut ( 43 | .rd_clk(rd_clk), 44 | .wr_clk(wr_clk), 45 | .rst(dut_if.rst), 46 | .data_wr(dut_if.data_wr), 47 | .wr_en(dut_if.wr_en), 48 | .fifo_full(dut_if.fifo_full), 49 | .data_rd(dut_if.data_rd), 50 | .rd_en(dut_if.rd_en), 51 | .fifo_empty(dut_if.fifo_empty) 52 | ); 53 | 54 | // UVM testbench 55 | initial begin 56 | uvm_config_db#(virtual fifo_vip_if)::set(null, "*", "fifo_vip_vif", dut_if); 57 | 58 | $dumpfile("waves.vcd"); 59 | $dumpvars(0, tb_top); 60 | 61 | run_test(); 62 | end 63 | 64 | // Timeout 65 | initial begin 66 | #50us; 67 | $finish; 68 | end 69 | 70 | endmodule -------------------------------------------------------------------------------- /FIFO/vip/uvm/sim/tests/fifo_vip_base_test.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 10:04 PM 5 | // Module Name: fifo_vip_base_test 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the base test for the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class base_test extends uvm_test; 12 | `uvm_component_utils(base_test) 13 | 14 | fifo_vip_env env; 15 | fifo_vip_config cfg; 16 | 17 | function new(string name = "base_test", uvm_component parent = null); 18 | super.new(name, parent); 19 | endfunction 20 | 21 | function void build_phase(uvm_phase phase); 22 | super.build_phase(phase); 23 | 24 | // Create config - MODIFY FOR YOUR FIFO 25 | cfg = fifo_vip_config::type_id::create("cfg"); 26 | cfg.DEPTH = 12; // Change this 27 | cfg.DATA_WIDTH = 8; // Change this 28 | cfg.ASYNC = 1; // Change this 29 | cfg.RD_BUFFER = 1; // Change this 30 | 31 | // Set config in database with better field name 32 | uvm_config_db#(fifo_vip_config)::set(this, "*", "fifo_vip_cfg", cfg); 33 | 34 | env = fifo_vip_env::type_id::create("env", this); 35 | endfunction 36 | 37 | function void end_of_elaboration_phase(uvm_phase phase); 38 | cfg.print_config(); 39 | uvm_top.print_topology(); 40 | endfunction 41 | 42 | endclass -------------------------------------------------------------------------------- /FIFO/vip/uvm/sim/tests/fifo_vip_simple_test.sv: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Create Date: 05/24/2024 03:37 PM 4 | // Last Update Date: 05/24/2024 10:04 PM 5 | // Module Name: fifo_vip_base_test 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: This package contains the base test for the FIFO VIP. 8 | // 9 | ////////////////////////////////////////////////////////////////////////////////// 10 | 11 | class simple_test extends base_test; 12 | `uvm_component_utils(simple_test) 13 | 14 | function new(string name = "simple_test", uvm_component parent = null); 15 | super.new(name, parent); 16 | endfunction 17 | 18 | task run_phase(uvm_phase phase); 19 | fifo_vip_write_req_seq wr_seq; 20 | fifo_vip_read_req_seq rd_seq; 21 | 22 | phase.raise_objection(this); 23 | 24 | // Write some data 25 | wr_seq = fifo_vip_write_req_seq::type_id::create("wr_seq"); 26 | wr_seq.num_writes = 8; 27 | wr_seq.start(env.get_wr_sequencer()); 28 | 29 | #200ns; 30 | 31 | // Read it back 32 | rd_seq = fifo_vip_read_req_seq::type_id::create("rd_seq"); 33 | rd_seq.num_reads = 8; 34 | rd_seq.start(env.get_rd_sequencer()); 35 | 36 | #200ns; 37 | phase.drop_objection(this); 38 | endtask 39 | 40 | endclass -------------------------------------------------------------------------------- /Hash_Table/README.md: -------------------------------------------------------------------------------- 1 | # Hash Table Module 2 | ## Overview 3 | This Verilog module implements a flexible hash table with configurable collision resolution methods and hash algorithms. It provides support for key-value storage with operations such as insert, delete, and search. 4 | 5 | ## Features 6 | - Configurable key and value widths 7 | - Support for different collision resolution methods 8 | - Multiple hash algorithm options 9 | - Synchronous operation with reset capability 10 | - Collision tracking and error reporting 11 | 12 | ## Parameters 13 | | Parameter | Description | Default | 14 | |-----------|-------------|---------| 15 | | KEY_WIDTH | Width of keys in bits | 32 | 16 | | VALUE_WIDTH | Width of values in bits | 32 | 17 | | TOTAL_ENTRY | Number of indices in the hash table | 64 | 18 | | CHAINING_SIZE | Maximum chain length for collision resolution | 4 | 19 | | COLLISION_METHOD | Method for handling collisions | "MULTI_STAGE_CHAINING" | 20 | | HASH_ALGORITHM | Algorithm used for hashing | "MODULUS" | 21 | 22 | ## Port Descriptions 23 | ### Input Ports 24 | | Port | Width | Description | 25 | |------|-------|-------------| 26 | | clk | 1 | System clock signal | 27 | | rst | 1 | Active-high reset signal | 28 | | key_in | KEY_WIDTH | Key for insert, delete, or search operations | 29 | | value_in | VALUE_WIDTH | Value to be stored (for insert operations) | 30 | | op_sel | 2 | Operation selector (00: Insert, 01: Delete, 10: Search) | 31 | | op_en | 1 | Operation enable signal | 32 | 33 | ### Output Ports 34 | | Port | Width | Description | 35 | |------|-------|-------------| 36 | | value_out | VALUE_WIDTH | Value retrieved during search operations | 37 | | op_done | 1 | Operation completion indicator | 38 | | op_error | 1 | Error indicator (FULL for insert, KEY_NOT_FOUND for delete/search) | 39 | | collision_count | log2(CHAINING_SIZE) | Number of collisions encountered | 40 | 41 | ## Collision Methods 42 | - **MULTI_STAGE_CHAINING**: Multiple entries at the same index using a linked list approach 43 | - **LINEAR_PROBING**: Referenced in parameters but not fully implemented in the provided code 44 | 45 | ## Hash Algorithms 46 | - **MODULUS**: Simple modulus operation (key % TABLE_SIZE) 47 | - **SHA1**, **FNV1A**: Referenced in parameters but not fully implemented in the provided code 48 | 49 | ## Timing 50 | - All operations are synchronized to the positive edge of the clock 51 | - Reset is asynchronous and active-high 52 | - Operations are initiated when op_en is asserted 53 | - op_done indicates completion of an operation 54 | 55 | ## Usage Example 56 | ```verilog 57 | hash_table #( 58 | .KEY_WIDTH(32), 59 | .VALUE_WIDTH(32), 60 | .TOTAL_ENTRY(128), 61 | .CHAINING_SIZE(8), 62 | .COLLISION_METHOD("MULTI_STAGE_CHAINING"), 63 | .HASH_ALGORITHM("MODULUS") 64 | ) hash_module ( 65 | .clk(system_clk), 66 | .rst(system_rst), 67 | .key_in(key), 68 | .value_in(value), 69 | .op_sel(operation), 70 | .op_en(enable), 71 | .value_out(retrieved_value), 72 | .op_done(operation_complete), 73 | .op_error(operation_error), 74 | .collision_count(collisions) 75 | ); 76 | ``` 77 | 78 | ## Implementation Details 79 | - Uses a state machine for operation control 80 | - Implements chained hash entries for collision resolution 81 | - Provides error reporting for table overflow or key not found conditions 82 | - Uses Verilog parameter-based configuration for flexibility 83 | 84 | ## Limitations and Issues 85 | - Implementation for alternative hash algorithms is mentioned but not provided 86 | 87 | ## Performance Considerations 88 | - Search and delete operations may require multiple cycles depending on chain length 89 | - Performance degrades as collision chains grow longer 90 | - No optimization for locality or cache behavior 91 | - Please consider using [CAM(Content Addressable Memory)](https://en.wikipedia.org/wiki/Content-addressable_memory) if you have the resources. 92 | -------------------------------------------------------------------------------- /Hash_Table/src/hash_table.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Create Date: 18/04/2025 1:15 AM 4 | // Last Update: 03/05/2025 6:20 PM 5 | // Module Name: Hash Table 6 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 7 | // Description: Support coliision method of Chaining and Hash Algorithm FNV1A and SHA1 8 | // Additional Comments: . 9 | // 10 | ////////////////////////////////////////////////////////////////////////////////// 11 | 12 | module hash_table #( 13 | parameter KEY_WIDTH = 32, 14 | parameter VALUE_WIDTH = 32, 15 | parameter TOTAL_INDEX = 8, // Total index of the hash table 16 | parameter CHAINING_SIZE = 4, // Number of chains for SINGLE_CYCLE_CHAINING and MULTI_STAGE_CHAINING only 17 | parameter COLLISION_METHOD = "MULTI_STAGE_CHAINING", // "MULTI_STAGE_CHAINING", "LINEAR_PROBING" 18 | parameter HASH_ALGORITHM = "MODULUS" // "MODULUS", "SHA1", "FNV1A" 19 | )( 20 | 21 | input wire clk, 22 | input wire rst, 23 | input wire [KEY_WIDTH-1:0] key_in, 24 | input wire [VALUE_WIDTH-1:0] value_in, 25 | input wire [1:0] op_sel, // 00: Insert, 01: Delete, 10: Search 26 | input wire op_en, 27 | output reg [VALUE_WIDTH-1:0] value_out, 28 | output reg op_done, 29 | output reg op_error, // FULL when insert FAIL, KEY_NOT_FOUND when delete or search FAIL 30 | output reg [$clog2(CHAINING_SIZE-1)-1:0] collision_count 31 | ); 32 | 33 | parameter CHAINING_SIZE_WIDTH = $clog2(CHAINING_SIZE+1); 34 | parameter INDEX_WIDTH = $clog2(TOTAL_INDEX); 35 | 36 | reg [2:0] current_state; 37 | //reg [2:0] next_state; 38 | reg [KEY_WIDTH*CHAINING_SIZE-1:0] hash_key_stored [0:TOTAL_INDEX-1]; 39 | reg [VALUE_WIDTH*CHAINING_SIZE-1:0] hash_value_stored [0:TOTAL_INDEX-1]; 40 | reg [CHAINING_SIZE_WIDTH-1:0] hash_chain_count [0:TOTAL_INDEX-1]; // for collision count 41 | reg [CHAINING_SIZE_WIDTH-1:0] search_ptr; // for searching the key in the chain 42 | reg [CHAINING_SIZE_WIDTH-1:0] next_search_ptr; // for searching the key in the chain 43 | integer i; 44 | 45 | localparam IDLE = 3'b000; 46 | localparam SEARCH_KEY = 3'b001; 47 | localparam INSERT = 3'b010; 48 | localparam DELETE = 3'b011; 49 | localparam READ = 3'b100; 50 | localparam OP_DONE = 3'b101; 51 | 52 | // Hash function selector 53 | function integer get_hash_index; 54 | input integer key; 55 | reg [31:0] hash_value; 56 | begin 57 | if (HASH_ALGORITHM == "MODULUS") 58 | hash_value = key % TOTAL_INDEX; 59 | else // for future implentation of other hash algorithm 60 | hash_value = key; 61 | 62 | get_hash_index = hash_value[INDEX_WIDTH-1:0]; 63 | end 64 | endfunction 65 | 66 | // Collision resolution method 67 | always @ (posedge clk, posedge rst) begin 68 | if(rst) begin 69 | current_state <= IDLE; 70 | search_ptr <= 0; 71 | value_out <= 0; 72 | collision_count <= 0; 73 | op_done <= 0; 74 | op_error <= 0; 75 | for (i = 0; i < TOTAL_INDEX; i++) begin 76 | hash_key_stored[i] <= 0; 77 | hash_value_stored[i] <= 0; 78 | hash_chain_count[i] <= 0; 79 | end 80 | end else begin 81 | case(current_state) 82 | IDLE: begin 83 | if (op_en) begin 84 | case (op_sel) 85 | 2'b00: current_state <= SEARCH_KEY; //INSERT 86 | 2'b01: current_state <= SEARCH_KEY; //DELETE 87 | 2'b10: current_state <= SEARCH_KEY; //SEARCH 88 | default: begin 89 | current_state <= OP_DONE; 90 | op_done <= 1; 91 | op_error <= 1; // Invalid operation 92 | end 93 | endcase 94 | end else begin 95 | current_state <= IDLE; 96 | op_done <= 0; 97 | op_error <= 0; 98 | end 99 | search_ptr <= 0; 100 | value_out <= 0; 101 | collision_count <= 0; 102 | end 103 | 104 | INSERT: begin 105 | // Insert logic here 106 | hash_key_stored[get_hash_index(key_in)][search_ptr*KEY_WIDTH +: KEY_WIDTH] <= key_in; 107 | hash_value_stored[get_hash_index(key_in)][search_ptr*VALUE_WIDTH +: VALUE_WIDTH] <= value_in; 108 | hash_chain_count[get_hash_index(key_in)] <= hash_chain_count[get_hash_index(key_in)] + 1; 109 | op_done <= 1; 110 | op_error <= 0; // No error 111 | current_state <= OP_DONE; 112 | end 113 | 114 | DELETE: begin 115 | // Delete logic here 116 | // Remove key and value from hash table 117 | // Shift the rest of the chain 118 | // use for loop instead 119 | for (i = 0; i < CHAINING_SIZE - 1; i++) begin 120 | if (i >= search_ptr) begin 121 | hash_key_stored[get_hash_index(key_in)][(i*KEY_WIDTH) +: KEY_WIDTH] <= hash_key_stored[get_hash_index(key_in)][((i+1)*KEY_WIDTH) +: KEY_WIDTH]; 122 | hash_value_stored[get_hash_index(key_in)][(i*VALUE_WIDTH) +: VALUE_WIDTH] <= hash_value_stored[get_hash_index(key_in)][((i+1)*VALUE_WIDTH) +: VALUE_WIDTH]; 123 | end else begin 124 | hash_key_stored[get_hash_index(key_in)][(i*KEY_WIDTH) +: KEY_WIDTH] <= {KEY_WIDTH{1'b0}}; 125 | hash_value_stored[get_hash_index(key_in)][(i*VALUE_WIDTH) +: VALUE_WIDTH] <= {VALUE_WIDTH{1'b0}}; 126 | end 127 | end 128 | hash_chain_count[get_hash_index(key_in)] <= hash_chain_count[get_hash_index(key_in)] - 1; 129 | current_state <= OP_DONE; 130 | op_done <= 1; 131 | op_error <= 0; // No error 132 | end 133 | 134 | READ: begin 135 | // Search logic here 136 | // Return the value associated with the key 137 | value_out <= hash_value_stored[get_hash_index(key_in)] >> search_ptr*VALUE_WIDTH; 138 | collision_count <= hash_chain_count[get_hash_index(key_in)]; 139 | current_state <= OP_DONE; 140 | op_done <= 1; 141 | op_error <= 0; // No error 142 | end 143 | 144 | SEARCH_KEY: begin 145 | if (hash_chain_count[get_hash_index(key_in)] == 0) begin 146 | if(op_sel == 2'b00) begin 147 | current_state <= INSERT; 148 | end else begin 149 | collision_count <= 0; 150 | op_done <= 1; 151 | op_error <= 1; // KEY_NOT_FOUND error 152 | current_state <= OP_DONE; 153 | end 154 | end else if(key_in == hash_key_stored[get_hash_index(key_in)][search_ptr*KEY_WIDTH +: KEY_WIDTH]) begin 155 | if(op_sel == 2'b00) begin 156 | current_state <= INSERT; 157 | end else if (op_sel == 2'b01) begin 158 | current_state <= DELETE; 159 | end else if (op_sel == 2'b10) begin 160 | current_state <= READ; 161 | end 162 | end else if (search_ptr == hash_chain_count[get_hash_index(key_in)]) begin 163 | if(op_sel == 2'b00 & (hash_chain_count[get_hash_index(key_in)] < CHAINING_SIZE)) begin 164 | current_state <= INSERT; 165 | end else begin 166 | collision_count <= CHAINING_SIZE; 167 | op_done <= 1; 168 | op_error <= 1; // KEY_NOT_FOUND error 169 | current_state <= OP_DONE; 170 | end 171 | end else begin 172 | current_state <= SEARCH_KEY; 173 | search_ptr <= search_ptr + 1; 174 | end 175 | end 176 | 177 | OP_DONE: begin 178 | current_state <= IDLE; 179 | op_done <= 0; 180 | op_error <= 0; 181 | end 182 | 183 | default: current_state <= IDLE; 184 | endcase 185 | end 186 | end 187 | 188 | endmodule 189 | -------------------------------------------------------------------------------- /Hash_Table/src/rtl_list.f: -------------------------------------------------------------------------------- 1 | hash_table.sv 2 | -------------------------------------------------------------------------------- /Hash_Table/tb/cocotb/Makefile: -------------------------------------------------------------------------------- 1 | SIM ?= icarus 2 | TOPLEVEL_LANG ?= verilog 3 | WAVES ?= 1 #set 1 to enable waveform dump. 4 | 5 | PWD=$(shell pwd) 6 | 7 | #export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) 8 | 9 | VERILOG_SOURCES = $(PWD)/../../src/hash_table.sv 10 | 11 | # DUT Top 12 | TOPLEVEL = hash_table 13 | # top python file name 14 | MODULE = tb 15 | 16 | #use , separtor to run multiple TESTCASE, by default all @cocotb.test will be run 17 | #TESTCASE = index_op_test , addr_op_test 18 | COMPILE_ARGS = -Phash_table.KEY_WIDTH=32 # DUT parameter #"-p" (parameter) iverilog command flags 19 | COMPILE_ARGS += -Phash_table.VALUE_WIDTH=32 # DUT parameter #"-p" (parameter) iverilog command flags 20 | COMPILE_ARGS += -Phash_table.TOTAL_INDEX=8 # DUT parameter #"-p" (parameter) iverilog command flags 21 | COMPILE_ARGS += -Phash_table.CHAINING_SIZE=4 # DUT parameter #"-p" (parameter) iverilog command flags 22 | #COMPILE_ARGS += -Phash_table.COLLISION_METHOD="MULTI_STAGE_CHAINING" # DUT parameter #"-p" (parameter) iverilog command flags 23 | #COMPILE_ARGS += -Phash_table.HASH_ALGORITHM="MODULUS" # DUT parameter #"-p" (parameter) iverilog command flags 24 | #run make clean before running with new parameter. 25 | 26 | #Set RANDOM_SEED number 27 | #PLUSARGS = +seed=1716033254 28 | COCOTB_HDL_TIMEUNIT = 1ns 29 | COCOTB_HDL_TIMEPRECISION = 1ps 30 | 31 | ifeq ($(SIM), icarus) 32 | $(shell echo 'module iverilog_dump();' > iverilog_dump.v) 33 | $(shell echo 'initial begin' >> iverilog_dump.v) 34 | $(shell echo ' $$dumpfile("$(TOPLEVEL).vcd");' >> iverilog_dump.v) 35 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 36 | $(shell echo 'end' >> iverilog_dump.v) 37 | $(shell echo 'endmodule' >> iverilog_dump.v) 38 | VERILOG_SOURCES += $(PWD)/iverilog_dump.v 39 | COMPILE_ARGS += -s iverilog_dump 40 | endif 41 | 42 | include $(shell cocotb-config --makefiles)/Makefile.sim 43 | 44 | 45 | -------------------------------------------------------------------------------- /Hash_Table/tb/cocotb/tb.py: -------------------------------------------------------------------------------- 1 | import random 2 | import asyncio 3 | import math 4 | import cocotb 5 | import cocotb.result 6 | from cocotb.triggers import Timer, RisingEdge, ReadOnly, with_timeout 7 | from cocotb.clock import Clock 8 | from cocotb_bus.drivers import BusDriver 9 | from cocotb_bus.monitors import BusMonitor 10 | from cocotb.binary import BinaryValue 11 | 12 | #BIN string 13 | #BinaryValue(dut.data_wr.value, n_bits=8) ; BinaryValue.integar ; BinaryValue.hex ; BinaryValue.binstr; BinaryValue.signed_integer ; can represent x,z 14 | 15 | DUT_KEY_WIDTH = 32 16 | DUT_VALUE_WIDTH = 32 17 | DUT_TOTAL_INDEX = 8 18 | DUT_CHAINING_SIZE = 4 19 | DUT_COLLISION_METHOD = "MULTI_STAGE_CHAINING" 20 | DUT_HASH_ALGORITHM = "MODULUS" 21 | 22 | MAX_VALUE = 2**DUT_VALUE_WIDTH - 1 23 | INDEX_WIDTH = int(math.log2(DUT_TOTAL_INDEX)) 24 | ADDR_WIDTH = int(math.log2(DUT_TOTAL_INDEX*DUT_CHAINING_SIZE)) 25 | 26 | OP_INSERT = 0b00 27 | OP_DELTE = 0b01 28 | OP_SEARCH = 0b10 29 | 30 | TB_CLK_PERIOD = 30 # TB clk generator 31 | TB_SIM_TIMEOUT = 30 # TB sim timeout 30ms 32 | TB_TEST_WEIGHT = 1 33 | err_cnt = 1 34 | 35 | # hash_table #( 36 | # .KEY_WIDTH(DUT_KEY_WIDTH), 37 | # .VALUE_WIDTH(DUT_VALUE_WIDTH), 38 | # .TOTAL_INDEX(DUT_TOTAL_INDEX), // Total index of the hash table 39 | # .CHAINING_SIZE(DUT_CHAINING_SIZE), // Number of chains for SINGLE_CYCLE_CHAINING and MULTI_STAGE_CHAINING only 40 | # .COLLISION_METHOD(DUT_COLLISION_METHOD), // "MULTI_STAGE_CHAINING", "LINEAR_PROBING" 41 | # .HASH_ALGORITHM(DUT_HASH_ALGORITHM)) // "MODULUS", "SHA1", "FNV1A")) 42 | # DUT ( 43 | # /*input wire*/ .clk(clk), 44 | # /*input wire*/ .rst(rst), 45 | # /*input wire [KEY_WIDTH-1:0]*/ .key_in(key_in), 46 | # /*input wire [VALUE_WIDTH-1:0]*/ .value_in(value_in), 47 | # /*input wire [1:0]*/ .op_sel(op_sel), // 00: Insert, 01: Delete, 10: Search 48 | # /*input wire*/ .op_en(op_en), 49 | # /*output reg [VALUE_WIDTH-1:0]*/ .value_out(value_out), 50 | # /*output reg*/ .op_done(op_done), 51 | # /*output reg*/ .op_error(op_error), // FULL when insert FAIL, KEY_NOT_FOUND when delete or search FAIL 52 | # /*output reg [CHAIN_WIDTH-1:0]*/ .collision_count(collision_count)); 53 | 54 | # Below is not a good way to define "hash_table" in python, but for the sake of simplicity, we define it like this. 55 | class hash_table: 56 | def __init__(self, dut): 57 | self.dut = dut 58 | self.key_value_pair = [{} for j in range(DUT_TOTAL_INDEX)] 59 | 60 | def hash(self, key): 61 | if DUT_HASH_ALGORITHM == "MODULUS": 62 | return key % DUT_TOTAL_INDEX 63 | elif DUT_HASH_ALGORITHM == "SHA1": 64 | # Implement SHA1 hash function here 65 | pass 66 | elif DUT_HASH_ALGORITHM == "FNV1A": 67 | # Implement FNV1A hash function here 68 | pass 69 | 70 | def insert(self, key, value): 71 | index = self.hash(key) 72 | if len(self.key_value_pair[index]) < DUT_CHAINING_SIZE: 73 | self.key_value_pair[index][key] = value 74 | cocotb.log.info("Inserted key: %d, value: %d at index: %d", key, value, index) 75 | return True 76 | else: 77 | cocotb.log.info("Collision occurred at index: %d", index) 78 | return False 79 | 80 | def delete(self, key): 81 | index = self.hash(key) 82 | if key in self.key_value_pair[index]: 83 | del self.key_value_pair[index][key] 84 | cocotb.log.info("Deleted key: %d at index: %d", key, index) 85 | return True 86 | else: 87 | cocotb.log.info("Key not found: %d at index: %d", key, index) 88 | return False 89 | 90 | def search(self, key): 91 | index = self.hash(key) 92 | if key in self.key_value_pair[index]: 93 | cocotb.log.info("Found key: %d, value: %d at index: %d", key, self.key_value_pair[index][key], index) 94 | return self.key_value_pair[index][key] 95 | else: 96 | cocotb.log.info("Key not found: %d at index: %d", key, index) 97 | return -1 98 | 99 | def print_content(self): 100 | cocotb.log.info("Hash Table Content:") 101 | for i in range(DUT_TOTAL_INDEX): 102 | info = "index " + str(i) + " : " + str(self.key_value_pair[i]) 103 | cocotb.log.info(info) 104 | cocotb.log.info("End of Hash Table Content") 105 | 106 | async def hash_table_insert(dut, hash_table, key, value): 107 | global err_cnt 108 | cocotb.log.info("OP_Insert key: %0d, value: %0d", key, value) 109 | await RisingEdge(dut.clk) 110 | await Timer (1, units = 'ns') 111 | dut.key_in.value = key 112 | dut.value_in.value = value 113 | dut.op_sel.value = OP_INSERT 114 | dut.op_en.value = 1 115 | await RisingEdge(dut.op_done) 116 | await Timer (1, units = 'ns') 117 | result = hash_table.insert(key, value) 118 | if (result == False): 119 | if(dut.op_error.value == 1): 120 | cocotb.log.info("Collision occurred, error flag is asserted correctly") 121 | else: 122 | cocotb.log.error("Collision occurred, error flag is not asserted") 123 | err_cnt += 1 124 | await RisingEdge(dut.clk) 125 | dut.op_en.value = 0 126 | hash_table.print_content() 127 | 128 | async def hash_table_delete(dut, hash_table, key): 129 | global err_cnt 130 | cocotb.log.info("OP_Delete key: %0d", key) 131 | await RisingEdge(dut.clk) 132 | await Timer (1, units = 'ns') 133 | dut.key_in.value = key 134 | dut.op_sel.value = OP_DELTE 135 | dut.op_en.value = 1 136 | await RisingEdge(dut.op_done) 137 | await Timer (1, units = 'ns') 138 | result = hash_table.delete(key) 139 | if (result == False): 140 | if(dut.op_error.value == 1): 141 | cocotb.log.info("Key not found, error flag is asserted correctly") 142 | else: 143 | cocotb.log.error("Key not found, error flag is not asserted") 144 | err_cnt += 1 145 | await RisingEdge(dut.clk) 146 | dut.op_en.value = 0 147 | hash_table.print_content() 148 | 149 | async def hash_table_search(dut, hash_table, key): 150 | global err_cnt 151 | cocotb.log.info("OP_Search key: %0d", key) 152 | await RisingEdge(dut.clk) 153 | await Timer (1, units = 'ns') 154 | dut.key_in.value = key 155 | dut.op_sel.value = OP_SEARCH 156 | dut.op_en.value = 1 157 | cocotb.log.info("hello") 158 | await RisingEdge(dut.op_done) 159 | cocotb.log.info("hello2") 160 | await Timer (1, units = 'ns') 161 | await ReadOnly() 162 | result = hash_table.search(key) 163 | if (result == -1): 164 | if(dut.op_error.value == 1): 165 | cocotb.log.info("Key not found, error flag is asserted correctly") 166 | else: 167 | cocotb.log.error("Key not found, error flag is not asserted") 168 | err_cnt += 1 169 | else: 170 | if(dut.value_out.value == result): 171 | cocotb.log.info("Key found, value: %0d", result) 172 | else: 173 | cocotb.log.error("Key found, but value is not correct") 174 | err_cnt += 1 175 | await RisingEdge(dut.clk) 176 | dut.op_en.value = 0 177 | hash_table.print_content() 178 | return -1 if (dut.op_error.value) else dut.value_out.value 179 | 180 | 181 | 182 | async def dut_init(dut): 183 | global DUT_KEY_WIDTH 184 | global DUT_VALUE_WIDTH 185 | global DUT_TOTAL_INDEX 186 | global DUT_CHAINING_SIZE 187 | global MAX_VALUE 188 | global INDEX_WIDTH 189 | global ADDR_WIDTH 190 | 191 | DUT_KEY_WIDTH = dut.KEY_WIDTH.value 192 | DUT_VALUE_WIDTH = dut.VALUE_WIDTH.value 193 | DUT_TOTAL_INDEX = dut.TOTAL_INDEX.value 194 | DUT_CHAINING_SIZE = dut.CHAINING_SIZE.value 195 | DUT_COLLISION_METHOD = dut.COLLISION_METHOD.value 196 | DUT_HASH_ALGORITHM = dut.HASH_ALGORITHM.value 197 | MAX_VALUE = 2**DUT_VALUE_WIDTH - 1 198 | INDEX_WIDTH = int(math.log2(DUT_TOTAL_INDEX)) 199 | ADDR_WIDTH = int(math.log2(DUT_TOTAL_INDEX*DUT_CHAINING_SIZE)) 200 | 201 | await cocotb.start(Clock(dut.clk, TB_CLK_PERIOD, units="ns").start()) 202 | dut.rst.value = 0 203 | dut.key_in.value = 0 204 | dut.value_in.value = 0 205 | dut.op_sel.value = 0 206 | dut.op_en.value = 0 207 | dut.rst.value = 1 208 | await(Timer(100,'ns')) 209 | dut.rst.value = 0 210 | await(Timer(100,'ns')) 211 | 212 | async def timeout(): 213 | await Timer(TB_SIM_TIMEOUT, units='ms') 214 | cocotb.log.error("Simulation timeout") 215 | raise cocotb.result.TestFailure("Simulation timeout") 216 | 217 | @cocotb.test() 218 | async def direct_basic_op_test(dut): 219 | #timeout_task = asyncio.create_task(timeout()) 220 | cocotb.start_soon(timeout()) 221 | await dut_init(dut) 222 | exp_hash_table = hash_table(dut) 223 | cocotb.log.info("SEED NUMBER = %d",cocotb.RANDOM_SEED) 224 | await hash_table_insert(dut, exp_hash_table, 1, 2) 225 | result = await hash_table_search(dut, exp_hash_table, 1) 226 | await hash_table_insert(dut, exp_hash_table, 3, 2) 227 | await hash_table_insert(dut, exp_hash_table, 11, 3) 228 | await hash_table_insert(dut, exp_hash_table, 19, 4) 229 | await hash_table_insert(dut, exp_hash_table, 27, 5) 230 | await hash_table_insert(dut, exp_hash_table, 35, 5) 231 | await hash_table_insert(dut, exp_hash_table, 43, 5) 232 | await hash_table_delete(dut, exp_hash_table, 1) 233 | result = await hash_table_search(dut, exp_hash_table, 19) 234 | result = await hash_table_search(dut, exp_hash_table, 1) 235 | result = await hash_table_search(dut, exp_hash_table, 3) 236 | 237 | #timeout_task.cancel() 238 | #task.kill() 239 | 240 | if (err_cnt > 0): 241 | cocotb.log.error("Errors count = %d",err_cnt) 242 | raise cocotb.result.TestFailure() 243 | -------------------------------------------------------------------------------- /Hash_Table/tb/sv/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: sim gen_sim synth netlist_sim gen_netlist_sim clean 2 | 3 | DUT ?= hash_table 4 | SIM_OPTS ?= ../../src/hash_table.sv 5 | SEED ?= $$(shuf -i 1-10000 -n 1) 6 | 7 | # DUT parameter #"-p" iverilog command flags 8 | COMPILE_ARGS = -P tb.DUT_KEY_WIDTH=32 9 | COMPILE_ARGS += -P tb.DUT_VALUE_WIDTH=32 10 | COMPILE_ARGS += -P tb.DUT_TOTAL_INDEX=8 11 | COMPILE_ARGS += -P tb.DUT_CHAINING_SIZE=4 12 | #ICARUS does not support passing with string type. 13 | #COMPILE_ARGS += -P tb.DUT_COLLISION_METHOD=MULTI_STAGE_CHAINING 14 | #COMPILE_ARGS += -P tb.DUT_HASH_ALGORITHM=MODULUS 15 | COMPILE_ARGS += -P tb.TB_CLK_PERIOD=100 16 | COMPILE_ARGS += -P tb.TB_TEST_WEIGHT=1 17 | COMPILE_ARGS += -P tb.TB_SIM_TIMEOUT=30 #//ms. 18 | 19 | XILINX_LIB_URL ?= https://github.com/Xilinx/XilinxUnisimLibrary.git 20 | XILINX_LIB_DIR ?= XilinxUnisimLibrary/verilog/src 21 | 22 | sim: gen_sim 23 | ./${DUT}.sim +VCDFILE=sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee sim.log 24 | 25 | gen_sim: tb.sv ${SIM_OPTS} # to generate executable file by using iverilator 26 | iverilog -g2012 -s tb ${COMPILE_ARGS} -o ${DUT}.sim $^ 27 | 28 | synth: ${SIM_OPTS} 29 | yosys -p synth_xilinx ${SIM_OPTS} -L synth.log -o ${DUT}.netlist.v 30 | #timeout 31 | 32 | netlist_sim: gen_netlist_sim 33 | ./${DUT}.netlist.sim +VCDFILE=netlist_sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee netlist_sim.log 34 | 35 | gen_netlist_sim: tb.sv ${DUT}.netlist.v | ${XILINX_LIBS_DIR}/.git 36 | iverilog -g2012 -s tb -y ${XILINX_LIB_DIR} -y ${XILINX_LIB_DIR}/unisims -DXILINX_GLS ${COMPILE_ARGS} -o ${DUT}.netlist.sim $^ 37 | 38 | ${XILINX_LIBS_DIR}/.git: 39 | git clone ${XILINX_LIB_URL} 40 | 41 | clean: 42 | rm -f ${DUT}_sim sim.log sim.vcd 43 | rm -f ${DUT}.netlist.v synth.log 44 | -------------------------------------------------------------------------------- /Hash_Table/tb/sv/tb.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // 4 | // Create Date: 26/04/2025 03:37:34 PM 5 | // Last Update: 03/05/2025 6:20 PM 6 | // Module Name: tb 7 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 8 | // Description: 9 | // Additional Comments: 10 | // 11 | ////////////////////////////////////////////////////////////////////////////////// 12 | 13 | 14 | module tb( 15 | ); 16 | parameter DUT_KEY_WIDTH = 32; 17 | parameter DUT_VALUE_WIDTH = 32; 18 | parameter DUT_TOTAL_INDEX = 8; // Total index of the hash table 19 | parameter DUT_CHAINING_SIZE = 4; // Number of chains for SINGLE_CYCLE_CHAINING and MULTI_STAGE_CHAINING only 20 | parameter DUT_COLLISION_METHOD = "MULTI_STAGE_CHAINING"; // "MULTI_STAGE_CHAINING", "LINEAR_PROBING" 21 | parameter DUT_HASH_ALGORITHM = "MODULUS"; // "MODULUS", "SHA1", "FNV1A" 22 | parameter TB_CLK_PERIOD = 100; 23 | parameter TB_TEST_WEIGHT = 1; 24 | parameter TB_SIM_TIMEOUT = 30; //ms. 25 | 26 | localparam MAX_VALUE = 2**(DUT_VALUE_WIDTH)-1; 27 | localparam INDEX_WIDTH = $clog2(DUT_TOTAL_INDEX); 28 | localparam CHAIN_WIDTH = $clog2(DUT_CHAINING_SIZE); 29 | 30 | // associative array datatype is not supported by icarus 31 | // icarus does not support 2d queue. 32 | reg [DUT_KEY_WIDTH-1:0] expected_hash_key [0:DUT_TOTAL_INDEX-1][0:DUT_CHAINING_SIZE-1]; 33 | reg [DUT_VALUE_WIDTH-1:0] expected_hash_value [0:DUT_TOTAL_INDEX-1][0:DUT_CHAINING_SIZE-1]; 34 | integer expected_hash_entry_count [0:DUT_TOTAL_INDEX-1]; 35 | 36 | reg clk=0; 37 | reg rst=0; 38 | reg [DUT_KEY_WIDTH-1:0] key_in=0; 39 | reg [DUT_VALUE_WIDTH-1:0] value_in=0; 40 | reg [1:0] op_sel=0; 41 | reg op_en=0; 42 | wire [DUT_VALUE_WIDTH-1:0] value_out; 43 | wire op_done; 44 | wire op_error; 45 | wire [CHAIN_WIDTH-1:0] collision_count; 46 | integer index_out; 47 | integer error; 48 | integer data_rd; 49 | 50 | integer err_cnt = 0; 51 | 52 | // Hash function selector 53 | function integer get_hash_index; 54 | input integer key; 55 | reg [31:0] hash_value; 56 | begin 57 | if (DUT_HASH_ALGORITHM == "MODULUS") 58 | hash_value = key % DUT_TOTAL_INDEX; 59 | else // for future implentation of other hash algorithm 60 | hash_value = key; 61 | 62 | get_hash_index = hash_value[INDEX_WIDTH-1:0]; 63 | end 64 | endfunction 65 | 66 | integer temp; 67 | 68 | task find_first_index (input integer key, output integer index_out); // icarus does not support in built method find_first_index 69 | begin 70 | index_out = -1; 71 | temp = get_hash_index(key); 72 | for (integer i = 0; i < expected_hash_entry_count[temp]; i = i + 1) begin 73 | if(key == expected_hash_key[temp][i]) begin 74 | index_out = i; 75 | $display("%0t find_first_index: Key %0d is found at Index %0d Chain no %0d", $realtime, key, temp, i); 76 | end 77 | end 78 | if (index_out == -1) begin 79 | $display("%0t find_first_index: Key %0d is not found", $realtime, key); 80 | end 81 | end 82 | endtask 83 | 84 | task print_hash_table; 85 | begin 86 | for (integer i = 0; i < DUT_TOTAL_INDEX; i = i + 1) begin 87 | $display("Hash Table Index %0d: ", i); 88 | for (integer j = 0; j < expected_hash_entry_count[i]; j = j + 1) begin 89 | $display("Key: %0d, Value: %0d", expected_hash_key[i][j], expected_hash_value[i][j]); 90 | end 91 | end 92 | end 93 | endtask 94 | 95 | task delete_index (input integer key, input integer index); 96 | begin 97 | temp = get_hash_index(key); 98 | for (integer i = index; i < expected_hash_entry_count[temp]; i = i + 1) begin 99 | expected_hash_key[temp][i] = expected_hash_key[temp][i+1]; 100 | expected_hash_value[temp][i] = expected_hash_value[temp][i+1]; 101 | end 102 | expected_hash_key[temp][expected_hash_entry_count[temp]-1] = 0; 103 | expected_hash_value[temp][expected_hash_entry_count[temp]-1] = 0; 104 | expected_hash_entry_count[temp] = expected_hash_entry_count[temp] - 1; 105 | $display("%0t delete_index: Key %0d is deleted at Index %0d Chain no %0d", $realtime, key, temp, index); 106 | end 107 | endtask 108 | // icarus does not support associative array datatype 109 | 110 | task reset_model; 111 | for (integer i = 0; i < DUT_TOTAL_INDEX; i = i + 1) begin 112 | expected_hash_entry_count[i] = 0; 113 | for (integer j = 0; j < DUT_CHAINING_SIZE; j = j + 1) begin 114 | expected_hash_key[i][j] = 0; 115 | expected_hash_value[i][j] = 0; 116 | end 117 | end 118 | endtask 119 | 120 | `ifdef XILINX_GLS 121 | glbl glbl (); // for Xilinx GLS 122 | `endif 123 | 124 | hash_table #( 125 | .KEY_WIDTH(DUT_KEY_WIDTH), 126 | .VALUE_WIDTH(DUT_VALUE_WIDTH), 127 | .TOTAL_INDEX(DUT_TOTAL_INDEX), // Total index of the hash table 128 | .CHAINING_SIZE(DUT_CHAINING_SIZE), // Number of chains for SINGLE_CYCLE_CHAINING and MULTI_STAGE_CHAINING only 129 | .COLLISION_METHOD(DUT_COLLISION_METHOD), // "MULTI_STAGE_CHAINING", "LINEAR_PROBING" 130 | .HASH_ALGORITHM(DUT_HASH_ALGORITHM)) // "MODULUS", "SHA1", "FNV1A")) 131 | DUT ( 132 | /*input wire*/ .clk(clk), 133 | /*input wire*/ .rst(rst), 134 | /*input wire [KEY_WIDTH-1:0]*/ .key_in(key_in), 135 | /*input wire [VALUE_WIDTH-1:0]*/ .value_in(value_in), 136 | /*input wire [1:0]*/ .op_sel(op_sel), // 00: Insert, 01: Delete, 10: Search 137 | /*input wire*/ .op_en(op_en), 138 | /*output reg [VALUE_WIDTH-1:0]*/ .value_out(value_out), 139 | /*output reg*/ .op_done(op_done), 140 | /*output reg*/ .op_error(op_error), // FULL when insert FAIL, KEY_NOT_FOUND when delete or search FAIL 141 | /*output reg [CHAIN_WIDTH-1:0]*/ .collision_count(collision_count)); 142 | 143 | always #(TB_CLK_PERIOD>>1) clk = ~clk; 144 | 145 | integer target_index; 146 | 147 | task hash_table_insert(input [DUT_KEY_WIDTH-1:0] key, input [DUT_VALUE_WIDTH-1:0] value); 148 | @ (posedge clk) begin 149 | key_in <= key; 150 | value_in <= value; 151 | op_sel <= 2'b00; 152 | op_en <= 1; 153 | target_index = get_hash_index(key); 154 | end 155 | wait(op_done) 156 | #1 157 | find_first_index(key,index_out); 158 | if(expected_hash_entry_count[target_index] < DUT_CHAINING_SIZE) begin 159 | if(index_out != -1) begin 160 | expected_hash_value[target_index][index_out] = value; 161 | $display("%0t hash_table_insert: Key %0d - Value %0d is updated to expected index %0d", $realtime, value, key, target_index); 162 | end else begin 163 | expected_hash_key[target_index][expected_hash_entry_count[target_index]] = key; 164 | expected_hash_value[target_index][expected_hash_entry_count[target_index]] = value; 165 | expected_hash_entry_count[target_index] = expected_hash_entry_count[target_index] + 1; 166 | $display("%0t hash_table_insert: Key %0d - Value %0d is inserted to expected index %0d", $realtime, value, key, target_index); 167 | end 168 | end else begin 169 | if(op_error) 170 | $display("%0t hash_table_insert: Key %0d - Value %0d is not inserted succesfully, chain is full, op_error is asserted correctly", $realtime, value, key); 171 | else begin 172 | $error("%0t hash_table_insert: Key %0d - Value %0d is not inserted succesfully, chain is full, op_error is not asserted expectedly", $realtime, value, key); 173 | err_cnt += 1; 174 | end 175 | end 176 | @ (posedge clk) 177 | op_en <= 0; 178 | print_hash_table(); 179 | endtask 180 | 181 | task hash_table_delete(input [DUT_KEY_WIDTH-1:0] key); 182 | @ (posedge clk) begin 183 | key_in <= key; 184 | op_sel <= 2'b01; 185 | op_en <= 1; 186 | target_index = get_hash_index(key); 187 | end 188 | wait(op_done) 189 | #1 190 | find_first_index(key,index_out); 191 | if(index_out != -1) begin 192 | $display("%0t hash_table_delete: Key %0d at index %0d is deleted", $realtime, key, target_index); 193 | delete_index(key,index_out); 194 | end else begin 195 | if(op_error) 196 | $display("%0t hash_table_delete: Key %0d is not deleted succesfully, key is unfound, op_error is asserted correctly", $realtime, key); 197 | else begin 198 | $error("%0t hash_table_delete: Key %0d is not deleted succesfully, key is unfound, op_error is not asserted expectedly", $realtime, key); 199 | err_cnt += 1; 200 | end 201 | end 202 | @ (posedge clk) 203 | op_en <= 0; 204 | print_hash_table(); 205 | endtask 206 | 207 | task hash_table_search(input [DUT_KEY_WIDTH-1:0] key, output error, output [DUT_VALUE_WIDTH-1:0] data_rd); 208 | @ (posedge clk) begin 209 | key_in <= key; 210 | op_sel <= 2'b10; 211 | op_en <= 1; 212 | target_index = get_hash_index(key); 213 | end 214 | wait(op_done) 215 | #1; 216 | find_first_index(key,index_out); 217 | if(index_out != -1) begin 218 | if(expected_hash_value[target_index][index_out] == value_out) 219 | $display("%0t hash_table_search: Key %0d - value %0d is found", $realtime, key, value_out); 220 | else begin 221 | $error("%0t hash_table_search: Key %0d - value %0d is mismatched, expeted_value %0d", $realtime, key, value_out, expected_hash_value[target_index][index_out]); 222 | err_cnt += 1; 223 | end 224 | end else begin 225 | if(op_error) 226 | $display("%0t hash_table_delete: Key %0d is unfound, op_error is asserted correctly", $realtime, key); 227 | else begin 228 | $error("%0t hash_table_delete: Key %0d is unfound, op_error is not asserted expectedly", $realtime, key); 229 | err_cnt += 1; 230 | end 231 | end 232 | @ (posedge clk) 233 | op_en <= 0; 234 | print_hash_table(); 235 | endtask 236 | 237 | task direct_basic_op_test; 238 | hash_table_insert(1,2); 239 | hash_table_search(1,error,data_rd); 240 | hash_table_insert(3,2); 241 | hash_table_insert(11,3); 242 | hash_table_insert(19,4); 243 | hash_table_insert(27,5); 244 | hash_table_insert(35,5); 245 | hash_table_insert(43,5); 246 | hash_table_delete(1); 247 | hash_table_search(19,error,data_rd); 248 | hash_table_search(1,error,data_rd); 249 | hash_table_search(3,error,data_rd); 250 | endtask 251 | 252 | initial begin 253 | string vcdfile; 254 | int vcdlevel; 255 | int seed; 256 | int temp; 257 | 258 | rst = 1'b1; 259 | if ($value$plusargs("VCDFILE=%s",vcdfile)) 260 | $dumpfile(vcdfile); 261 | if ($value$plusargs("VCDLEVEL=%d",vcdlevel)) 262 | $dumpvars(vcdlevel,tb); 263 | if ($value$plusargs("SEED=%d",seed)) begin 264 | temp = $urandom(seed); 265 | $display("Seed = %d",seed); 266 | end 267 | rst = 1; 268 | reset_model(); 269 | #100 270 | rst = 0; 271 | direct_basic_op_test(); 272 | #1000; 273 | if (err_cnt > 0) begin 274 | $display("\n%0t TEST FAILED",$realtime); 275 | $display("Error count = %d\n", err_cnt); 276 | end else 277 | $display("\n%0t TEST PASSED\n", $realtime); 278 | $finish; 279 | end 280 | 281 | initial begin 282 | #(TB_SIM_TIMEOUT * 1ms); 283 | $display("\n%0t TEST FAILED", $realtime); 284 | $display("SIM TIMEOUT!\n"); 285 | $finish; 286 | end 287 | 288 | endmodule 289 | 290 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [Wei Yet Ng] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LIFO/README.md: -------------------------------------------------------------------------------- 1 | # LIFO (Stack) 2 | 3 | A configurable LIFO (Last-In-First-Out) buffer implementation in Verilog. This module implements a stack-like behavior where the last element written is the first one to be read out. The design includes bypass functionality for simultaneous read/write operations. 4 | 5 | ## Features 6 | 7 | - Configurable data width and depth 8 | - Single clock domain operation 9 | - Full and empty status flags 10 | - Bypass path for simultaneous read/write 11 | - Registered read output 12 | - Automatic pointer management 13 | - Parameterized design for easy customization 14 | 15 | ## Parameters 16 | 17 | | Parameter | Description | Default | 18 | |-------------|--------------------------------------------------|---------| 19 | | DEPTH | Number of entries in the LIFO | 12 | 20 | | DATA_WIDTH | Width of each data entry | 8 | 21 | 22 | ## Interface Signals 23 | 24 | | Signal | Direction | Width | Description | 25 | |-------------|-----------|---------------|------------------------------------------------| 26 | | clk | Input | 1 | System clock | 27 | | rst | Input | 1 | Active high reset | 28 | | data_wr | Input | DATA_WIDTH | Data input for writing | 29 | | wr_en | Input | 1 | Write enable signal | 30 | | lifo_full | Output | 1 | LIFO full indicator | 31 | | data_rd | Output | DATA_WIDTH | Data output from reading | 32 | | rd_en | Input | 1 | Read enable signal | 33 | | lifo_empty | Output | 1 | LIFO empty indicator | 34 | 35 | ## Usage Example 36 | 37 | ```verilog 38 | // Instantiate a 16-deep, 32-bit wide LIFO 39 | lifo #( 40 | .DEPTH(16), 41 | .DATA_WIDTH(32) 42 | ) lifo_inst ( 43 | .clk(system_clock), 44 | .rst(reset), 45 | .data_wr(write_data), 46 | .wr_en(write_enable), 47 | .lifo_full(full_flag), 48 | .data_rd(read_data), 49 | .rd_en(read_enable), 50 | .lifo_empty(empty_flag) 51 | ); 52 | ``` 53 | 54 | ## Operation Modes 55 | 56 | The module supports three operation modes: 57 | 58 | 1. **Write Operation (wr_op)** 59 | - Activated when wr_en=1 and rd_en=0 60 | - Data is written to current pointer location 61 | - Pointer increments if not full 62 | 63 | 2. **Read Operation (rd_op)** 64 | - Activated when rd_en=1 and wr_en=0 65 | - Data is read from (pointer-1) location 66 | - Pointer decrements if not empty 67 | 68 | 3. **Bypass Operation (bypass_op)** 69 | - Activated when both wr_en=1 and rd_en=1 70 | - Input data (data_wr) is directly passed to output (data_rd) 71 | - Pointer remains unchanged 72 | 73 | ## Design Notes 74 | 75 | 1. **Pointer Management**: 76 | - Single pointer tracks both read and write positions 77 | - Increments on write, decrements on read 78 | - Range: 0 to (DEPTH-1) 79 | 80 | 2. **Memory Organization**: 81 | - Stack-like structure with DEPTH entries 82 | - Each entry is DATA_WIDTH bits wide 83 | - Last written data is first to be read 84 | 85 | 3. **Flag Generation**: 86 | - Empty flag is combinatorial (pointer = 0) 87 | - Full flag is registered (pointer = DEPTH-1) 88 | - Flags prevent invalid operations 89 | 90 | ## Reset Behavior 91 | 92 | On assertion of reset: 93 | - Pointer is cleared to zero 94 | - All memory locations are cleared 95 | - Full flag is deasserted 96 | - Output data is cleared 97 | - Empty flag becomes active 98 | 99 | ## Timing Considerations 100 | 101 | 1. All outputs except lifo_empty are registered 102 | 2. Data is available on the next clock cycle after rd_en assertion 103 | 3. Bypass operation provides data in the same clock cycle 104 | 105 | ## Limitations 106 | 107 | - Single clock domain operation only 108 | - No protection against overflow/underflow if flags are ignored 109 | - DEPTH parameter should be at least 2 for proper operation 110 | - Simultaneous read/write operations bypass the stack memory 111 | 112 | ## Alternative Implementation Note 113 | 114 | The commented code at the bottom of the module suggests an alternative implementation for full flag handling and read operations, which could be implemented if different timing behavior is needed. 115 | -------------------------------------------------------------------------------- /LIFO/src/lifo.sv: -------------------------------------------------------------------------------- 1 | module lifo #( 2 | parameter DEPTH = 12, 3 | parameter DATA_WIDTH = 8 4 | )( 5 | input wire clk, 6 | input wire rst, 7 | input wire [DATA_WIDTH-1:0] data_wr, 8 | input wire wr_en, 9 | output reg lifo_full, 10 | output reg [DATA_WIDTH-1:0] data_rd, 11 | input wire rd_en, 12 | output wire lifo_empty 13 | ); 14 | localparam CNTR_WIDTH = $clog2(DEPTH); 15 | 16 | reg [CNTR_WIDTH-1:0] pointer; 17 | reg [DATA_WIDTH-1:0] lifo_stored [DEPTH-1:0]; 18 | wire wr_op; 19 | wire rd_op; 20 | wire bypass_op; 21 | 22 | integer i; 23 | 24 | assign wr_op = wr_en & !rd_en; 25 | assign rd_op = rd_en & !wr_en; 26 | assign bypass_op = wr_en & rd_en; 27 | 28 | // pointer logic 29 | always @ (posedge clk, posedge rst) begin 30 | if(rst) begin 31 | pointer <= {CNTR_WIDTH{1'b0}}; 32 | end else if(rd_op & !lifo_empty) begin 33 | pointer <= pointer - 1; 34 | end else if(wr_op & !lifo_full) begin 35 | pointer <= pointer + 1; 36 | end 37 | end 38 | 39 | // lifo memory 40 | always @ (posedge clk, posedge rst) begin 41 | if(rst) begin 42 | for (i = 0; i < DEPTH; i = i + 1) 43 | lifo_stored[i] <= {DATA_WIDTH{1'b0}}; 44 | end else if (wr_op & !lifo_full) begin 45 | lifo_stored[pointer] <= data_wr; 46 | end 47 | end 48 | 49 | // data_rd 50 | always @ (posedge clk, posedge rst) begin 51 | if(rst) begin 52 | data_rd <= {DATA_WIDTH{1'b0}}; 53 | end else if (rd_op & !lifo_empty) begin 54 | data_rd <= lifo_stored[pointer-1]; 55 | end else if (bypass_op) begin 56 | data_rd <= data_wr; 57 | end 58 | end 59 | 60 | // flag 61 | assign lifo_empty = pointer == {CNTR_WIDTH{1'b0}}; 62 | always @ (posedge clk, posedge rst) begin 63 | if(rst) begin 64 | lifo_full <= 1'b0; 65 | end else if(rd_op) begin 66 | lifo_full <= 1'b0; 67 | end else if(pointer == (DEPTH-1) & wr_op) begin 68 | lifo_full <= 1'b1; 69 | end 70 | end 71 | 72 | // assign lifo_full = pointer == (DEPTH); 73 | // can use this as flag and modify read cond when lifo is full 74 | // if lifo_full : data_rd <= (lifo_stored[pointer-1]) else data_rd <= lifo_stored[pointer] 75 | // : pointer <= pointer - 2 else pointer <= pointer <= pointer -1 76 | 77 | endmodule 78 | -------------------------------------------------------------------------------- /LIFO/src/rtl_list.f: -------------------------------------------------------------------------------- 1 | lifo.sv 2 | -------------------------------------------------------------------------------- /LIFO/tb/cocotb/Makefile: -------------------------------------------------------------------------------- 1 | SIM ?= icarus 2 | TOPLEVEL_LANG ?= verilog 3 | WAVES ?= 1 #set 1 to enable waveform dump. 4 | 5 | PWD=$(shell pwd) 6 | 7 | #export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) 8 | 9 | VERILOG_SOURCES = $(PWD)/../../src/lifo.sv 10 | 11 | # DUT Top 12 | TOPLEVEL = lifo 13 | # top python file 14 | MODULE = tb 15 | 16 | #use , separtor to run multiple TESTCASE, by default all @cocotb.test will be run 17 | #TESTCASE = lifo_rand_op_test 18 | TESTCASE = lifo_rand_op_test 19 | 20 | COMPILE_ARGS = -Plifo.DEPTH=12 # DUT parameter #"-p" iverilog command flags 21 | COMPILE_ARGS += -Plifo.DATA_WIDTH=8 # DUT parameter #"-p" iverilog command flags 22 | #run make clean before running with new parameter. 23 | 24 | #Set RANDOM_SEED number 25 | #PLUSARGS = +seed=1716033254 26 | COCOTB_HDL_TIMEUNIT = 1ns 27 | COCOTB_HDL_TIMEPRECISION = 1ps 28 | 29 | ifeq ($(SIM), icarus) 30 | $(shell echo 'module iverilog_dump();' > iverilog_dump.v) 31 | $(shell echo 'initial begin' >> iverilog_dump.v) 32 | $(shell echo ' $$dumpfile("$(TOPLEVEL).vcd");' >> iverilog_dump.v) 33 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 34 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 35 | $(shell echo 'end' >> iverilog_dump.v) 36 | $(shell echo 'endmodule' >> iverilog_dump.v) 37 | VERILOG_SOURCES += $(PWD)/iverilog_dump.v 38 | COMPILE_ARGS += -s iverilog_dump 39 | endif 40 | 41 | include $(shell cocotb-config --makefiles)/Makefile.sim 42 | 43 | 44 | -------------------------------------------------------------------------------- /LIFO/tb/cocotb/tb.py: -------------------------------------------------------------------------------- 1 | import random 2 | #import asyncio 3 | import cocotb 4 | from cocotb.triggers import Timer, RisingEdge, ReadOnly 5 | from cocotb.clock import Clock 6 | from cocotb_bus.drivers import BusDriver 7 | from cocotb_bus.monitors import BusMonitor 8 | 9 | DEPTH = 12 # DUT parameter 10 | DATA_WIDTH = 8 # DUT paramter 11 | TEST_WEIGHT = 1 # TB multiplier for stimulus injected 12 | CLK_PERIOD = 20 # TB clk generator 13 | #SIM_TIMEOUT = 100000; // TB simulation time out 14 | MAX_DATA = 2**DATA_WIDTH - 1 15 | err_cnt = 0 16 | 17 | # lifo #( 18 | # .DEPTH(DEPTH), 19 | # .DATA_WIDTH(DATA_WIDTH)) DUT ( 20 | # /*input wire*/ .clk(clk), 21 | # /*input wire*/ .rst(rst), 22 | # /*input wire [DATA_WIDTH-1:0]*/ .data_wr(data_wr), 23 | # /*input wire*/ .wr_en(wr_en), 24 | # /*output reg*/ .lifo_full(lifo_full), 25 | # /*output reg [DATA_WIDTH-1:0]*/ .data_rd(data_rd), 26 | # /*input wire*/ .rd_en(rd_en), 27 | # /*output wire*/ .lifo_empty(lifo_empty)); 28 | 29 | class OutputDriver(BusDriver): 30 | _signals = ["wr_en", "rd_en", "data_wr"] #dut.wr_en ; dut.rd_en; dut.data_wr 31 | 32 | def __init__(self, dut, name, clk): 33 | BusDriver.__init__(self, dut, name, clk) 34 | self.bus.wr_en.value = 0 35 | self.bus.rd_en.value = 0 36 | self.dut = dut 37 | self.clk = clk 38 | 39 | async def _driver_send(self, op_sel, op_count): 40 | match op_sel: 41 | case 0: 42 | wr_data_array = [] 43 | for i in range (op_count): 44 | wr_data_array.append(random.randint(0,MAX_DATA)) 45 | await self.lifo_write(wr_data_array) 46 | case 1: 47 | await self.lifo_read(op_count) 48 | case 2: 49 | wr_data_array = [] 50 | for i in range (op_count): 51 | wr_data_array.append(random.randint(0,MAX_DATA)) 52 | await self.lifo_read_write_simul(wr_data_array) 53 | 54 | async def lifo_write(self, wr_data_array): 55 | data_w = 0 56 | while len(wr_data_array) != 0: 57 | await RisingEdge(self.clk) 58 | await Timer (1, units = 'ns') 59 | self.bus.wr_en.value = 1 60 | data_w = wr_data_array.pop() 61 | self.bus.data_wr.value = data_w 62 | self.dut._log.info("Driver: Writting Data = %d",data_w) 63 | await RisingEdge(self.clk) 64 | self.bus.wr_en.value = 0 65 | 66 | async def lifo_read(self,op_count): 67 | for i in range(op_count): 68 | await RisingEdge(self.clk) 69 | self.bus.rd_en.value = 1 70 | self.dut._log.info("Driver: Reading Data = %d",) 71 | await RisingEdge(self.clk) 72 | await Timer (1, units='ns') 73 | self.bus.rd_en.value = 0 74 | 75 | async def lifo_read_write_simul(self,wr_data_array): 76 | data_w = 0 77 | while len(wr_data_array) != 0: 78 | await RisingEdge(self.clk) 79 | await Timer (1, units = 'ns') 80 | self.bus.rd_en.value = 1 81 | self.bus.wr_en.value = 1 82 | data_w = wr_data_array.pop() 83 | self.bus.data_wr.value = data_w 84 | self.dut._log.info("Driver: Simultanenous read write, data = %d",data_w) 85 | await RisingEdge(self.clk) 86 | await Timer (1, units='ns') 87 | self.bus.rd_en.value = 0 88 | self.bus.wr_en.value = 0 89 | 90 | class InputMonitor(BusMonitor): 91 | _signals = ["wr_en","rd_en","lifo_empty","lifo_full","data_rd","data_wr"] 92 | 93 | def __init__(self, dut, name, clk, reset): 94 | BusMonitor.__init__(self, dut, name, clk, reset) 95 | self.clk = clk 96 | self.reset = reset 97 | self.dut = dut 98 | 99 | async def _monitor_recv(self): 100 | global err_cnt 101 | lifo_expected = [] 102 | rd_en_buf = 0 103 | wr_en_buf = 0 104 | data_wr_buf = 0 105 | while True: 106 | await RisingEdge(self.clock) 107 | await Timer(3,units='ns') 108 | await ReadOnly() 109 | 110 | if self.reset.value == 1: 111 | rd_en_buf = 0 112 | wr_en_buf = 0 113 | data_wr_buf = 0 114 | continue 115 | 116 | if(rd_en_buf == 1 and wr_en_buf == 1): 117 | if(self.bus.data_rd.value == data_wr_buf): 118 | self.dut._log.info("Monitor: Simultaneous Data read/write, ACT = %d, EXP = %d, FIFO entry = %d", self.bus.data_rd.value, data_wr_buf, len(lifo_expected)) 119 | elif(wr_en_buf and len(lifo_expected) != DEPTH): 120 | lifo_expected.append(data_wr_buf) 121 | self.dut._log.info("Monitor: Data write = %d, FIFO entry = %d", data_wr_buf, len(lifo_expected)) 122 | elif(rd_en_buf == 1 and len(lifo_expected) != 0): 123 | lifo_expected.pop() 124 | self.dut._log.info("Monitor: Data read = %d, FIFO entry = %d", self.bus.data_rd.value, len(lifo_expected)) 125 | 126 | if(len(lifo_expected) == 0): 127 | if(self.bus.lifo_empty.value): 128 | self.dut._log.info("Monitor: LIFO is empty, lifo_full flag is asserted correctly") 129 | else: 130 | self.dut._log.error("Monitor: LIFO is empty, but lifo_full flag is not asserted") 131 | err_cnt += 1 132 | elif(self.bus.lifo_empty.value): 133 | self.dut._log.error("Monitor: LIFO is not empty, but lifo_empty flag is asserted") 134 | 135 | if(len(lifo_expected) == DEPTH): 136 | if(self.bus.lifo_full.value): 137 | self.dut._log.info("Monitor: LIFO is full, lifo_full flag is asserted correctly") 138 | else: 139 | self.dut._log.error("Monitor: LIFO is full, but lifo_full flag is not asserted") 140 | err_cnt += 1 141 | elif(self.bus.lifo_full.value): 142 | self.dut._log.error("Monitor: LIFO is not full, but lifo_full flag is asserted") 143 | 144 | rd_en_buf = int(self.bus.rd_en.value) 145 | wr_en_buf = int(self.bus.wr_en.value) 146 | data_wr_buf = int(self.bus.data_wr.value) 147 | 148 | 149 | async def dut_init(dut): 150 | global DEPTH 151 | global DATA_WIDTH 152 | global MAX_DATA 153 | DEPTH = dut.DEPTH.value 154 | DATA_WIDTH = dut.DATA_WIDTH.value 155 | MAX_DATA = 2**DATA_WIDTH - 1 156 | await cocotb.start(Clock(dut.clk, CLK_PERIOD, units="ns").start()) 157 | dut.data_wr.value = 0 158 | dut.rd_en.value = 0 159 | dut.wr_en.value = 0 160 | dut.rst.value = 1 161 | await(Timer(100,'ns')) 162 | dut.rst.value = 0 163 | await(Timer(100,'ns')) 164 | 165 | 166 | @cocotb.test() 167 | async def lifo_rand_op_test(dut): 168 | await dut_init(dut) 169 | driver = OutputDriver(dut, None, dut.clk) #set name='None', refer to Bus class 170 | monitor = InputMonitor(dut, None, dut.clk, dut.rst) 171 | cocotb.log.info("SEED NUMBER = %d",cocotb.RANDOM_SEED) 172 | i = DEPTH 173 | lifo_expected = [] 174 | while(i >= 0): 175 | op_sel = random.randint(0,1) 176 | op_count = random.randint(1,5) 177 | i = i - op_count 178 | await driver._driver_send(op_sel,op_count) 179 | await Timer(CLK_PERIOD,'ns') 180 | if (err_cnt > 0): 181 | cocotb.log.error("Errors count = %d",err_cnt) 182 | cocotb.result.test_fail() 183 | -------------------------------------------------------------------------------- /LIFO/tb/sv/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DUT = lifo 3 | SIM_OPTS ?= ../../src/lifo.sv 4 | SEED ?= $$(shuf -i 1-10000 -n 1) 5 | XILINX_LIB_URL ?= https://github.com/Xilinx/XilinxUnisimLibrary.git 6 | XILINX_LIB_DIR ?= XilinxUnisimLibrary/verilog/src 7 | 8 | sim: gen_sim 9 | ./${DUT}.sim +VCDFILE=sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee sim.log 10 | 11 | gen_sim: tb.sv ${SIM_OPTS} # to generate executable file by using iverilator 12 | iverilog -g2012 -s tb -o ${DUT}.sim $^ 13 | 14 | synth: ${SIM_OPTS} 15 | yosys -p synth_xilinx ${SIM_OPTS} -L synth.log -o ${DUT}.netlist.v 16 | #timeout 17 | 18 | netlist_sim: gen_netlist_sim 19 | ./${DUT}.netlist.sim +VCDFILE=netlist_sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee netlist_sim.log 20 | 21 | gen_netlist_sim: tb.sv ${DUT}.netlist.v | ${XILINX_LIBS_DIR}/.git 22 | iverilog -g2012 -s tb -y ${XILINX_LIB_DIR} -y ${XILINX_LIB_DIR}/unisims -DXILINX_GLS -o ${DUT}.netlist.sim $^ 23 | 24 | ${XILINX_LIBS_DIR}/.git: 25 | git clone ${XILINX_LIB_URL} 26 | 27 | clean: 28 | rm -f ${DUT}_sim sim.log sim.vcd 29 | rm -f ${DUT}.netlist.v synth.log 30 | -------------------------------------------------------------------------------- /LIFO/tb/sv/tb.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // 4 | // Create Date: 05/05/2024 05:05:34 PM 5 | // Last Update Date: 05/05/2024 05:05:34 PM 6 | // Module Name: tb 7 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 8 | // Description: 1. lifo_random_op_test 9 | // - Read/Write/Read_write_simultaneous 10 | // Additional Comments: 11 | // 12 | ////////////////////////////////////////////////////////////////////////////////// 13 | 14 | 15 | module tb( 16 | ); 17 | parameter DEPTH = 12; // DUT parameter 18 | parameter DATA_WIDTH = 8; // DUT paramter 19 | parameter CLK_PERIOD = 20; // TB clk generator 20 | parameter SIM_TIMEOUT = 100000; // TB simulation time out 21 | parameter TEST_WEIGHT = 1; //TB Test weight 22 | 23 | integer lifo_expected [$]; 24 | integer i; 25 | integer op_sel,op_count; 26 | integer err_cnt = 0; 27 | reg clk = 0; 28 | 29 | reg rst = 1; 30 | reg [DATA_WIDTH-1:0] data_wr = 0; 31 | reg wr_en = 0; 32 | reg rd_en = 0; 33 | wire [DATA_WIDTH-1:0] data_rd; 34 | wire lifo_empty; 35 | wire lifo_full; 36 | 37 | `ifdef XILINX_GLS 38 | // for xilinx gate sim 39 | glbl glbl(); 40 | `endif 41 | 42 | lifo #( 43 | .DEPTH(DEPTH), 44 | .DATA_WIDTH(DATA_WIDTH)) DUT ( 45 | /*input wire*/ .clk(clk), 46 | /*input wire*/ .rst(rst), 47 | /*input wire [DATA_WIDTH-1:0]*/ .data_wr(data_wr), 48 | /*input wire*/ .wr_en(wr_en), 49 | /*output reg*/ .lifo_full(lifo_full), 50 | /*output reg [DATA_WIDTH-1:0]*/ .data_rd(data_rd), 51 | /*input wire*/ .rd_en(rd_en), 52 | /*output wire*/ .lifo_empty(lifo_empty)); 53 | 54 | always #(CLK_PERIOD>>1) clk = ~clk; 55 | 56 | integer exp_data_wr = 0; 57 | 58 | task lifo_write(input integer wr_data_array[$]); 59 | while($size(wr_data_array) != 0) begin 60 | exp_data_wr = wr_data_array.pop_front(); 61 | @(posedge(clk)) 62 | wr_en <= 1; 63 | data_wr <= exp_data_wr; 64 | #1 65 | if($size(lifo_expected)0) begin 98 | if(lifo_empty) begin 99 | $error("%0t LIFO is not empty but lifo_empty flag is asserted incorrectly",$realtime); 100 | end 101 | #1 102 | act_data_rd = data_rd; 103 | exp_data_rd = lifo_expected.pop_back(); 104 | if(act_data_rd == exp_data_rd) begin 105 | $display("%0t Data read = %d, FIFO entry = %d", $realtime, act_data_rd, $size(lifo_expected)); 106 | end 107 | else begin 108 | $error("%0t Data read mismatch, ACT = %d, EXP =%d, FIFO entry = %d", $realtime, act_data_rd, exp_data_rd, $size(lifo_expected)); 109 | err_cnt = err_cnt + 1; 110 | end 111 | end else begin 112 | if(!lifo_empty) begin 113 | $error("%0t LIFO is empty but lifo_empty flag is not asserted",$realtime); 114 | err_cnt = err_cnt + 1; 115 | end 116 | else begin 117 | $display("%0t LIFO is empty, lifo_empty flag is asserted correctly",$realtime); 118 | end 119 | end 120 | end 121 | rd_en = 0; 122 | endtask 123 | 124 | task lifo_simul_read_write(input integer wr_data_array[$]); 125 | exp_data_wr = wr_data_array.pop_front(); 126 | @(posedge(clk)) 127 | rd_en <= 1; 128 | wr_en <= 1; 129 | data_wr <= exp_data_wr; 130 | 131 | while($size(wr_data_array) != 0) begin 132 | @(posedge(clk)) 133 | #1 134 | act_data_rd = data_rd; 135 | if(act_data_rd == exp_data_wr) begin 136 | $display("%0t Simultaneous Data read/write = %d, FIFO entry = %d", $realtime, act_data_rd, $size(lifo_expected)); 137 | end 138 | else begin 139 | $error("%0t Simultaneous Data read/write, ACT = %d, EXP = %d, FIFO entry = %d", $realtime, act_data_rd, exp_data_wr, $size(lifo_expected)); 140 | end 141 | #1 142 | exp_data_wr = wr_data_array.pop_front(); 143 | data_wr <= exp_data_wr; 144 | end 145 | @(posedge(clk)) 146 | wr_en <= 0; 147 | rd_en <= 0; 148 | #1 149 | act_data_rd = data_rd; 150 | if(act_data_rd == exp_data_wr) begin 151 | $display("%0t Simultaneous Data read/write = %d, FIFO entry = %d", $realtime, act_data_rd, $size(lifo_expected)); 152 | end 153 | else begin 154 | $error("%0t Simultaneous Data read/write, ACT = %d, EXP = %d, FIFO entry = %d", $realtime, act_data_rd, exp_data_wr, $size(lifo_expected)); 155 | end 156 | endtask 157 | 158 | integer wr_data_array[$]; 159 | integer k; 160 | 161 | task lifo_random_op_test(input integer count); 162 | i = count; 163 | while (i > 0) begin 164 | op_sel = $urandom_range(0,2); 165 | op_count = $urandom_range(1,i); 166 | i = i - op_count; 167 | case(op_sel) 168 | 0: begin // read 169 | $display("%0t LIFO Read %d times", $realtime, op_count); 170 | lifo_read(op_count); 171 | end 172 | 1: begin // write 173 | $display("%0t LIFO Write %d times", $realtime, op_count); 174 | for(k=0 ; k < op_count; k = k+1) begin 175 | wr_data_array.push_back($urandom_range(0,2**DATA_WIDTH-1)); 176 | end 177 | lifo_write(wr_data_array); 178 | end 179 | 2: begin // simultaneous read write 180 | $display("%0t LIFO Simul Read Write %d times", $realtime, op_count); 181 | for(k=0 ; k < op_count; k = k+1) begin 182 | wr_data_array.push_back($urandom_range(0,2**DATA_WIDTH-1)); 183 | end 184 | lifo_simul_read_write(wr_data_array); 185 | end 186 | endcase 187 | end 188 | endtask 189 | 190 | initial begin 191 | string vcdfile; 192 | int vcdlevel; 193 | int seed; 194 | int temp; 195 | if ($value$plusargs("VCDFILE=%s",vcdfile)) 196 | $dumpfile(vcdfile); 197 | if ($value$plusargs("VCDLEVEL=%d",vcdlevel)) 198 | $dumpvars(vcdlevel, tb); 199 | $display("Seed number: %d",vcdlevel); 200 | if ($value$plusargs("SEED=%d",seed)) begin 201 | $display("Seed number: %d",seed); 202 | temp = $urandom(seed); 203 | end 204 | 205 | repeat(TEST_WEIGHT) begin 206 | rst = 1; 207 | #100 208 | rst = 0; 209 | #100 210 | lifo_expected = {}; 211 | lifo_random_op_test(DEPTH); 212 | #1000; 213 | end 214 | if (err_cnt > 0) begin 215 | $display("\n%0t TEST FAILED",$realtime); 216 | $display("Error count = %d\n", err_cnt); 217 | end else 218 | $display("\n%0t TEST PASSED\n", $realtime); 219 | $finish; 220 | end 221 | 222 | initial begin 223 | #(SIM_TIMEOUT); 224 | $display("\n%0t TEST FAILED", $realtime); 225 | $display("SIM TIMEOUT!\n"); 226 | $finish; 227 | end 228 | 229 | endmodule 230 | -------------------------------------------------------------------------------- /List/README.md: -------------------------------------------------------------------------------- 1 | # List Module (Development in Progress) 2 | 3 | ## Overview 4 | The List module is a versatile data storage and manipulation component implemented in Verilog. It provides various operations for managing and processing data, including reading, writing, summing, and sorting functionalities. This module can be particularly useful in FPGA designs requiring data buffering, accumulation, or processing. 5 | 6 | ## Features 7 | - Data storage with configurable width and length 8 | - Multiple data manipulation operations: 9 | - Read/write operations 10 | - Element searching 11 | - Data summation with multiple implementation methods 12 | - Sorting (ascending and descending) 13 | - Configurable sum calculation methods: 14 | - Parallel sum (combinatorial) 15 | - Sequential sum 16 | - Adder tree 17 | 18 | ## Module Instantiation 19 | ``` verilog 20 | // Instantiation with custom parameters 21 | list #( 22 | .DATA_WIDTH(16), // 16-bit data elements 23 | .LENGTH(32), // List can store up to 32 elements 24 | .SUM_METHOD(1) // Use sequential summation method 25 | ) custom_list ( 26 | .clk(clk), 27 | .rst(rst), 28 | .op_sel(op_sel), 29 | .op_en(op_en), 30 | .data_in(data_in), 31 | .index_in(index_in), 32 | .data_out(data_out), 33 | .op_done(op_done), 34 | .op_error(op_error) 35 | ); 36 | ``` 37 | 38 | ## Parameters 39 | 40 | | Parameter | Description | Default | 41 | |--------------|--------------------------------------------------|---------| 42 | | DATA_WIDTH | Width of each data element in bits | 32 | 43 | | LENGTH | Maximum number of elements in the list | 8 | 44 | | SUM_METHOD | Method for calculating sum (0: parallel, 1: sequential, 2: adder tree) | 0 | 45 | 46 | ## IO Ports 47 | 48 | | Port | Direction | Width | Description | 49 | |-----------|-----------|-------------------------------|-------------------------------------------| 50 | | clk | input | 1 | System clock | 51 | | rst | input | 1 | Reset signal (active high) | 52 | | op_sel | input | 2 | Operation selector | 53 | | op_en | input | 1 | Operation enable | 54 | | data_in | input | DATA_WIDTH | Input data for write operations | 55 | | index_in | input | LENGTH_WIDTH | Index for read/write operations | 56 | | data_out | output | LENGTH_WIDTH+DATA_WIDTH | Output data | 57 | | op_done | output | 1 | Operation completion indicator | 58 | | op_error | output | 1 | Operation error indicator | 59 | 60 | ## Operation Codes 61 | 62 | | op_sel | Operation | Description | 63 | |--------|---------------------|------------------------------------------| 64 | | 3'b000 | Read | Read data from specified index | 65 | | 3'b001 | Write | Write data to specified index | 66 | | 3'b010 | Find All Indices | Find all indices matching data_in | 67 | | 3'b011 | Find First Index | Find first index matching data_in | 68 | | 3'b100 | Sum | Calculate sum of all elements | 69 | | 3'b101 | Sort Ascending | Sort elements in ascending order | 70 | | 3'b110 | Sort Descending | Sort elements in descending order | 71 | 72 | ## State Machine 73 | The module implements a simple state machine with the following states: 74 | - **IDLE**: Default state, waiting for operation 75 | - **SUM**: Calculating sum in progress (for sequential or adder tree methods) 76 | - **SORT**: Sorting the list in progress (implementation in progress) 77 | - **ACCESS_DONE**: Operation completed, transitioning back to IDLE 78 | 79 | ## Implementation Notes 80 | - The internal storage is implemented as a register array, which could be replaced with RAM for larger data sizes 81 | - The parallel sum implementation uses combinatorial logic 82 | - Sequential sum and adder tree implementations are placeholders in the current version 83 | -------------------------------------------------------------------------------- /List/src/adder.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Module Name: adder 4 | // Create Date: 11/05/2025 01:58 AM 5 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 6 | // Last Update: 08/06/2025 09:47 PM 7 | // Last Updated By: https://www.linkedin.com/in/wei-yet-ng-065485119/ 8 | // Description: 9 | // Additional Comments: 10 | // 11 | ////////////////////////////////////////////////////////////////////////////////// 12 | 13 | module adder #( 14 | parameter DATA_WIDTH = 32, 15 | parameter LENGTH = 8, //need not to be power of 2 16 | parameter SUM_METHOD = 0, // 0: parallel (combo) sum, 1: sequential sum, 2: adder tree. //ICARUS does not support string overriden to parameter in CLI. 17 | localparam LENGTH_WIDTH = $clog2(LENGTH), 18 | localparam DATA_OUT_WIDTH = $clog2(LENGTH)+DATA_WIDTH-1 19 | )( 20 | input wire clk, 21 | input wire rst, 22 | input wire [LENGTH-1:0][DATA_WIDTH-1:0] data_in, 23 | input wire sum_en, 24 | output reg [LENGTH_WIDTH+DATA_WIDTH-1:0] sum_result, 25 | output reg sum_done, 26 | output reg sum_in_progress 27 | ); 28 | 29 | localparam NO_OF_STAGE = $clog2(LENGTH); 30 | localparam STG_PTR_WIDTH = $clog2(NO_OF_STAGE); 31 | localparam TOTAL_INPUT_INT = 2**NO_OF_STAGE; // round up to integer, LENGTH needs not to be in power of 2. 32 | 33 | reg [LENGTH_WIDTH-1:0] cur_ptr; 34 | reg [STG_PTR_WIDTH-1:0] stg_ptr; 35 | reg [DATA_WIDTH-1:0] output_stage[TOTAL_INPUT_INT-1:0][NO_OF_STAGE-1:0]; 36 | 37 | generate 38 | if(SUM_METHOD == 0) begin //: parallel sum (Combo) 39 | integer i; 40 | always @(*) begin 41 | for(i=0; i= LENGTH) begin 124 | current_state <= ACCESS_DONE; //LENGTH is full, cannot insert 125 | op_done <= 1'b1; 126 | op_error <= 1'b1; 127 | end else if (index_in >= data_count) begin 128 | current_state <= ACCESS_DONE; 129 | data_stored[data_count] <= data_in; //insert data at the end 130 | data_count <= data_count + 1; //increment data count 131 | op_done <= 1'b1; 132 | op_error <= 1'b0; 133 | end else begin 134 | current_state <= ACCESS_DONE; //insert data at index_in 135 | for(i = 0; i >= LENGTH; i = i + 1) begin 136 | if(i == index_in) begin 137 | data_stored[LENGTH-1-i] <= data_in; //insert data at index_in 138 | end else if(i > index_in) begin 139 | data_stored[LENGTH-1-i] <= data_stored[LENGTH-1-i-1]; //shift right 140 | end else begin 141 | data_stored[LENGTH-1-i] <= data_stored[LENGTH-1-i]; //keep the same 142 | end 143 | end 144 | data_count <= data_count + 1; //increment data count 145 | op_done <= 1'b1; 146 | op_error <= 1'b0; 147 | end 148 | end else if (op_is_read) begin 149 | if (index_in >= data_count) begin 150 | current_state <= ACCESS_DONE; //index_in is out of range 151 | op_done <= 1'b1; 152 | op_error <= 1'b1; 153 | end else begin 154 | current_state <= ACCESS_DONE; 155 | data_out <= {{LENGTH_WIDTH{1'b0}},data_stored[index_in]}; 156 | op_done <= 1'b1; 157 | op_error <= 1'b0; 158 | end 159 | end else if (op_is_sum) begin 160 | if(SUM_METHOD == 0) begin //PARALLEL SUM 161 | current_state <= ACCESS_DONE; 162 | data_out <= sum_result; 163 | op_done <= 1'b1; 164 | op_error <= 1'b0; 165 | end else if(SUM_METHOD == 1) begin //SEQUENTIAL SUM 166 | current_state <= SUM; 167 | op_in_progress <= 1'b1; 168 | sum_en <= 1'b1; 169 | end else if (SUM_METHOD == 2) begin //ADDER TREE 170 | current_state <= SUM; 171 | op_in_progress <= 1'b1; 172 | sum_en <= 1'b1; 173 | end 174 | end else if (op_is_find_1st_index) begin 175 | current_state <= SEARCH_1ST; 176 | op_in_progress <= 1'b1; 177 | cur_ptr <= 'd0; 178 | end else if (op_is_find_all_index) begin 179 | current_state <= SEARCH_ALL; 180 | op_in_progress <= 1'b1; 181 | cur_ptr <= 'd0; 182 | found <= 1'b0; 183 | end else if (op_is_sort_asc) begin 184 | current_state <= SORT; 185 | op_in_progress <= 1'b1; 186 | sort_en <= 1'b1; 187 | sort_order <= 1'b0; 188 | end else if (op_is_sort_des) begin 189 | current_state <= SORT; 190 | op_in_progress <= 1'b1; 191 | sort_en <= 1'b1; 192 | sort_order <= 1'b1; 193 | end else if(op_en) begin // OP selected is not available : OP_ERROR 194 | current_state <= ACCESS_DONE; 195 | op_done <= 1'b1; 196 | op_error <= 1'b0; 197 | end else begin 198 | current_state <= IDLE; 199 | op_done <= 1'b0; 200 | op_error <= 1'b0; 201 | op_in_progress <= 1'b0; 202 | sum_en <= 1'b0; 203 | sort_en <= 1'b0; 204 | end 205 | found <= 1'b0; 206 | cur_ptr <= 'b0; 207 | end 208 | SEARCH_1ST: begin 209 | if(data_stored[cur_ptr] == data_in) begin 210 | current_state <= ACCESS_DONE; 211 | data_out <= {{DATA_WIDTH{1'b0}},cur_ptr}; 212 | op_done <= 1'b1; 213 | op_in_progress <= 1'b0; 214 | op_error <= 1'b0; 215 | end else if(cur_ptr == LENGTH-1) begin 216 | current_state <= ACCESS_DONE; 217 | op_done <= 1'b1; 218 | op_error <= 1'b1; 219 | end else begin 220 | cur_ptr <= cur_ptr + 'b1; 221 | end 222 | end 223 | SEARCH_ALL: begin 224 | if(cur_ptr < LENGTH-1) begin 225 | if(data_stored[cur_ptr] == data_in) begin 226 | data_out <= {{DATA_WIDTH{1'b0}},cur_ptr}; 227 | op_done <= 1'b1; 228 | op_error <= 1'b0; 229 | found <= 1'b1; 230 | cur_ptr <= cur_ptr + 'b1; 231 | end else begin 232 | cur_ptr <= cur_ptr + 'b1; 233 | end 234 | end else begin 235 | if(data_stored[cur_ptr] == data_in) begin 236 | data_out <= {{DATA_WIDTH{1'b0}},cur_ptr}; 237 | op_done <= 1'b1; 238 | op_in_progress <= 1'b0; 239 | op_error <= 1'b0; 240 | end else begin 241 | op_done <= 1'b1; 242 | op_in_progress <= 1'b0; 243 | op_error <= found; 244 | end 245 | end 246 | end 247 | SORT: if(sort_done) begin 248 | current_state <= ACCESS_DONE; 249 | op_done <= 1'b1; 250 | data_stored <= data_sorted; 251 | op_in_progress <= 1'b0; 252 | op_error <= 1'b0; 253 | sort_en <= 1'b0; 254 | end 255 | SUM: if(sum_done) begin 256 | current_state <= ACCESS_DONE; 257 | data_out <= sum_result; 258 | op_done <= 1'b0; 259 | op_in_progress <= 1'b0; 260 | op_error <= 1'b0; 261 | sum_en <= 1'b0; 262 | end 263 | ACCESS_DONE: begin 264 | current_state <= IDLE; 265 | op_done <= 1'b0; 266 | op_error <= 1'b0; 267 | op_in_progress <= 1'b0; 268 | end 269 | default: begin 270 | current_state <= IDLE; 271 | end 272 | endcase 273 | end 274 | end 275 | endmodule 276 | -------------------------------------------------------------------------------- /List/src/rtl_src.f: -------------------------------------------------------------------------------- 1 | list.sv 2 | adder.sv -------------------------------------------------------------------------------- /List/src/sorter.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Module Name: sorter 4 | // Create Date: 12/05/2025 11:32 AM 5 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 6 | // Last Update: 12/05/2025 11:32 PM 7 | // Last Updated By: https://www.linkedin.com/in/wei-yet-ng-065485119/ 8 | // Description: 9 | // Additional Comments: Just simpler insertion sort 10 | // 11 | ////////////////////////////////////////////////////////////////////////////////// 12 | 13 | module sorter #( 14 | parameter DATA_WIDTH = 32, 15 | parameter LENGTH = 8, 16 | localparam LENGTH_WIDTH = $clog2(LENGTH) 17 | )( 18 | input wire clk, 19 | input wire rst, 20 | input wire [LENGTH-1:0][DATA_WIDTH-1:0] data_in, 21 | input wire sort_en, 22 | input wire sort_order, 23 | output reg [LENGTH-1:0][DATA_WIDTH-1:0] data_sorted, 24 | output reg sort_done, 25 | output reg sort_in_progress 26 | ); 27 | 28 | localparam IDLE = 3'b000; 29 | localparam NEXT_KEY = 3'b001; 30 | localparam COMP_AND_SWAP = 3'b010; 31 | localparam SORT_DONE = 3'b011; 32 | reg [LENGTH_WIDTH-1:0] key_ptr; 33 | reg [LENGTH_WIDTH-1:0] cur_ptr; 34 | reg [DATA_WIDTH-1:0] temp; 35 | reg [2:0] current_state; 36 | reg [LENGTH-1:0][DATA_WIDTH-1:0] data_temp; 37 | 38 | // always @ (*) begin 39 | // data_temp <= data_in; 40 | // end 41 | 42 | always @ (posedge clk, posedge rst) begin 43 | if(rst) begin 44 | sort_done <= 1'b0; 45 | sort_in_progress <= 1'b0; 46 | current_state <= IDLE; 47 | for(integer i=0; i temp) begin 83 | data_temp[cur_ptr+1] <= data_temp[cur_ptr]; 84 | data_temp[cur_ptr] <= temp; 85 | cur_ptr <= cur_ptr - 'b1; 86 | current_state <= (cur_ptr > 'd0) ? COMP_AND_SWAP : NEXT_KEY; 87 | end else begin 88 | data_temp[cur_ptr+1] <= temp; 89 | current_state <= NEXT_KEY; 90 | end 91 | end else begin 92 | if(data_in[cur_ptr] < temp) begin 93 | data_temp[cur_ptr+1] <= data_temp[cur_ptr]; 94 | data_temp[cur_ptr] <= temp; 95 | cur_ptr <= cur_ptr - 'b1; 96 | current_state <= (cur_ptr > 'd0) ? COMP_AND_SWAP : NEXT_KEY; 97 | end else begin 98 | data_temp[cur_ptr+1] <= temp; 99 | current_state <= NEXT_KEY; 100 | end 101 | end 102 | end 103 | 104 | SORT_DONE: begin 105 | current_state <= IDLE; 106 | sort_done <= 1'b0; 107 | sort_in_progress <= 1'b0; 108 | end 109 | endcase 110 | end 111 | end 112 | 113 | endmodule -------------------------------------------------------------------------------- /List/tb/sv/tb.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Create Date: 08/06/2025 09:10 PM 4 | // Module Name: tb 5 | // Description: Supported Operation 6 | // 0. Read(index_in) -> data_out 7 | // 1. Insert(index_in, data_in) 8 | // 2. Find_all_index(index_in) -> data_out (array of indexes) 9 | // 3. Find_1st_index(index_in) -> data_out (index of first occurrence) 10 | // 4. Sum() -> data_out (sum of all elements) 11 | // 5. Sort_Asc() 12 | // 6. Sort_Des() 13 | // Additional Comments: 14 | // 15 | ////////////////////////////////////////////////////////////////////////////////// 16 | 17 | 18 | module tb( 19 | ); 20 | // DUT parameter 21 | localparam DUT_DATA_WIDTH = 8; 22 | localparam DUT_LENGTH = 8; // Max number of nodes in the list 23 | localparam DUT_SUM_METHOD = 0; // 0: parallel (combo) sum, 1: sequentia sum, 2: adder tree. //ICARUS does not support string overriden to parameter in CLI. 24 | 25 | // TB parameter 26 | localparam TB_CLK_PERIOD = 25; 27 | localparam TB_TEST_WEIGHT = 1; 28 | localparam SIM_TIMEOUT = 500000; 29 | 30 | localparam LENGTH_WIDTH = $clog2(DUT_LENGTH); 31 | localparam DATA_OUT_WIDTH = LENGTH_WIDTH+DUT_DATA_WIDTH-1; 32 | 33 | localparam OP_Read = 3'b000; 34 | localparam OP_Insert = 3'b001; 35 | localparam OP_Find_all_index = 3'b010; 36 | localparam OP_Find_1st_index = 3'b011; 37 | localparam OP_Sum = 3'b100; 38 | localparam OP_Sort_Asc = 3'b101; 39 | localparam OP_Sort_Des = 3'b110; 40 | localparam OP_Delete = 3'b111; 41 | 42 | integer err_cnt = 0; 43 | 44 | // input 45 | reg clk = 0; 46 | reg rst = 0; 47 | reg [2:0] op_sel = 0; 48 | reg op_en = 0; // Enable operation 49 | reg [DUT_DATA_WIDTH-1:0] data_in = 0; 50 | reg [LENGTH_WIDTH-1:0] index_in = 0; // Index for Insert_At_Index and Delete_At_Index 51 | // output 52 | wire [DUT_DATA_WIDTH+LENGTH_WIDTH-1:0] data_out; // Data out for Read and Insert_At_Index 53 | wire op_done; // Operation done flag 54 | wire op_in_progress; // Operation in progress flag 55 | wire op_error; // Operation error flag 56 | 57 | 58 | `ifdef XILINX_GLS 59 | // for xilinx gate sim 60 | glbl glbl(); 61 | `endif 62 | 63 | list #( 64 | .DATA_WIDTH(DUT_DATA_WIDTH), 65 | .LENGTH(DUT_LENGTH), 66 | .SUM_METHOD(DUT_SUM_METHOD) 67 | ) DUT ( 68 | /*input wire */.clk(clk), 69 | /*input wire */.rst(rst), 70 | /*input wire [1:0] */.op_sel(op_sel), 71 | /*input wire */.op_en(op_en), 72 | /*input wire [DATA_WIDTH-1:0] */.data_in(data_in), 73 | /*input wire [LENGTH_WIDTH-1:0] */.index_in(index_in), 74 | /*output reg [LENGTH_WIDTH+DATA_WIDTH-1:0]*/.data_out(data_out), 75 | /*output reg */.op_done(op_done), 76 | /*output reg */.op_in_progress(op_in_progress), 77 | /*output reg */.op_error(op_error) 78 | ); 79 | 80 | always #(TB_CLK_PERIOD/2) clk = ~clk; 81 | 82 | integer list_exp[$]; // expected list contents 83 | integer temp[$]; // temporary storage for find_index results 84 | 85 | task list_print_contents(); 86 | for (integer i = 0; i < list_exp.size(); i = i + 1) begin 87 | $write("%0d ", list_exp[i]); // $write does not add newline 88 | end 89 | $write("\n"); 90 | endtask 91 | 92 | task find_index (input integer value); // input integer list[$], ref integer addr[$]); icarus does not support "ref" (pass by reference). 93 | begin 94 | temp = {}; 95 | for (integer i = 0; i < list_exp.size(); i = i + 1) begin 96 | if(value == list_exp[i]) begin 97 | temp.push_back(i); 98 | $display("%0t value %0d is found at Index %0d", $realtime, value, i); 99 | end 100 | end 101 | end 102 | endtask 103 | 104 | task read (input integer index); 105 | begin 106 | $display("%0t OP_Read at index %0d", $realtime,index); 107 | @(posedge (clk)); 108 | #1 109 | op_sel = OP_Read; 110 | op_en = 1; 111 | index_in = index; 112 | @(posedge (clk)); 113 | #1 114 | wait (op_done); 115 | if(index >= list_exp.size()) begin 116 | if(op_error) begin 117 | $display("%0t Data read out of bound, fault flag is asserted correctly",$realtime); 118 | end else begin 119 | $error("%0t Data read out of bound, fault flag is not asserted",$realtime); 120 | err_cnt = err_cnt + 1; 121 | end 122 | end else begin 123 | if(op_error) begin 124 | $error("%0t fault flag is asserted incorrectly",$realtime); 125 | err_cnt = err_cnt + 1; 126 | end 127 | if(data_out == list_exp[index]) begin 128 | $display("%0t Data read: %0d",$realtime,data_out); 129 | end else begin 130 | $error("%0t Data read: %0d, Data Exp: %0d", $realtime, data_out, list_exp[index]); 131 | err_cnt = err_cnt + 1; 132 | end 133 | end 134 | @(posedge (clk)); 135 | #1 136 | op_en = 0; 137 | end 138 | endtask 139 | 140 | integer count = 0; 141 | 142 | task read_n_burst(input integer n); 143 | begin 144 | $display("%0t OP_Read burst %d time", $realtime,n); 145 | count = 0; 146 | @(posedge (clk)); 147 | #1 148 | op_sel = OP_Read; 149 | op_en = 1; 150 | index_in = count; 151 | while (count < n) begin 152 | @(posedge (clk)); 153 | #1 154 | wait (op_done); 155 | if(index_in >= list_exp.size()) begin 156 | if(op_error) begin 157 | $display("%0t Data read out of bound, fault flag is asserted correctly",$realtime); 158 | end else begin 159 | $error("%0t Data read out of bound, fault flag is not asserted",$realtime); 160 | err_cnt = err_cnt + 1; 161 | end 162 | end else begin 163 | if(op_error) begin 164 | $error("%0t fault flag is asserted incorrectly",$realtime); 165 | err_cnt = err_cnt + 1; 166 | end 167 | if(data_out == list_exp[index_in]) begin 168 | $display("%0t Data read: %0d",$realtime,data_out); 169 | end else begin 170 | $error("%0t Data read: %0d, Data Exp: %0d", $realtime, data_out, list_exp[index_in]); 171 | err_cnt = err_cnt + 1; 172 | end 173 | end 174 | count = count + 1; 175 | index_in = count; 176 | end 177 | @(posedge (clk)); 178 | #1 179 | op_en = 0; 180 | end 181 | endtask 182 | 183 | task insert(input integer index, input integer value); 184 | begin 185 | $display("%0t OP_Insert at index %d, value %d", $realtime,index,value); 186 | @(posedge (clk)); 187 | #1 188 | op_sel = OP_Insert; 189 | op_en = 1; 190 | index_in = index; 191 | data_in = value; 192 | @(posedge (clk)); 193 | #1 194 | wait (op_done); 195 | if(list_exp.size() >= DUT_LENGTH) begin 196 | if(op_error) begin 197 | $display("%0t Data insert out of bound, fault flag is asserted correctly",$realtime); 198 | end else begin 199 | $error("%0t Data insert out of bound, fault flag is not asserted",$realtime); 200 | err_cnt = err_cnt + 1; 201 | end 202 | end else begin 203 | if(op_error) begin 204 | $error("%0t fault flag is asserted incorrectly",$realtime); 205 | err_cnt = err_cnt + 1; 206 | end 207 | if(index > list_exp.size()) begin 208 | list_exp.push_back(value); // Insert at the end if index is out of bound 209 | end else begin 210 | list_exp.insert(index, value); 211 | end 212 | end 213 | @(posedge (clk)); 214 | #1 215 | op_en = 0; 216 | list_print_contents(); 217 | end 218 | endtask 219 | 220 | task delete(input integer index); 221 | begin 222 | $display("%0t OP_Delete at index %d", $realtime,index); 223 | @(posedge (clk)); 224 | #1 225 | op_sel = OP_Delete; 226 | op_en = 1; 227 | index_in = index; 228 | @(posedge (clk)); 229 | #1 230 | wait (op_done); 231 | if(index >= list_exp.size()) begin 232 | if(op_error) begin 233 | $display("%0t Data delete out of bound, fault flag is asserted correctly",$realtime); 234 | end else begin 235 | $error("%0t Data delete out of bound, fault flag is not asserted",$realtime); 236 | err_cnt = err_cnt + 1; 237 | end 238 | end else begin 239 | if(op_error) begin 240 | $error("%0t fault flag is asserted incorrectly",$realtime); 241 | err_cnt = err_cnt + 1; 242 | end 243 | list_exp.delete(index); 244 | end 245 | @(posedge (clk)); 246 | #1 247 | op_en = 0; 248 | list_print_contents(); 249 | end 250 | endtask 251 | 252 | task sort_acending(); 253 | begin 254 | int temp; 255 | $display("%0t OP_Sort_Asc Request", $realtime); 256 | @(posedge (clk)); 257 | #1 258 | op_sel = OP_Sort_Asc; 259 | op_en = 1; 260 | @(posedge (clk)); 261 | #1 262 | wait (op_done); 263 | for (integer i = 0; i < list_exp.size() - 1; i = i + 1) begin 264 | for (integer j = i + 1; j < list_exp.size(); j = j + 1) begin 265 | if(list_exp[i] > list_exp[j]) begin 266 | temp = list_exp[i]; 267 | list_exp[i] = list_exp[j]; 268 | list_exp[j] = temp; 269 | end 270 | end 271 | $display("%0t OP_Sort_Asc Complete", $realtime); 272 | end 273 | @(posedge (clk)); 274 | #1 275 | op_en = 0; 276 | list_print_contents(); 277 | end 278 | endtask 279 | 280 | task sort_desending(); 281 | begin 282 | int temp; 283 | $display("%0t OP_Sort_Des Request", $realtime); 284 | @(posedge (clk)); 285 | #1 286 | op_sel = OP_Sort_Des; 287 | op_en = 1; 288 | @(posedge (clk)); 289 | #1 290 | wait (op_done); 291 | for (integer i = 0; i < list_exp.size() - 1; i = i + 1) begin 292 | for (integer j = i + 1; j < list_exp.size(); j = j + 1) begin 293 | if(list_exp[i] < list_exp[j]) begin 294 | temp = list_exp[i]; 295 | list_exp[i] = list_exp[j]; 296 | list_exp[j] = temp; 297 | end 298 | end 299 | end 300 | $display("%0t OP_Sort_Des Complete", $realtime); 301 | @(posedge (clk)); 302 | #1 303 | op_en = 0; 304 | list_print_contents(); 305 | end 306 | endtask 307 | 308 | task direct_op_test(); 309 | begin 310 | $display("Starting direct operation test"); 311 | 312 | // Insert operation 313 | insert($urandom_range(0,2**DUT_DATA_WIDTH-1), 0); 314 | insert($urandom_range(0,2**DUT_DATA_WIDTH-1), 1); 315 | insert($urandom_range(0,2**DUT_DATA_WIDTH-1), 2); // Out of bound insert 316 | 317 | // Read operation 318 | read(0); 319 | read(1); 320 | read(2); 321 | 322 | // // Sort Ascending 323 | // sort_acending(); 324 | 325 | // // Read after sort 326 | // read_n_burst(3); 327 | 328 | // // Sort Descending 329 | // sort_desending(); 330 | 331 | // // Read after sort 332 | // read_n_burst(3); 333 | 334 | end 335 | endtask 336 | 337 | initial begin 338 | string vcdfile; 339 | int vcdlevel; 340 | int seed; 341 | int temp; 342 | 343 | rst = 1'b1; 344 | if ($value$plusargs("VCDFILE=%s",vcdfile)) 345 | $dumpfile(vcdfile); 346 | if ($value$plusargs("VCDLEVEL=%d",vcdlevel)) 347 | $dumpvars(vcdlevel,tb); 348 | if ($value$plusargs("SEED=%d",seed)) begin 349 | temp = $urandom(seed); 350 | $display("Seed = %d",seed); 351 | end 352 | #100; 353 | rst = 1'b0; 354 | direct_op_test(); 355 | 356 | #1000; 357 | 358 | if (err_cnt > 0) begin 359 | $display("\n%0t TEST FAILED",$realtime); 360 | $display("Error count = %d\n", err_cnt); 361 | end else 362 | $display("\n%0t TEST PASSED\n", $realtime); 363 | $finish; 364 | end 365 | 366 | initial begin 367 | #(SIM_TIMEOUT) 368 | $display("\n%0t TEST FAILED", $realtime); 369 | $display("SIM TIMEOUT!\n"); 370 | $finish; 371 | end 372 | 373 | endmodule 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 | 7 | ![Verilog](https://img.shields.io/badge/Verilog-IEEE_1364--2005-blue) 8 | ![SystemVerilog](https://img.shields.io/badge/SystemVerilog-IEEE_1800--2017-blue) 9 | ![Python](https://img.shields.io/badge/Python-3.8%2B-blue) 10 | ![Status](https://img.shields.io/badge/Status-In_Development-yellow) 11 | 12 |
13 | 14 | # RTLStructLib 15 | Highly optimized, synthesizable data structures module/IP library for hardware design 16 | 17 | ### Overview 18 | RTLStructlib is an open-source project providing a collection of synthesizable RTL data structures implemented at the Register-Transfer Level (RTL). These modules are designed for high performance, scalability, and ease of integration into digital systems, serving as a standard library for FPGA and ASIC engineers. 19 | By using these pre-built RTL modules, engineers can accelerate development, reduce verification time, and focus on higher-level system design. 20 | 21 | ### Features 22 | ✅ Synthesizable, Optimized, Modular and Reusable
23 | ✅ Fully parameterized
24 | ✅ Comprehensive verification sequence and testbench
25 | ✅ Verification Agent (WIP)
26 | ✅ Open-source and community-driven
27 | 28 | ### Supported Data Structures 29 | - FIFO (First-In-First-Out) Queue – Parameterized depth, support for synchronous & asynchronous modes
30 | - LIFO (Last-In-First-Out) Stack – Configurable width and depth
31 | - Singly Linked List – Efficient memory utilization, dynamic data handling
32 | - Doubly Linked List – Bi-directional traversal support
33 | - Table - Indexed storage mechanism, similar to a register file, enabling rapid direct access and simultaneous read write access to data without hashing.
34 | - List (WIP) - Support sorting, find_index, delete, insert operations
35 | - Circular Linked List (WIP) 36 | - Hash Table – Optimized for high-speed lookups, currently only supports modulus hashing and simple multi-staged chaining to handle collision
37 | - Systolic Array (WIP) - Organizes processing elements in a regular grid where data flows rhythmically, enabling parallel computation.
38 | - Binary Tree (WIP) – Fundamental structure for hierarchical data organization
39 | - AVL Tree (WIP) – Self-balancing binary search tree for efficient operations
40 | - And More and More and More (WIP) 41 | 42 | ### License 43 | This project is licensed under the MIT License – see the LICENSE file for details. 44 | 45 | ### Getting Started 46 | 1️⃣ Install required tools and package 47 | ``` bash 48 | sudo apt install make git iverilog yosys gtkwave 49 | pip install cocotb 50 | pip install cocotb-bus 51 | ``` 52 | 53 | 1️⃣ Clone the Repository
54 | ``` bash 55 | git clone https://github.com/Weiyet/RTL_Data_Structure.git 56 | ``` 57 | 58 | 2️⃣ Directory Structure of Each Data Structure Module
59 | ```` 60 | 📦 \/ # Data Structure Module as folder name
61 | ├── 📃 readme.md # Documentation of waveform, modules IOs, parameter.
62 | ├── 📂 src/ # RTL Source Code
63 | │ ├── 📃 rtl_list.f # RTL file list required for the modules
64 | ├── 📂 tb/ # Testbench Directory
65 | │ ├── 📂 cocotb/ # Python Cocotb (Non-UVM) Testbench
66 | │ ├── 📂 sv/ # SystemVerilog (Non-UVM) Testbench
67 | ├── 📂 vip/ # Verification IP
68 | │ ├── 📂 uvm/ # system verilog UVM
69 | | | ├── 📃 readme.md # Documentation of VIP
70 | │ ├── 📂 pyuvm/ # python UVM
71 | | | ├── 📃 readme.md # Documentation of VIP
72 | ```` 73 | 2️⃣ RTL Simulation and Verification 74 | ``` bash 75 | # System Verilog Simulation 76 | cd /tb/sv 77 | make sim 78 | # Python CocoTB Simulation 79 | cd /tb/cocotb 80 | make 81 | ``` 82 | 3️⃣ Synthesis and Netlist simulation 83 | ``` bash 84 | make synth 85 | ``` 86 | 4️⃣ To view VCD waveform 87 | ``` bash 88 | gtkwave 89 | ``` 90 | 5️⃣ Integrate to your project 91 | Include file list /src/rtl_list.f to your simulation or project. 92 | 93 | ### Work in Progress/Future Works 🚀 94 | 🔹 Implementing Hash Table, Binary Tree, AVL Tree and more and more
95 | 🔹 Providing comprehensive test benches (TB) for verification
96 | 🔹 Exploring pyUVM for developing UVM & pyUVM agents
97 | 🔹 Improving performance & adding more use cases
98 | 99 | ### Disclaimer 100 | Hardware is often highly customized — these modules are designed as references, and you're encouraged to tweak them as needed (e.g., swap registers with RAM, adjust logic latency based on your STA, use content addressable RAM instead of RTL hash table). 101 | 102 | 103 | -------------------------------------------------------------------------------- /Singly_Linked_List/README.md: -------------------------------------------------------------------------------- 1 | # Singly Linked List 2 | 3 | A configurable hardware implementation of a singly linked list data structure in Verilog. The module supports basic linked list operations including insertion, deletion, and reading at specific addresses or indices, with full hardware synchronization and status monitoring. 4 | 5 | ## Features 6 | 7 | - Configurable data width and maximum node count 8 | - Multiple insertion and deletion operations 9 | - Hardware-based node management 10 | - Status monitoring (full, empty, length) 11 | - Fault detection 12 | - Head and tail pointer tracking 13 | - Synchronous operation with start/done handshaking 14 | 15 | ## Parameters 16 | 17 | | Parameter | Description | Default | 18 | |-------------|--------------------------------------------------|---------| 19 | | DATA_WIDTH | Width of data stored in each node | 8 | 20 | | MAX_NODE | Maximum number of nodes supported | 8 | 21 | 22 | ## Interface Signals 23 | 24 | | Signal | Direction | Width | Description | 25 | |-----------------|-----------|---------------|------------------------------------------------| 26 | | clk | Input | 1 | System clock | 27 | | rst | Input | 1 | Active high reset | 28 | | data_in | Input | DATA_WIDTH | Input data for insertion | 29 | | addr_in | Input | ADDR_WIDTH | Target address/index for operations | 30 | | op | Input | 3 | Operation code | 31 | | op_start | Input | 1 | Start signal for operation | 32 | | op_done | Output | 1 | Operation completion signal | 33 | | data_out | Output | DATA_WIDTH | Output data from read operations | 34 | | next_node_addr | Output | ADDR_WIDTH | Address of next node in list | 35 | | length | Output | ADDR_WIDTH | Current number of nodes in list | 36 | | head | Output | ADDR_WIDTH | Address of first node | 37 | | tail | Output | ADDR_WIDTH | Address of last node | 38 | | full | Output | 1 | List full indicator | 39 | | empty | Output | 1 | List empty indicator | 40 | | fault | Output | 1 | Error indicator | 41 | 42 | ## Supported Operations 43 | 44 | | Op Code | Operation | Description | 45 | |---------|--------------------|---------------------------------------------------------| 46 | | 0 | Read_Addr | Read data at specified address | 47 | | 1 | Insert_At_Addr | Insert new node at specified address | 48 | | 2 | Delete_Value | Delete first occurrence of specified value | 49 | | 3 | Delete_At_Addr | Delete node at specified address | 50 | | 5 | Insert_At_Index | Insert new node at specified index | 51 | | 7 | Delete_At_Index | Delete node at specified index | 52 | 53 | ## State Machine 54 | 55 | The module implements a state machine with the following states: 56 | 57 | 1. **IDLE (3'b000)** 58 | - Waits for operation start 59 | - Validates operation parameters 60 | - Initializes operation sequence 61 | 62 | 2. **FIND_ADDR (3'b001)** 63 | - Traverses list to find target address 64 | - Used for address-based operations 65 | 66 | 3. **FIND_VALUE (3'b010)** 67 | - Traverses list to find target value 68 | - Used for value-based deletion 69 | 70 | 4. **INSERT_STG1 (3'b011)** 71 | - First stage of node insertion 72 | - Updates node links 73 | 74 | 5. **EXECUTE (3'b100)** 75 | - Completes operation 76 | - Updates status signals 77 | 78 | 6. **FAULT (3'b101)** 79 | - Handles error conditions 80 | 81 | ## Usage Example 82 | 83 | ```verilog 84 | // Instantiate a linked list with 16 nodes and 32-bit data 85 | singly_linked_list #( 86 | .DATA_WIDTH(32), 87 | .MAX_NODE(16) 88 | ) list_inst ( 89 | .clk(system_clock), 90 | .rst(reset), 91 | .data_in(input_data), 92 | .addr_in(target_addr), 93 | .op(operation_code), 94 | .op_start(start_signal), 95 | .op_done(done_signal), 96 | .data_out(output_data), 97 | .next_node_addr(next_addr), 98 | .length(list_length), 99 | .head(head_addr), 100 | .tail(tail_addr), 101 | .full(list_full), 102 | .empty(list_empty), 103 | .fault(error_signal) 104 | ); 105 | ``` 106 | 107 | ## Operation Protocol 108 | 109 | 1. Set `data_in` and/or `addr_in` as required by operation 110 | 2. Set `op` to desired operation code 111 | 3. Assert `op_start` for one clock cycle 112 | 4. Wait for `op_done` assertion 113 | 5. Check `fault` signal for operation status 114 | 6. Read `data_out` if applicable 115 | 116 | ## Reset Behavior 117 | 118 | On assertion of reset: 119 | - All nodes are invalidated 120 | - Head and tail pointers set to NULL 121 | - Length cleared to zero 122 | - All node data cleared 123 | - All status signals deasserted 124 | 125 | ## Implementation Notes 126 | 127 | 1. **Memory Organization**: 128 | - Next node pointers stored separately 129 | - Valid bits for node tracking 130 | 131 | 2. **Address Handling**: 132 | - NULL address = MAX_NODE 133 | - Valid addresses: 0 to (MAX_NODE-1) 134 | 135 | 3. **Error Conditions**: 136 | - List full during insertion 137 | - List empty during deletion/read 138 | - Invalid address/index 139 | - Address overflow 140 | 141 | ## Limitations 142 | 143 | - Fixed maximum size defined by MAX_NODE 144 | - Sequential search for operations 145 | - Single operation at a time 146 | - No concurrent access support 147 | - User could actually use distributed RAM to stored data if necessary 148 | -------------------------------------------------------------------------------- /Singly_Linked_List/src/rtl_file.f: -------------------------------------------------------------------------------- 1 | singly_linked_list.sv 2 | -------------------------------------------------------------------------------- /Singly_Linked_List/tb/cocotb/Makefile: -------------------------------------------------------------------------------- 1 | SIM ?= icarus 2 | TOPLEVEL_LANG ?= verilog 3 | WAVES ?= 1 #set 1 to enable waveform dump. 4 | 5 | PWD=$(shell pwd) 6 | 7 | #export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) 8 | 9 | VERILOG_SOURCES = $(PWD)/../../src/singly_linked_list.sv 10 | 11 | # DUT Top 12 | TOPLEVEL = singly_linked_list 13 | # top python file name 14 | MODULE = tb 15 | 16 | #use , separtor to run multiple TESTCASE, by default all @cocotb.test will be run 17 | #TESTCASE = direct_index_op_test , direct_addr_op_test 18 | 19 | COMPILE_ARGS = -Ptable_top.DATA_WIDTH=8 # DUT parameter #"-p" (parameter) iverilog command flags 20 | COMPILE_ARGS += -Ptable_top.MAX_NODE=8 # DUT parameter #"-p" (parameter) iverilog command flags 21 | #run make clean before running with new parameter. 22 | 23 | #Set RANDOM_SEED number 24 | #PLUSARGS = +seed=1716033254 25 | COCOTB_HDL_TIMEUNIT = 1ns 26 | COCOTB_HDL_TIMEPRECISION = 1ps 27 | 28 | ifeq ($(SIM), icarus) 29 | $(shell echo 'module iverilog_dump();' > iverilog_dump.v) 30 | $(shell echo 'initial begin' >> iverilog_dump.v) 31 | $(shell echo ' $$dumpfile("$(TOPLEVEL).vcd");' >> iverilog_dump.v) 32 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 33 | $(shell echo 'end' >> iverilog_dump.v) 34 | $(shell echo 'endmodule' >> iverilog_dump.v) 35 | VERILOG_SOURCES += $(PWD)/iverilog_dump.v 36 | COMPILE_ARGS += -s iverilog_dump 37 | endif 38 | 39 | include $(shell cocotb-config --makefiles)/Makefile.sim 40 | 41 | 42 | -------------------------------------------------------------------------------- /Singly_Linked_List/tb/sv/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DUT ?= singly_linked_list 3 | SIM_OPTS ?= ../../src/singly_linked_list.sv 4 | SEED ?= $$(shuf -i 1-10000 -n 1) 5 | 6 | # DUT parameter #"-p" iverilog command flags 7 | # COMPILE_ARGS = -P tb.DUT_DATA_WIDTH=8 8 | # COMPILE_ARGS += -P tb.DUT_MAX_NODE=8 9 | # COMPILE_ARGS += -P tb.TB_CLK_PERIOD=25 10 | # COMPILE_ARGS += -P tb.TB_TEST_WEIGHT=1 11 | # COMPILE_ARGS += -P tb.TB_CLK_PERIOD=100 12 | # COMPILE_ARGS += -P tb.SIM_TIMEOUT=500000 13 | 14 | XILINX_LIB_URL ?= https://github.com/Xilinx/XilinxUnisimLibrary.git 15 | XILINX_LIB_DIR ?= XilinxUnisimLibrary/verilog/src 16 | 17 | sim: gen_sim 18 | ./${DUT}.sim +VCDFILE=sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee sim.log 19 | 20 | gen_sim: tb.sv ${SIM_OPTS} # to generate executable file by using iverilator 21 | iverilog -g2012 -s tb ${COMPILE_ARGS} -o ${DUT}.sim $^ 22 | 23 | synth: ${SIM_OPTS} 24 | yosys -p synth_xilinx ${SIM_OPTS} -L synth.log -o ${DUT}.netlist.v 25 | #timeout 26 | 27 | netlist_sim: gen_netlist_sim 28 | ./${DUT}.netlist.sim +VCDFILE=netlist_sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee netlist_sim.log 29 | 30 | gen_netlist_sim: tb.sv ${DUT}.netlist.v | ${XILINX_LIBS_DIR}/.git 31 | iverilog -g2012 -s tb -y ${XILINX_LIB_DIR} -y ${XILINX_LIB_DIR}/unisims -DXILINX_GLS ${COMPILE_ARGS} -o ${DUT}.netlist.sim $^ 32 | 33 | ${XILINX_LIBS_DIR}/.git: 34 | git clone ${XILINX_LIB_URL} 35 | 36 | clean: 37 | rm -f ${DUT}_sim sim.log sim.vcd 38 | rm -f ${DUT}.netlist.v synth.log 39 | -------------------------------------------------------------------------------- /Systolic_Array/README.md: -------------------------------------------------------------------------------- 1 | # Development In Progress! 2 | -------------------------------------------------------------------------------- /Systolic_Array/src/systolic_array.sv: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Systolic_Array/tb/sv/tb.sv: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Table/README.md: -------------------------------------------------------------------------------- 1 | # Multi-port table 2 | 3 | ## Overview 4 | This Verilog module implements a flexible table structure that supports multiple simultaneous read and write operations. It functions similarly to a register file, providing direct indexed access to stored data without the need for hashing mechanisms. 5 | 6 | ## Features 7 | - Configurable table size and data width 8 | - Support for multiple simultaneous write operations 9 | - Support for multiple simultaneous read operations 10 | - Synchronous operation with reset capability 11 | - Direct indexed access to stored data 12 | 13 | ## Parameters 14 | 15 | | Parameter | Description | Default | 16 | |-------------|------------------------------------------------|---------| 17 | | TABLE_SIZE | Number of entries in the table | 32 | 18 | | DATA_WIDTH | Width of each data entry in bits | 8 | 19 | | INPUT_RATE | Number of simultaneous write operations | 2 | 20 | | OUTPUT_RATE | Number of simultaneous read operations | 2 | 21 | 22 | ## Port Descriptions 23 | 24 | ### Input Ports 25 | 26 | | Port | Width | Description | 27 | |-----------|----------------------------------|--------------------------------| 28 | | clk | 1 | System clock signal | 29 | | rst | 1 | Active-high reset signal | 30 | | wr_en | INPUT_RATE | Write enable signals | 31 | | rd_en | 1 | Read enable signal | 32 | | index_wr | INPUT_RATE * log2(TABLE_SIZE) | Write address indices | 33 | | index_rd | OUTPUT_RATE * log2(TABLE_SIZE) | Read address indices | 34 | | data_wr | INPUT_RATE * DATA_WIDTH | Write data input | 35 | 36 | ### Output Ports 37 | 38 | | Port | Width | Description | 39 | |-----------|--------------------------|----------------------| 40 | | data_rd | OUTPUT_RATE * DATA_WIDTH | Read data output | 41 | 42 | ## Timing 43 | - All operations are synchronized to the positive edge of the clock 44 | - Reset is asynchronous and active-high 45 | - Write operations occur on the next clock edge after wr_en is asserted 46 | - Read operations occur on the next clock edge after rd_en is asserted 47 | 48 | ## Usage Example 49 | ```verilog 50 | table_top #( 51 | .TABLE_SIZE(64), 52 | .DATA_WIDTH(16), 53 | .INPUT_RATE(2), 54 | .OUTPUT_RATE(2) 55 | ) table_inst ( 56 | .clk(system_clk), 57 | .rst(system_rst), 58 | .wr_en(write_enable), 59 | .rd_en(read_enable), 60 | .index_wr(write_indices), 61 | .index_rd(read_indices), 62 | .data_wr(write_data), 63 | .data_rd(read_data) 64 | ); 65 | ``` 66 | 67 | ## Implementation Details 68 | - Uses a 2D register array for data storage 69 | - Implements parallel write operations using generate blocks 70 | - Supports concurrent read operations 71 | - Reset initializes all storage elements to zero 72 | - Uses Verilog-2001 syntax and features 73 | 74 | ## Performance Considerations 75 | - All read and write operations complete in a single clock cycle 76 | - No read-after-write hazard protection is implemented 77 | - Care should be taken when accessing the same index simultaneously 78 | 79 | ## Limitations 80 | - No built-in error checking for invalid indices 81 | - No protection against simultaneous read/write to same location 82 | - Write operations take precedence in case of conflicts 83 | 84 | -------------------------------------------------------------------------------- /Table/src/rtl_file.f: -------------------------------------------------------------------------------- 1 | table.sv 2 | -------------------------------------------------------------------------------- /Table/src/table.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Create Date: 31/01/2025 12:46:52 PM 4 | // Last Update Date: 09/02/2024 15:40 PM 5 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 6 | // Module Name: table 7 | // Description: Table Structure that supports multiple read, write oepration simultaneously 8 | // Additional Comments: . 9 | // 10 | ////////////////////////////////////////////////////////////////////////////////// 11 | 12 | module table_top #( 13 | parameter TABLE_SIZE = 32, 14 | parameter DATA_WIDTH = 8, 15 | parameter INPUT_RATE = 2, 16 | parameter OUTPUT_RATE = 2 17 | )( 18 | input wire clk, 19 | input wire rst, 20 | input wire [INPUT_RATE-1:0] wr_en, 21 | input wire rd_en, 22 | input wire [INPUT_RATE*$clog2(TABLE_SIZE)-1:0] index_wr, 23 | input wire [OUTPUT_RATE*$clog2(TABLE_SIZE)-1:0] index_rd, 24 | input wire [INPUT_RATE*DATA_WIDTH-1:0] data_wr, 25 | output reg [OUTPUT_RATE*DATA_WIDTH-1:0] data_rd 26 | ); 27 | localparam INDEX_WIDTH = $clog2(TABLE_SIZE); 28 | reg [DATA_WIDTH-1:0] data_stored [TABLE_SIZE]; 29 | integer i; 30 | 31 | always @ (posedge clk or posedge rst) begin 32 | if(rst) begin 33 | for (i=0; i iverilog_dump.v) 33 | $(shell echo 'initial begin' >> iverilog_dump.v) 34 | $(shell echo ' $$dumpfile("$(TOPLEVEL).vcd");' >> iverilog_dump.v) 35 | $(shell echo ' $$dumpvars(0, $(TOPLEVEL));' >> iverilog_dump.v) 36 | $(shell echo 'end' >> iverilog_dump.v) 37 | $(shell echo 'endmodule' >> iverilog_dump.v) 38 | VERILOG_SOURCES += $(PWD)/iverilog_dump.v 39 | COMPILE_ARGS += -s iverilog_dump 40 | endif 41 | 42 | include $(shell cocotb-config --makefiles)/Makefile.sim 43 | 44 | 45 | -------------------------------------------------------------------------------- /Table/tb/cocotb/tb.py: -------------------------------------------------------------------------------- 1 | import random 2 | #import asyncio 3 | import math 4 | import cocotb 5 | from cocotb.triggers import Timer, RisingEdge, ReadOnly 6 | from cocotb.clock import Clock 7 | from cocotb_bus.drivers import BusDriver 8 | from cocotb_bus.monitors import BusMonitor 9 | from cocotb.binary import BinaryValue 10 | 11 | #BIN string 12 | #BinaryValue(dut.data_wr.value, n_bits=8) ; BinaryValue.integar ; BinaryValue.hex ; BinaryValue.binstr; BinaryValue.signed_integer ; can represent x,z 13 | 14 | TABLE_SIZE = 32 # DUT parameter 15 | DATA_WIDTH = 8 # DUT paramter 16 | INPUT_RATE = 1 # DUT parameter 17 | OUTPUT_RATE = 1 # DUT parameter 18 | INDEX_WIDTH = 1 19 | TB_CLK_PERIOD = 30 # TB clk generator 20 | TB_SIM_TIMEOUT = 30 # TB sim timeout 30ms 21 | TB_TEST_WEIGHT = 1 22 | table_expected = [0 for i in range(TABLE_SIZE)] 23 | err_cnt = 0 24 | 25 | # table_top #( 26 | # .TABLE_SIZE(TABLE_SIZE), 27 | # .DATA_WIDTH(DATA_WIDTH), 28 | # .INPUT_RATE(INPUT_RATE), 29 | # .OUTPUT_RATE(OUTPUT_RATE)) DUT ( 30 | # /*input wire*/ .clk(clk), 31 | # /*input wire*/ .rst(rst), 32 | # /*input wire*/ .wr_en(wr_en), 33 | # /*input wire*/ .rd_en(rd_en), 34 | # /*input wire [INPUT_RATE*$clog2(TABLE_SIZE)-1:0]*/ .index_wr(index_wr), 35 | # /*input wire [OUTPUT_RATE*$clog2(TABLE_SIZE)-1:0]*/ .index_rd(index_rd), 36 | # /*input wire [INPUT_RATE*DATA_WIDTH-1:0]*/ .data_wr(data_wr), 37 | # /*output reg [OUTPUT_RATE*DATA_WIDTH-1:0]*/ .data_rd(data_rd)); 38 | 39 | class OutputDriver(BusDriver): 40 | _signals = ["wr_en", "rd_en", "index_wr", "index_rd", "data_wr"] #dut.wr_en ; dut.rd_en; dut.index_wr; dut.data_wr 41 | 42 | def __init__(self, dut, name, clk): 43 | BusDriver.__init__(self, dut, name, clk) 44 | self.bus.wr_en.value = 0 45 | self.bus.rd_en.value = 0 46 | self.bus.index_wr.value = 0 47 | self.bus.index_rd.value = 0 48 | self.bus.data_wr.value = 0 49 | self.dut = dut 50 | self.clk = clk 51 | 52 | # async def _driver_send(self): 53 | async def write_burst(self, input_data): 54 | for i in range(0,len(input_data)//INPUT_RATE): 55 | await RisingEdge(self.clk) 56 | await Timer (1, units = 'ns') 57 | data_wr = 0 58 | index_wr = 0 59 | for j in range(0,INPUT_RATE): 60 | data_wr = (data_wr<<(j)*DATA_WIDTH) + input_data[i*INPUT_RATE+j][1] 61 | index_wr = (index_wr<<(j)*INDEX_WIDTH) + input_data[i*INPUT_RATE+j][0] 62 | self.bus.wr_en.value = 2**INPUT_RATE - 1 63 | self.bus.data_wr.value = data_wr 64 | self.bus.index_wr.value = index_wr 65 | await RisingEdge(self.clk) 66 | await Timer (1, units = 'ns') 67 | self.bus.wr_en.value = 0 68 | 69 | async def read_burst(self, target_index): 70 | for i in range(0,len(target_index)//OUTPUT_RATE): 71 | await RisingEdge(self.clk) 72 | await Timer (1, units = 'ns') 73 | index_rd = 0 74 | for j in range(0,OUTPUT_RATE): 75 | index_rd = (index_rd<<(j)*INDEX_WIDTH) + target_index[i*OUTPUT_RATE+j] 76 | #print(index_rd) 77 | self.bus.rd_en.value = 1 78 | self.bus.index_rd.value = index_rd 79 | await RisingEdge(self.clk) 80 | await Timer (1, units = 'ns') 81 | self.bus.rd_en.value = 0 82 | 83 | class InputMonitor(BusMonitor): 84 | _signals = ["wr_en","rd_en","index_wr","index_rd","data_wr","data_rd"] 85 | 86 | def __init__(self, dut, name, clk, reset): 87 | BusMonitor.__init__(self, dut, name, clk, reset) 88 | self.clk = clk 89 | self.reset = reset 90 | self.dut = dut 91 | 92 | async def _monitor_recv(self): #this will be called in init. 93 | global err_cnt 94 | global table_expected 95 | while True: 96 | await RisingEdge(self.clock) 97 | await ReadOnly() 98 | #await Timer (1, units = 'ns') 99 | 100 | if self.reset.value == 1: 101 | self.bus.wr_en.value = 0 102 | self.bus.rd_en.value = 0 103 | self.bus.index_wr.value = 0 104 | self.bus.index_rd.value = 0 105 | self.bus.data_wr.value = 0 106 | table_expected = [0 for i in range(TABLE_SIZE)] 107 | continue 108 | 109 | if self.bus.wr_en.value == (2**INPUT_RATE -1) and self.bus.rd_en.value == 1: 110 | self.read_update() 111 | self.write_update() 112 | elif self.bus.wr_en.value == (2**INPUT_RATE - 1): 113 | self.write_update() 114 | elif self.bus.rd_en.value == 1: 115 | self.read_update() 116 | 117 | def write_update(self): 118 | global table_expected 119 | for j in range(0,INPUT_RATE): 120 | target_index = self.bus.index_wr.value & ((2**INDEX_WIDTH - 1) << (INDEX_WIDTH*j)) 121 | target_index = target_index >> (INDEX_WIDTH*j) 122 | exp_data_wr = self.bus.data_wr.value & ((2**DATA_WIDTH - 1) << (DATA_WIDTH*j)) 123 | exp_data_wr = exp_data_wr >> (DATA_WIDTH*j) 124 | table_expected[target_index] = exp_data_wr 125 | cocotb.log.info("WRITE OPERATION: INDEX = d%0d, DATA = d%0d", target_index, exp_data_wr) 126 | 127 | def read_update(self): 128 | global err_cnt 129 | for j in range(0,OUTPUT_RATE): 130 | target_index = self.bus.index_rd.value & ((2**INDEX_WIDTH - 1) << (INDEX_WIDTH*j)) 131 | target_index = target_index >> (INDEX_WIDTH*j) 132 | act_data_rd = self.bus.data_rd.value & ((2**DATA_WIDTH - 1) << (DATA_WIDTH*j)) 133 | act_data_rd = act_data_rd >> (DATA_WIDTH*j) 134 | exp_data_rd = table_expected[target_index] 135 | if (act_data_rd == exp_data_rd): 136 | cocotb.log.info("READ OPERATION: INDEX = d%0d, DATA = d%0d", target_index,act_data_rd) 137 | else: 138 | err_cnt += 1 139 | cocotb.log.error("READ OPERATION: INDEX = d%0d, ACT DATA = d%0d, EXP DATA = d%0d, ", target_index, act_data_rd, exp_data_rd) 140 | 141 | async def dut_init(dut): 142 | global TABLE_SIZE # DUT parameter 143 | global DATA_WIDTH # DUT paramter 144 | global INPUT_RATE # DUT parameter 145 | global OUTPUT_RATE # DUT parameter 146 | global INDEX_WIDTH 147 | TABLE_SIZE = dut.TABLE_SIZE.value 148 | DATA_WIDTH = dut.DATA_WIDTH.value 149 | INPUT_RATE = dut.INPUT_RATE.value 150 | OUTPUT_RATE = dut.OUTPUT_RATE.value 151 | INDEX_WIDTH = math.ceil(math.log2(TABLE_SIZE)); 152 | await cocotb.start(Clock(dut.clk, TB_CLK_PERIOD, units="ns").start()) 153 | dut.data_wr.value = 0 154 | dut.index_wr.value = 0 155 | dut.index_rd.value = 0 156 | dut.rd_en.value = 0 157 | dut.wr_en.value = 0 158 | dut.rst.value = 1 159 | await(Timer(100,'ns')) 160 | dut.rst.value = 0 161 | await(Timer(100,'ns')) 162 | 163 | 164 | @cocotb.test() 165 | async def table_rand_test(dut): 166 | await dut_init(dut) 167 | driver = OutputDriver(dut, None, dut.clk) #set name='None', refer to Bus class 168 | monitor = InputMonitor(dut, None, dut.clk, dut.rst) 169 | cocotb.log.info("SEED NUMBER = %d",cocotb.RANDOM_SEED) 170 | input_data = [] 171 | read_index = [] 172 | 173 | for j in range(TB_TEST_WEIGHT): 174 | for i in range(30): 175 | #data_wr = cocotb.binary.BinaryValue(n_bits=INPUT_RATE*DATA_WIDTH) 176 | #index_wr = cocotb.binary.BinaryValue(n_bits=INDEX_WIDTH) 177 | data_wr = 0 178 | index_wr = 0 179 | for k in range(0,INPUT_RATE): 180 | #data_wr = (data_wr<<(k-1)*DATA_WIDTH) + random.randint(0, 2**DATA_WIDTH-1) 181 | data_wr = random.randint(0, 2**DATA_WIDTH-1) 182 | #index_wr = (index_wr<<(k-1)*INDEX_WIDTH) + random.randint(0, TABLE_SIZE-1) 183 | index_wr = random.randint(0, TABLE_SIZE-1) 184 | input_data.append([index_wr,data_wr]) 185 | read_index.append(index_wr) 186 | await driver.write_burst(input_data) 187 | await Timer (500, units = 'ns') 188 | await driver.read_burst(read_index) 189 | await Timer (500, units = 'ns') 190 | 191 | if (err_cnt > 0): 192 | cocotb.log.error("Errors count = %d",err_cnt) 193 | cocotb.result.test_fail() 194 | -------------------------------------------------------------------------------- /Table/tb/sv/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DUT ?= table_top 3 | SIM_OPTS ?= ../../src/table.sv 4 | SEED ?= $$(shuf -i 1-10000 -n 1) 5 | 6 | # DUT parameter #"-p" iverilog command flags 7 | COMPILE_ARGS = -P tb.TABLE_SIZE=32 8 | COMPILE_ARGS += -P tb.DATA_WIDTH=8 9 | COMPILE_ARGS += -P tb.INPUT_RATE=2 10 | COMPILE_ARGS += -P tb.OUTPUT_RATE=2 11 | COMPILE_ARGS += -P tb.TB_CLK_PERIOD=100 12 | COMPILE_ARGS += -P tb.TB_TEST_WEIGHT=1 13 | COMPILE_ARGS += -P tb.TB_SIM_TIMEOUT=30 #//ms. 14 | 15 | XILINX_LIB_URL ?= https://github.com/Xilinx/XilinxUnisimLibrary.git 16 | XILINX_LIB_DIR ?= XilinxUnisimLibrary/verilog/src 17 | 18 | sim: gen_sim 19 | ./${DUT}.sim +VCDFILE=sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee sim.log 20 | 21 | gen_sim: tb.sv ${SIM_OPTS} # to generate executable file by using iverilator 22 | iverilog -g2012 -s tb ${COMPILE_ARGS} -o ${DUT}.sim $^ 23 | 24 | synth: ${SIM_OPTS} 25 | yosys -p synth_xilinx ${SIM_OPTS} -L synth.log -o ${DUT}.netlist.v 26 | #timeout 27 | 28 | netlist_sim: gen_netlist_sim 29 | ./${DUT}.netlist.sim +VCDFILE=netlist_sim.vcd +VCDLEVEL=0 +SEED=${SEED} | tee netlist_sim.log 30 | 31 | gen_netlist_sim: tb.sv ${DUT}.netlist.v | ${XILINX_LIBS_DIR}/.git 32 | iverilog -g2012 -s tb -y ${XILINX_LIB_DIR} -y ${XILINX_LIB_DIR}/unisims -DXILINX_GLS ${COMPILE_ARGS} -o ${DUT}.netlist.sim $^ 33 | 34 | ${XILINX_LIBS_DIR}/.git: 35 | git clone ${XILINX_LIB_URL} 36 | 37 | clean: 38 | rm -f ${DUT}_sim sim.log sim.vcd 39 | rm -f ${DUT}.netlist.v synth.log 40 | -------------------------------------------------------------------------------- /Table/tb/sv/tb.sv: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // 4 | // Create Date: 05/05/2024 03:37:34 PM 5 | // Last Update Date: 01/02/2024 10:37:12 AM 6 | // Module Name: tb 7 | // Author: https://www.linkedin.com/in/wei-yet-ng-065485119/ 8 | // Description: 1. table_write_random() --> table_read_compare() 9 | // Additional Comments: 10 | // 11 | ////////////////////////////////////////////////////////////////////////////////// 12 | 13 | 14 | module tb( 15 | ); 16 | parameter TABLE_SIZE = 32; 17 | parameter DATA_WIDTH = 8; 18 | parameter INPUT_RATE = 2; 19 | parameter OUTPUT_RATE = 2; 20 | parameter TB_CLK_PERIOD = 100; 21 | parameter TB_TEST_WEIGHT = 1; 22 | parameter TB_SIM_TIMEOUT = 30; //ms. 23 | 24 | localparam MAX_DATA = 2**(DATA_WIDTH) - 1; 25 | localparam INDEX_WIDTH = $clog2(TABLE_SIZE-1); 26 | 27 | bit [DATA_WIDTH-1:0] table_expected [TABLE_SIZE]; 28 | integer index_queue[$]; 29 | 30 | reg clk=0; 31 | reg rst=0; 32 | reg wr_en=0; 33 | reg rd_en=0; 34 | reg [INPUT_RATE*$clog2(TABLE_SIZE)-1:0] index_wr=0; 35 | reg [OUTPUT_RATE*$clog2(TABLE_SIZE)-1:0] index_rd=0; 36 | reg [INPUT_RATE*DATA_WIDTH-1:0] data_wr=0; 37 | wire [OUTPUT_RATE*DATA_WIDTH-1:0] data_rd; 38 | 39 | reg [DATA_WIDTH-1:0] data_wr_rand; 40 | reg [$clog2(TABLE_SIZE)-1:0] index_wr_rand; 41 | 42 | integer err_cnt = 0; 43 | 44 | `ifdef XILINX_GLS 45 | glbl glbl (); // for Xilinx GLS 46 | `endif 47 | 48 | table_top #( 49 | .TABLE_SIZE(TABLE_SIZE), 50 | .DATA_WIDTH(DATA_WIDTH), 51 | .INPUT_RATE(INPUT_RATE), 52 | .OUTPUT_RATE(OUTPUT_RATE)) DUT ( 53 | /*input wire*/ .clk(clk), 54 | /*input wire*/ .rst(rst), 55 | /*input wire*/ .wr_en(wr_en), 56 | /*input wire*/ .rd_en(rd_en), 57 | /*input wire [INPUT_RATE*$clog2(TABLE_SIZE)-1:0]*/ .index_wr(index_wr), 58 | /*input wire [OUTPUT_RATE*$clog2(TABLE_SIZE)-1:0]*/ .index_rd(index_rd), 59 | /*input wire [INPUT_RATE*DATA_WIDTH-1:0]*/ .data_wr(data_wr), 60 | /*output reg [OUTPUT_RATE*DATA_WIDTH-1:0]*/ .data_rd(data_rd)); 61 | 62 | always #(TB_CLK_PERIOD>>1) clk = ~clk; 63 | 64 | task table_write_random (input integer count); 65 | for (int i=0; i