diff --git a/info.yaml b/info.yaml index f04da75..ea4b1af 100644 --- a/info.yaml +++ b/info.yaml @@ -15,33 +15,40 @@ project: # List your project's source files here. Source files must be in ./src and you must list each source file separately, one per line: source_files: - - "project.v" + - "tt_um_pa1mantri_cdc_fifo.sv" + - "cdc_fifo.sv" + - "dpram.sv" + - "binary_to_gray.sv" + - "gray_to_binary.sv" + - "synchronizer.sv" + - "cdc_fifo_read_state.sv" + - "cdc_fifo_write_state.sv" # The pinout of your project. Leave unused pins blank. DO NOT delete or add any pins. pinout: # Inputs - ui[0]: "" - ui[1]: "" - ui[2]: "" - ui[3]: "" - ui[4]: "" - ui[5]: "" - ui[6]: "" - ui[7]: "" + ui[0]: "write_clock" + ui[1]: "write_increment" + ui[2]: "read_clock" + ui[3]: "read_increment" + ui[4]: "write_data0" + ui[5]: "write_data1" + ui[6]: "write_data2" + ui[7]: "write_data3" # Outputs - uo[0]: "" - uo[1]: "" + uo[0]: "empty" + uo[1]: "full" uo[2]: "" uo[3]: "" - uo[4]: "" - uo[5]: "" - uo[6]: "" - uo[7]: "" + uo[4]: "read_data0" + uo[5]: "read_data1" + uo[6]: "read_data2" + uo[7]: "read_data3" # Bidirectional pins - uio[0]: "" - uio[1]: "" + uio[0]: "write_reset" + uio[1]: "read_reset" uio[2]: "" uio[3]: "" uio[4]: "" diff --git a/src/binary_to_gray.sv b/src/binary_to_gray.sv new file mode 100644 index 0000000..ae24034 --- /dev/null +++ b/src/binary_to_gray.sv @@ -0,0 +1,10 @@ +module binary_to_gray #( + parameter WIDTH = 8 +) ( + input logic [WIDTH-1:0] binary, + output logic [WIDTH-1:0] gray +); + + assign gray = (binary >> 1) ^ binary; + +endmodule diff --git a/src/cdc_fifo.sv b/src/cdc_fifo.sv new file mode 100644 index 0000000..c5250b8 --- /dev/null +++ b/src/cdc_fifo.sv @@ -0,0 +1,93 @@ +// FIFO for passing registers across clock domains + +//`include "dpram.sv" +//`include "synchronizer.sv" +//`include "cdc_fifo_read_state.sv" +//`include "cdc_fifo_write_state.sv" +//`include "binary_to_gray.sv" +//`include "gray_to_binary.sv" + +module cdc_fifo #( + parameter DATA_WIDTH = 8, + parameter ADDRESS_WIDTH = 4 +) ( + // Sender side signals/buses + input logic write_clock, + input logic write_reset, + input logic [DATA_WIDTH-1:0] write_data, + input logic write_increment, + output logic full, + + // Receiver side signals/buses + input logic read_clock, + input logic read_reset, + input logic read_increment, + output logic [DATA_WIDTH-1:0] read_data, + output logic empty +); + + wire write_enable; + wire [ADDRESS_WIDTH-1:0] write_address; + wire [ADDRESS_WIDTH-1:0] write_address_gray_presync; + wire [ADDRESS_WIDTH-1:0] write_address_gray_postsync; + wire [ADDRESS_WIDTH-1:0] read_address; + wire [ADDRESS_WIDTH-1:0] read_address_gray_presync; + wire [ADDRESS_WIDTH-1:0] read_address_gray_postsync; + + assign write_enable = (!full & write_increment); + + dpram #( + .DATA_WIDTH(DATA_WIDTH), + .ADDRESS_WIDTH(ADDRESS_WIDTH) + ) fifo_memory ( + .clock(write_clock), + .write_address(write_address), + .write_data(write_data), + .write_enable(write_enable), + .read_address(read_address), + .read_data(read_data) + ); + + cdc_fifo_write_state #( + .ADDRESS_WIDTH(ADDRESS_WIDTH) + ) writestate ( + .clock(write_clock), + .reset(write_reset), + .increment(write_increment), + .read_address_gray(read_address_gray_postsync), + .write_address(write_address), + .write_address_gray(write_address_gray_presync), + .full(full) + ); + + cdc_fifo_read_state #( + .ADDRESS_WIDTH(ADDRESS_WIDTH) + ) readstate ( + .clock(read_clock), + .reset(read_reset), + .increment(read_increment), + .write_address_gray(write_address_gray_postsync), + .read_address(read_address), + .read_address_gray(read_address_gray_presync), + .empty(empty) + ); + + synchronizer #( + .WIDTH(ADDRESS_WIDTH) + ) write_address_sync ( + .clock(read_clock), + .reset(read_reset), + .in(write_address_gray_presync), + .out(write_address_gray_postsync) + ); + + synchronizer #( + .WIDTH(ADDRESS_WIDTH) + ) read_address_sync ( + .clock(write_clock), + .reset(write_reset), + .in(read_address_gray_presync), + .out(read_address_gray_postsync) + ); + +endmodule diff --git a/src/cdc_fifo_read_state.sv b/src/cdc_fifo_read_state.sv new file mode 100644 index 0000000..efae3c3 --- /dev/null +++ b/src/cdc_fifo_read_state.sv @@ -0,0 +1,40 @@ +module cdc_fifo_read_state #( + parameter ADDRESS_WIDTH = 4 +) ( + input logic clock, + input logic reset, + input logic increment, + input logic [ADDRESS_WIDTH-1:0] write_address_gray, + + output logic [ADDRESS_WIDTH-1:0] read_address, + output logic [ADDRESS_WIDTH-1:0] read_address_gray, + output logic empty +); + + logic [ADDRESS_WIDTH-1:0] write_address; + + gray_to_binary #( + .WIDTH(ADDRESS_WIDTH) + ) write_addr_decode ( + .gray(write_address_gray), + .binary(write_address) + ); + + binary_to_gray #( + .WIDTH(ADDRESS_WIDTH) + ) read_addr_encode ( + .binary(read_address), + .gray(read_address_gray) + ); + + assign empty = (write_address == read_address); + + always_ff @ (posedge clock or posedge reset) begin + if (reset) begin + read_address <= 0; + end else if (increment & !empty) begin + read_address <= read_address + 1; + end + end + +endmodule diff --git a/src/cdc_fifo_write_state.sv b/src/cdc_fifo_write_state.sv new file mode 100644 index 0000000..83140b0 --- /dev/null +++ b/src/cdc_fifo_write_state.sv @@ -0,0 +1,40 @@ +module cdc_fifo_write_state #( + parameter ADDRESS_WIDTH = 4 +) ( + input logic clock, + input logic reset, + input logic increment, + input logic [ADDRESS_WIDTH-1:0] read_address_gray, + + output logic [ADDRESS_WIDTH-1:0] write_address, + output logic [ADDRESS_WIDTH-1:0] write_address_gray, + output logic full +); + + assign full = (write_address + 1 == read_address); + + logic [ADDRESS_WIDTH-1:0] read_address; + + gray_to_binary #( + .WIDTH(ADDRESS_WIDTH) + ) read_addr_decode ( + .gray(read_address_gray), + .binary(read_address) + ); + + binary_to_gray #( + .WIDTH(ADDRESS_WIDTH) + ) write_addr_encode ( + .binary(write_address), + .gray(write_address_gray) + ); + + always_ff @ (posedge clock or posedge reset) begin + if (reset) begin + write_address <= 0; + end else if (increment & !full) begin + write_address <= write_address + 1; + end + end + +endmodule diff --git a/src/dpram.sv b/src/dpram.sv new file mode 100644 index 0000000..ad73947 --- /dev/null +++ b/src/dpram.sv @@ -0,0 +1,26 @@ +// Dual-ported parameterized RAM module +module dpram #( + parameter DATA_WIDTH = 8, + parameter ADDRESS_WIDTH = 4 +) ( + input logic clock, + + input logic [ADDRESS_WIDTH-1:0] write_address, + input logic [DATA_WIDTH-1:0] write_data, + input logic write_enable, + + input logic [ADDRESS_WIDTH-1:0] read_address, + output logic [DATA_WIDTH-1:0] read_data +); + + logic [DATA_WIDTH-1:0] memory [0:(1<> i); + end + end + +endmodule diff --git a/src/project.v b/src/project.v deleted file mode 100644 index cd6f740..0000000 --- a/src/project.v +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2024 Your Name - * SPDX-License-Identifier: Apache-2.0 - */ - -`default_nettype none - -module tt_um_example ( - input wire [7:0] ui_in, // Dedicated inputs - output wire [7:0] uo_out, // Dedicated outputs - input wire [7:0] uio_in, // IOs: Input path - output wire [7:0] uio_out, // IOs: Output path - output wire [7:0] uio_oe, // IOs: Enable path (active high: 0=input, 1=output) - input wire ena, // always 1 when the design is powered, so you can ignore it - input wire clk, // clock - input wire rst_n // reset_n - low to reset -); - - // All output pins must be assigned. If not used, assign to 0. - assign uo_out = ui_in + uio_in; // Example: ou_out is the sum of ui_in and uio_in - assign uio_out = 0; - assign uio_oe = 0; - - // List all unused inputs to prevent warnings - wire _unused = &{ena, clk, rst_n, 1'b0}; - -endmodule diff --git a/src/synchronizer.sv b/src/synchronizer.sv new file mode 100644 index 0000000..04d81b7 --- /dev/null +++ b/src/synchronizer.sv @@ -0,0 +1,19 @@ +module synchronizer #( + parameter WIDTH = 1 +) ( + input logic clock, + input logic reset, + input logic [WIDTH-1:0] in, + output logic [WIDTH-1:0] out); + + logic [WIDTH-1:0] data; + + always_ff @ (posedge clock or posedge reset) begin + if (reset) begin + out <= 0; + data <= 0; + end else begin + {out, data} <= {data, in}; + end + end +endmodule diff --git a/src/tt_um_pa1mantri_cdc_fifo.sv b/src/tt_um_pa1mantri_cdc_fifo.sv new file mode 100644 index 0000000..0042301 --- /dev/null +++ b/src/tt_um_pa1mantri_cdc_fifo.sv @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Your Name + * SPDX-License-Identifier: Apache-2.0 + */ + +`default_nettype none + +module tt_um_pa1mantri_cdc_fifo ( + input wire [7:0] ui_in, // Dedicated inputs + output wire [7:0] uo_out, // Dedicated outputs + input wire [7:0] uio_in, // IOs: Input path + output wire [7:0] uio_out, // IOs: Output path + output wire [7:0] uio_oe, // IOs: Enable path (active high: 0=input, 1=output) + input wire ena, // always 1 when the design is powered, so you can ignore it + input wire clk, // clock + input wire rst_n // reset_n - low to reset +); + + // ui_in mappings + + wire write_clock,write_increment,read_clock,read_increment; + wire [3:0]write_data; + + assign write_clock = ui_in[0]; + assign write_increment = ui_in[1]; + assign read_clock = ui_in[2]; + assign read_increment = ui_in[3]; + assign write_data = ui_in[4]; + + //uo_out mappings + + wire empty,full; + wire [3:0]read_data; + + assign uo_out[0] = empty; + assign uo_out[1] = full; + assign uo_out[3:2] = 'b00; + assign uo_out[7:4] = read_data; + + //uio_in mappings + + wire read_reset,write_reset; + + assign write_reset = !uio_in[0]; + assign read_reset = !uio_in[1]; + + //Fifo instantiation + + cdc_fifo #( + .DATA_WIDTH(4), + .ADDRESS_WIDTH(5) + ) fifo ( + .write_clock(write_clock),.write_reset(write_reset),.write_data(write_data),.write_increment(write_increment),.full(full), + .read_clock(read_clock),.read_reset(read_reset),.read_data(read_data),.read_increment(read_increment),.empty(empty) + ); + + // All output pins must be assigned. If not used, assign to 0. + + assign uio_out =0; + assign uio_oe =0; + +endmodule diff --git a/test/Makefile b/test/Makefile index 4413db2..2a1da8c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -5,7 +5,7 @@ SIM ?= icarus TOPLEVEL_LANG ?= verilog SRC_DIR = $(PWD)/../src -PROJECT_SOURCES = project.v +PROJECT_SOURCES = tt_um_pa1mantri_cdc_fifo.sv cdc_fifo.sv dpram.sv cdc_fifo_read_state.sv cdc_fifo_write_state.sv binary_to_gray.sv gray_to_binary.sv synchronizer.sv ifneq ($(GATES),yes) diff --git a/test/tb.v b/test/tb.v index 2fc848c..84c7b3a 100644 --- a/test/tb.v +++ b/test/tb.v @@ -24,7 +24,7 @@ module tb (); wire [7:0] uio_oe; // Replace tt_um_example with your module name: - tt_um_example user_project ( + tt_um_pa1mantri_cdc_fifo user_project ( // Include power ports for the Gate Level test: `ifdef GL_TEST diff --git a/test/test.py b/test/test.py index 6638ca2..0de3e7c 100644 --- a/test/test.py +++ b/test/test.py @@ -9,32 +9,56 @@ @cocotb.test() async def test_project(dut): dut._log.info("Start") + write_clock_port = dut.ui_in[0] + write_increment = dut.ui_in[1] + read_clock_port = dut.ui_in[2] + read_increment = dut.ui_in[3] + write_data = [ + dut.ui_in[4], + dut.ui_in[5], + dut.ui_in[6], + dut.ui_in[7] + ] + empty = dut.uo_out[0] + full = dut.uo_out[1] + read_data = [ + dut.uo_out[4], + dut.uo_out[5], + dut.uo_out[6], + dut.uo_out[7] + ] + write_reset = dut.uio_in[0] + read_reset = dut.uio_in[1] + write_increment.value = 0 + read_increment.value = 0 + for i in write_data: + i.value = 0 # Set the clock period to 10 us (100 KHz) clock = Clock(dut.clk, 10, units="us") cocotb.start_soon(clock.start()) + write_clock = Clock(write_clock_port, 10, units="us") + read_clock = Clock(read_clock_port, 25, units="us") + cocotb.start_soon(read_clock.start()) + cocotb.start_soon(write_clock.start()) # Reset dut._log.info("Reset") - dut.ena.value = 1 - dut.ui_in.value = 0 - dut.uio_in.value = 0 - dut.rst_n.value = 0 + write_reset.value = 0 + read_reset.value = 0 await ClockCycles(dut.clk, 10) - dut.rst_n.value = 1 - - dut._log.info("Test project behavior") - - # Set the input values you want to test - dut.ui_in.value = 20 - dut.uio_in.value = 30 - - # Wait for one clock cycle to see the output values - await ClockCycles(dut.clk, 1) + write_reset.value = 1 + read_reset.value = 1 + await ClockCycles(dut.clk, 10) + + # Set the input values, wait one clock cycle, and check the output + dut._log.info("Test") + assert empty.value == 1 + assert full.value == 0 - # The following assersion is just an example of how to check the output values. - # Change it to match the actual expected output of your module: - assert dut.uo_out.value == 50 + write_increment.value = 1 + await ClockCycles(dut.clk, 2) + write_increment.value = 0 + await ClockCycles(dut.clk, 10) + assert empty.value == 0 - # Keep testing the module by changing the input values, waiting for - # one or more clock cycles, and asserting the expected output values.