From f6139daf1136cca6b29d6d84e72148dac45abe75 Mon Sep 17 00:00:00 2001 From: otroubi Date: Mon, 7 Sep 2026 13:59:39 +0300 Subject: [PATCH] test for reflectometer without clk wizard --- .../tests/Makefile | 65 +++++ .../tests/adc_model.sv | 59 ++++ .../tests/dac_model.sv | 58 ++++ .../tests/drivers.py | 185 ++++++++++++ .../tests/full_reflectometer_test.py | 174 ++++++++++++ .../tests/reference_model.py | 253 +++++++++++++++++ .../tests/reg_map.py | 54 ++++ .../tests/scoreboard.py | 40 +++ .../tests/tb_full_reflectometer.sv | 263 ++++++++++++++++++ 9 files changed, 1151 insertions(+) create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/Makefile create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/adc_model.sv create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/dac_model.sv create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/drivers.py create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/full_reflectometer_test.py create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/reference_model.py create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/reg_map.py create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/scoreboard.py create mode 100644 designs/full_new_reflectometer_without_clk_wiz/tests/tb_full_reflectometer.sv diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/Makefile b/designs/full_new_reflectometer_without_clk_wiz/tests/Makefile new file mode 100644 index 0000000..3c890e0 --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/Makefile @@ -0,0 +1,65 @@ +TOPLEVEL_LANG = verilog +SIM ?= questa +WAVES = 1 +WLF_FILE := $(SIM_BUILD)/waves.wlf + +PWD := $(shell pwd) + + +RTL_DIR = $(PWD)/../src +RTL_ACCUM_DIR = $(PWD)/../../../rtl/accum/src +RTL_GENERATOR_DIR = $(PWD)/../../../rtl/generator/src +RTL_SAMPLER_DIR = $(PWD)/../../../rtl/sampler/src +RTL_CTRL_DIR = $(PWD)/../../../rtl/controller_new/src +LIBS_DIR = $(PWD)/../../../external/rtl_libs + + +VERILOG_SOURCES += $(LIBS_DIR)/axi/rtl/axi_pkg.sv +VERILOG_SOURCES += $(RTL_DIR)/dma_reg_pkg.sv +VERILOG_SOURCES += $(LIBS_DIR)/axi/rtl/axi_if.sv +VERILOG_SOURCES += $(LIBS_DIR)/axi/rtl/axis_if.sv +VERILOG_SOURCES += $(LIBS_DIR)/axi/rtl/axis_if_to_flat.sv +VERILOG_SOURCES += $(LIBS_DIR)/axi/rtl/axi4l_flat_to_if.sv +VERILOG_SOURCES += $(LIBS_DIR)/axi/axi_reg/axi4l_reg_map.sv +VERILOG_SOURCES += $(LIBS_DIR)/external/verilog-axi/rtl/axi_dma_rd.v +VERILOG_SOURCES += $(LIBS_DIR)/external/verilog-axi/rtl/axi_dma_wr.v +VERILOG_SOURCES += $(LIBS_DIR)/external/verilog-axi/rtl/axi_dma.v +VERILOG_SOURCES += $(LIBS_DIR)/external/verilog-axi/rtl/axi_ram.v +VERILOG_SOURCES += $(RTL_DIR)/axi_ram_wrapper.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/controller.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/dma_controller.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/shaper_axis_desc.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/shaper_axis_status.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/controller_wrapper_axil.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/axi4l_reg_map_controller_pkg.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/axis_defaults_helper.sv +VERILOG_SOURCES += $(RTL_CTRL_DIR)/axi4l_reg_map_controller.sv +VERILOG_SOURCES += $(RTL_DIR)/axi_dma_wrapper_if.sv +VERILOG_SOURCES += $(RTL_DIR)/wrapper_controller_dma.sv +VERILOG_SOURCES += $(RTL_ACCUM_DIR)/adder.sv +VERILOG_SOURCES += $(RTL_ACCUM_DIR)/out_axis_fifo.sv +VERILOG_SOURCES += $(RTL_ACCUM_DIR)/accum.sv +VERILOG_SOURCES += $(RTL_ACCUM_DIR)/accum_top.sv +VERILOG_SOURCES += $(RTL_GENERATOR_DIR)/generator.sv +VERILOG_SOURCES += $(RTL_SAMPLER_DIR)/sampler.sv + +VERILOG_SOURCES += $(PWD)/adc_model.sv +VERILOG_SOURCES += $(PWD)/dac_model.sv +VERILOG_SOURCES += $(RTL_DIR)/reflectometer_ip.sv +VERILOG_SOURCES += $(RTL_DIR)/reflectometer_and_dma_wrapper.sv +VERILOG_SOURCES += $(PWD)/tb_full_reflectometer.sv +VERILOG_SOURCES += /mnt/c/Xilinx/Vivado/2021.2/data/verilog/src/glbl.v + +TOPLEVEL = tb_full_reflectometer +MODULE = full_reflectometer_test + + +ifeq ($(SIM),questa) + SIM_ARGS += -L xpm -L unisim + SIM_ARGS += work.glbl + SIM_ARGS += -wlf $(WLF_FILE) + COMPILE_ARGS += +acc +endif + + +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/adc_model.sv b/designs/full_new_reflectometer_without_clk_wiz/tests/adc_model.sv new file mode 100644 index 0000000..dee6ef3 --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/adc_model.sv @@ -0,0 +1,59 @@ +// AN9238 virtual ADC model (1 port) + +module virtual_adc_model #( + parameter int unsigned ADC_DATA_WIDTH = 12, + + // Bipolar input range: +/- VOLTAGE_RANGE + parameter real VOLTAGE_RANGE = 1.0, + + // Analog input correction + parameter real VOLTAGE_GAIN = 0.2, + parameter real GROUND_BIAS = 0.0, + + // ADC timing parameters + parameter time CONVERSION_DELAY = 250ps +)( + input logic clk_i, + input real voltage_i, + + output logic otr_o, + output logic [ADC_DATA_WIDTH-1:0] data_o +); + + localparam int unsigned ZERO_CODE = (1 << (ADC_DATA_WIDTH - 1)); + localparam real VOLTAGE_STEP = (2 * VOLTAGE_RANGE) / real'((1 << ADC_DATA_WIDTH) - 1); + + real voltage_corrected; + + //------------------------------------------------------------ + // Convert analog voltage to ADC code + //------------------------------------------------------------ + function automatic logic [ADC_DATA_WIDTH-1:0] voltage_to_code( input real voltage ); + if (voltage <= -VOLTAGE_RANGE) return '0; + if (voltage >= VOLTAGE_RANGE) return {ADC_DATA_WIDTH{1'b1}}; + return $rtoi(voltage / VOLTAGE_STEP + real'((ZERO_CODE)) + 0.5); + endfunction + + function automatic logic range_check( input real voltage ); + real v_abs = (voltage < 0.0) ? -voltage : voltage; + return v_abs >= VOLTAGE_RANGE; + endfunction + + //------------------------------------------------------------ + // Initial state + //------------------------------------------------------------ + initial begin + data_o = ZERO_CODE; // 0V + otr_o = 0; + end + + //------------------------------------------------------------ + // Update analog output + //------------------------------------------------------------ + always @(posedge clk_i) begin + voltage_corrected = (voltage_i - GROUND_BIAS) * VOLTAGE_GAIN; + data_o <= #(CONVERSION_DELAY) voltage_to_code(voltage_corrected); + otr_o <= #(CONVERSION_DELAY) range_check(voltage_corrected); + end + +endmodule diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/dac_model.sv b/designs/full_new_reflectometer_without_clk_wiz/tests/dac_model.sv new file mode 100644 index 0000000..f830117 --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/dac_model.sv @@ -0,0 +1,58 @@ +// AN9767 model (1 port) + +module virtual_dac_model #( + parameter int unsigned DAC_DATA_WIDTH = 14, + + // Bipolar output range: +/- VOLTAGE_RANGE + parameter real VOLTAGE_RANGE = 5.0, + + // Analog output correction + parameter real VOLTAGE_GAIN = 1.0, + parameter real GROUND_BIAS = 0.0, + + // DAC timing parameters + parameter time TRANSMISSION_DELAY = 150ps, + parameter time CONVERSION_DELAY = 150ps +)( + input logic clk_i, + input logic wrt_i, + input logic [DAC_DATA_WIDTH-1:0] data_i, + + output real voltage_o +); + + localparam int unsigned ZERO_CODE = (1 << (DAC_DATA_WIDTH - 1)); + localparam real VOLTAGE_STEP = (2 * VOLTAGE_RANGE) / real'((1 << DAC_DATA_WIDTH) - 1); + + logic [DAC_DATA_WIDTH-1:0] dac_code; + + //------------------------------------------------------------ + // Convert DAC code to analog voltage + //------------------------------------------------------------ + function automatic real code_to_voltage( input logic [DAC_DATA_WIDTH-1:0] code); + return (int'(code) - int'(ZERO_CODE)) * VOLTAGE_STEP; + endfunction + + //------------------------------------------------------------ + // Initial state + //------------------------------------------------------------ + initial begin + dac_code = '0; + voltage_o = code_to_voltage('0) * VOLTAGE_GAIN + GROUND_BIAS; + end + + //------------------------------------------------------------ + // Latch new DAC code + //------------------------------------------------------------ + always @(posedge wrt_i) begin + dac_code <= #(TRANSMISSION_DELAY) data_i; + end + + //------------------------------------------------------------ + // Update analog output + //------------------------------------------------------------ + always @(posedge clk_i) begin + voltage_o <= #(CONVERSION_DELAY) code_to_voltage(dac_code) * VOLTAGE_GAIN + GROUND_BIAS; + end + +endmodule diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/drivers.py b/designs/full_new_reflectometer_without_clk_wiz/tests/drivers.py new file mode 100644 index 0000000..b0cbb45 --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/drivers.py @@ -0,0 +1,185 @@ +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge +from cocotbext.axi import AxiLiteBus, AxiLiteMaster +from cocotbext.axi import AxiStreamBus, AxiStreamSink, AxiStreamSource, AxiStreamFrame + +from reg_map import * + +def reg_addr(reg_index: int) -> int: + # AXI-Lite uses byte addresses, 32-bit registers are spaced by 4 bytes. + return reg_index * 4 + + +def u32(value: int) -> bytes: + return int(value & 0xFFFFFFFF).to_bytes(4, "little") + +class Drivers: + def __init__(self, dut): + self.dut = dut + + cocotb.start_soon(Clock(dut.ctrl_clk, 5, unit="ns").start()) + + cocotb.start_soon(Clock(dut.clk_sampler, 15.384, unit="ns").start()) + + cocotb.start_soon(Clock(dut.clk_generator, 8, unit="ns").start()) + + self.axil = AxiLiteMaster( + AxiLiteBus.from_prefix(dut, "s_axil"), + dut.ctrl_clk, + dut.rst) + + + # self.axis_source = AxiStreamSource( + # AxiStreamBus.from_prefix(dut, "s_axis_write_data"), + # dut.ctrl_clk, + # dut.rst) + + self.axis_sink = AxiStreamSink( + AxiStreamBus.from_prefix(dut, "m_axis_read_data"), + dut.ctrl_clk, + dut.rst) + + async def reset(self): + self.dut.rst.value = 1 + for _ in range(20): + await RisingEdge(self.dut.ctrl_clk) + self.dut.rst.value = 0 + for _ in range(20): + await RisingEdge(self.dut.ctrl_clk) + + async def write_reg(self, reg_index: int, value: int): + await self.axil.write(reg_addr(reg_index), u32(value)) + + async def read_reg(self, reg_index: int) -> int: + resp = await self.axil.read(reg_addr(reg_index), 4) + return int.from_bytes(bytes(resp.data), "little") + + async def pulse_control(self, mask): + await self.write_reg( REG_CONTROL, mask ) + + # Reflectometer Driver + + async def configure_reflectometer ( + self, + pulse_width, + pulse_period, + pulse_num, + pulse_height, + adc_period, + window_size, + timeout_cycles): + await self.write_reg(REG_DAC_WIDTH, pulse_width) + await self.write_reg(REG_DAC_PERIOD, pulse_period) + await self.write_reg(REG_DAC_PULSE_NUM, pulse_num) + await self.write_reg(REG_DAC_PULSE_HEIGHT, pulse_height) + await self.write_reg(REG_ADC_PERIOD, adc_period) + await self.write_reg(REG_WINDOW_SIZE, window_size) + + await self.pulse_control(CTRL_CFG_BUS_VALID) + + control = self.dut.dut.reflectometer_ip_inst.controller_wrapper_axil_inst.controller + + for cycle in range(timeout_cycles): + dac_wait = int(control.cfg_wait_dac_ack.value) + adc_wait = int(control.cfg_wait_adc_ack.value) + + if dac_wait == 0 and adc_wait == 0: + print(f"Configuration done after {cycle} ctrl_clk cycles") + return + await RisingEdge(self.dut.ctrl_clk) + + async def send_start(self): + await self.pulse_control(CTRL_START) + + async def soft_reset(self): + await self.pulse_control(CTRL_RST_SOFT) + + async def get_status(self): + status = await self.read_reg(REG_STATUS) + return { + "busy": bool(status & STATUS_BUSY), + "processing_done": bool(status & STATUS_PROCESSING_DONE), + "desc_read_busy": bool(status & STATUS_DESC_READ_BUSY), + "desc_write_busy": bool(status & STATUS_DESC_WRITE_BUSY), + "status_read_busy": bool(status & STATUS_STATUS_READ_BUSY), + "status_write_busy": bool(status & STATUS_STATUS_WRITE_BUSY), + "desc_read_hs": bool(status & STATUS_DESC_READ_HS), + "desc_write_hs": bool(status & STATUS_DESC_WRITE_HS), + "status_read_hs": bool(status & STATUS_STATUS_READ_HS), + "status_write_hs": bool(status & STATUS_STATUS_WRITE_HS) + } + + async def wait_status(self, field, value=True, timeout_cycles=1000): + + for _ in range(timeout_cycles): + status = await self.get_status() + + if status[field] == value: + return + + await RisingEdge(self.dut.ctrl_clk) + + status = await self.get_status() + + raise TimeoutError( f"Timeout waiting for status.{field} == {value}. " f"Current status = {status}") + + async def wait_processing_done(self, timeout_cycles=1000): + await self.wait_status("processing_done", True, timeout_cycles) + + + async def wait_finish(self, timeout_cycles=1000): + await self.wait_status("busy", False, timeout_cycles) + + # DMA Driver + + async def send_desc_write(self, addr, length_tag): + + await self.write_reg(REG_DESC_WRITE_ADDR, addr) + await self.write_reg(REG_DESC_WRITE_LEN_AND_TAG, length_tag) + await self.pulse_control(CTRL_SEND_DESC_WRITE) + + async def send_desc_read(self, addr, length, config): + + await self.write_reg(REG_DESC_READ_ADDR, addr) + await self.write_reg(REG_DESC_READ_LEN, length) + await self.write_reg(REG_DESC_READ_CONFIG, config) + await self.pulse_control( CTRL_SEND_DESC_READ) + + async def take_status_write(self, status_write_len, status_write_config): + await self.write_reg(REG_CONTROL, CTRL_TAKE_STATUS_WRITE) + + + assert await self.read_reg(REG_STATUS_WRITE_CONFIG) == status_write_config + assert await self.read_reg(REG_STATUS_WRITE_LEN) == status_write_len + + async def take_status_read(self, status_read): + await self.pulse_control( CTRL_TAKE_STATUS_READ) + for _ in range(10): + await RisingEdge(self.dut.ctrl_clk) + + assert await self.read_reg(REG_READ_STATUS) == status_read + + async def wait_dma_write_done(self, timeout_cycles=1000): + await self.wait_status("desc_write_busy", False, timeout_cycles) + + async def wait_dma_read_done(self, timeout_cycles=1000): + await self.wait_status("desc_read_busy", False, timeout_cycles) + + async def wait_status_read_handshake(self, timeout_cycles=1000): + await self.wait_status("status_read_hs", True, timeout_cycles) + + async def wait_status_write_handshake(self, timeout_cycles=1000): + await self.wait_status("status_write_hs", True, timeout_cycles) + + # AxiStream Driver + + # async def send_axis_data(self, data: bytes): + + # await self.axis_source.send(AxiStreamFrame(data) ) + + async def receive_axis_data(self): + + frame = await self.axis_sink.recv() + return bytes(frame) + diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/full_reflectometer_test.py b/designs/full_new_reflectometer_without_clk_wiz/tests/full_reflectometer_test.py new file mode 100644 index 0000000..6f162dc --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/full_reflectometer_test.py @@ -0,0 +1,174 @@ +import cocotb +from cocotb.triggers import RisingEdge + +from drivers import Drivers +from reference_model import Reference_model +from scoreboard import Scoreboard + +from reg_map import * + +class TB: + + def __init__( + self, + dut, + pulse_width, + pulse_period, + pulse_num, + pulse_height, + adc_period, + window_size + ): + self.dut = dut + + self.driver = Drivers(dut) + + self.reference = Reference_model( + dut=dut, + + pulse_width=pulse_width, + pulse_period=pulse_period, + pulse_num=pulse_num, + pulse_height=pulse_height, + adc_period=adc_period, + window_size=window_size, + + DAC_DATA_WIDTH=DAC_DATA_WIDTH, + ADC_DATA_WIDTH=ADC_DATA_WIDTH, + PACK_FACTOR=PACK_FACTOR, + PROCESS_MODE=PROCESS_MODE, + ZERO_LEVEL=ZERO_LEVEL, + ACCUM_WIDTH=ACCUM_WIDTH, + N_MAX=N_MAX, + PACKET_SIZE=PACKET_SIZE, + RD_FIFO_WIDTH=RD_FIFO_WIDTH, + ) + + self.scoreboard = Scoreboard() + + +SEQ_NUM = 1 +WINDOW_SIZE = 1 + +PULSE_WIDTH = 10 +PULSE_PERIOD = 20 +PULSE_HEIGHT = 15000 +ADC_PERIOD = 20 + +@cocotb.test() +async def rest_init(dut): + tb = TB(dut, + pulse_width=PULSE_WIDTH, + pulse_period=PULSE_PERIOD, + pulse_num=SEQ_NUM, + pulse_height=PULSE_HEIGHT, + adc_period=ADC_PERIOD, + window_size=WINDOW_SIZE) + await tb.driver.reset() + +@cocotb.test() +async def base_full_test(dut): + tb = TB(dut, + pulse_width=PULSE_WIDTH, + pulse_period=PULSE_PERIOD, + pulse_num=SEQ_NUM, + pulse_height=PULSE_HEIGHT, + adc_period=ADC_PERIOD, + window_size=WINDOW_SIZE + ) + await tb.driver.reset() + await tb.driver.soft_reset() + + RESULT_ADDR = 0x1000 + + await tb.driver.configure_reflectometer( + pulse_width = PULSE_WIDTH, + pulse_period=PULSE_PERIOD, + pulse_num=SEQ_NUM, + pulse_height=PULSE_HEIGHT, + adc_period=ADC_PERIOD, + window_size=WINDOW_SIZE, + timeout_cycles=1000 + ) + + samples = tb.reference.gen_input_samples() + + SMP_NUM = len(samples[0]) + # SMP_NUM = ADC_PERIOD + + expected = tb.reference.calculate_expected( samples, WINDOW_SIZE, ACCUM_WIDTH) + + print("") + print("========================================") + print("ACCUMULATOR RANDOM TEST") + print("========================================") + print(f"seq_num = {SEQ_NUM}") + print(f"smp_num = {SMP_NUM}") + print(f"window_size = {WINDOW_SIZE}") + print(f"data_width = {ADC_DATA_WIDTH}") + print(f"accum_width = {ACCUM_WIDTH}") + print(f"expected words = {len(expected)}") + + RESULT_WORDS = len(expected) + RESULT_BYTES = RESULT_WORDS * 4 + + + print("==============================") + print("ACCUM TEST") + print("words =", RESULT_WORDS) + print("bytes =", RESULT_BYTES) + print("==============================") + + for _ in range(10): + await RisingEdge(dut.ctrl_clk) + + await tb.driver.send_start() + + for _ in range(4): + await RisingEdge(dut.ctrl_clk) + + await tb.driver.wait_processing_done(timeout_cycles=100000) + + await tb.driver.send_desc_write( addr=RESULT_ADDR, length_tag=RESULT_BYTES ) + await tb.driver.wait_dma_write_done(timeout_cycles=5000) + + for _ in range(100): + await RisingEdge(dut.ctrl_clk) + + # await tb.driver.take_status_write(status_write_len=RESULT_BYTES, status_write_config=0) + + await tb.driver.send_desc_read(addr=RESULT_ADDR, length=RESULT_BYTES, config=0x0000_0001 ) + + received_data = await tb.driver.receive_axis_data() + + await tb.driver.wait_dma_read_done(timeout_cycles=5000) + + + await tb.driver.take_status_read(status_read=0x1) + + + received = tb.scoreboard.bytes_to_words(received_data, RD_FIFO_WIDTH) + + print("") + print("Expected:") + for i, value in enumerate(expected): + print(f" [{i}] = 0x{value:08X}") + + print("") + print("Received:") + for i, value in enumerate(received): + print(f" [{i}] = 0x{value:08X}") + + tb.scoreboard.check_results( + expected=expected, + received=received + ) + + print("") + print("========================================") + print("ACCUMULATOR RANDOM TEST PASSED") + print("========================================") + + + + \ No newline at end of file diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/reference_model.py b/designs/full_new_reflectometer_without_clk_wiz/tests/reference_model.py new file mode 100644 index 0000000..fe9cefe --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/reference_model.py @@ -0,0 +1,253 @@ +from reg_map import * + +class Reference_model: + def __init__( + self, + dut, + pulse_width, + pulse_period, + pulse_num, + pulse_height, + adc_period, + window_size, + DAC_DATA_WIDTH, + ADC_DATA_WIDTH, + PACK_FACTOR, + PROCESS_MODE, + ZERO_LEVEL, + ACCUM_WIDTH, + N_MAX, + PACKET_SIZE, + RD_FIFO_WIDTH + ): + + self.dut = dut + + # configuration + self.pulse_width = pulse_width + self.pulse_period = pulse_period + self.pulse_num = pulse_num + self.pulse_height = pulse_height + self.adc_sample_num = adc_period + self.window_size = window_size + + # parameters + self.DAC_DATA_WIDTH = DAC_DATA_WIDTH + self.ADC_DATA_WIDTH = ADC_DATA_WIDTH + self.PACK_FACTOR = PACK_FACTOR + self.PROCESS_MODE = PROCESS_MODE + self.ZERO_LEVEL = ZERO_LEVEL + self.ACCUM_WIDTH = ACCUM_WIDTH + self.N_MAX = N_MAX + self.PACKET_SIZE = PACKET_SIZE + self.RD_FIFO_WIDTH = RD_FIFO_WIDTH + + # intermediate data + self.samples = [] + self.expected = [] + + + def gen_input_samples(self): + + self.samples = [] + + # ----------------------------- + # DAC: 14 bit + # ----------------------------- + DAC_ZERO = self.ZERO_LEVEL + DAC_MAX = (1 << self.DAC_DATA_WIDTH) - 1 + + DAC_RANGE = 5.0 + + DAC_STEP = ( + 2.0 * DAC_RANGE + ) / DAC_MAX + + # ----------------------------- + # ADC: 12 bit + # ----------------------------- + ADC_ZERO = 1 << (self.ADC_DATA_WIDTH - 1) + ADC_MAX = (1 << self.ADC_DATA_WIDTH) - 1 + + ADC_RANGE = 1.0 + ADC_GAIN = 0.2 + GROUND_BIAS = 0.0 + + ADC_STEP = ( + 2.0 * ADC_RANGE + ) / ADC_MAX + + # ----------------------------- + # Generate every pulse sequence + # ----------------------------- + for _ in range(self.pulse_num): + + pulse_samples = [] + + for sample_idx in range(self.adc_sample_num): + + # ========================================== + # 1. Generator produces a 14-bit DAC code + # ========================================== + if sample_idx < self.pulse_width: + dac_code = self.pulse_height + else: + dac_code = DAC_ZERO + + # Limit to actual DAC width + dac_code = max(0, min(dac_code, DAC_MAX)) + + # ========================================== + # 2. 14-bit DAC code -> analog voltage + # ========================================== + voltage = ( + (dac_code - DAC_ZERO) + * DAC_STEP + ) + + # ========================================== + # 3. Analog path -> ADC input voltage + # ========================================== + voltage = ( + (voltage - GROUND_BIAS) + * ADC_GAIN + ) + + # ========================================== + # 4. Analog voltage -> 12-bit ADC code + # ========================================== + if voltage <= -ADC_RANGE: + + adc_code = 0 + + elif voltage >= ADC_RANGE: + + adc_code = ADC_MAX + + else: + + adc_code = int( + round( + voltage / ADC_STEP + + ADC_ZERO + ) + ) + + # Make absolutely sure that the result + # is a valid 12-bit value. + adc_code = max( + 0, + min(adc_code, ADC_MAX) + ) + + # ========================================== + # 5. ADC out-of-range processing + # ========================================== + out_of_range = ( + abs(voltage) >= ADC_RANGE + ) + + if self.PROCESS_MODE: + + msb = ( + adc_code + >> (self.ADC_DATA_WIDTH - 1) + ) & 1 + + if out_of_range: + + if msb: + sample = ADC_MAX + else: + sample = 0 + + else: + + sample = ( + (((~msb) & 1) + << (self.ADC_DATA_WIDTH - 1)) + | + ( + adc_code + & ( + (1 << (self.ADC_DATA_WIDTH - 1)) + - 1 + ) + ) + ) + + else: + + if out_of_range: + + if adc_code & ADC_ZERO: + sample = ADC_MAX + else: + sample = 0 + + else: + + sample = adc_code + + # ========================================== + # Final sample is ALWAYS 12-bit ADC data + # ========================================== + sample &= ADC_MAX + + pulse_samples.append(sample) + + self.samples.append(pulse_samples) + + return self.samples + + + + + def calculate_expected( + self, + samples, + window_size: int, + accum_width: int): + if window_size <= 0: + raise ValueError( f"window_size must be > 0, got {window_size}") + + if not samples: + raise ValueError("samples must not be empty") + + seq_num = len(samples) + smp_num = len(samples[0]) + + if smp_num == 0: + raise ValueError("samples must not contain empty sequences") + + for seq_idx, seq_samples in enumerate(samples): + if len(seq_samples) != smp_num: + raise ValueError(f"Sequence {seq_idx} has {len(seq_samples)} samples, " f"expected {smp_num}" ) + + if smp_num % window_size != 0: + raise ValueError(f"smp_num ({smp_num}) must be divisible " f"by window_size ({window_size})") + + exp_word_count = smp_num // window_size + accum_mask = (1 << accum_width) - 1 + + expected = [] + + for word_idx in range(exp_word_count): + local_sum = 0 + + for seq_idx in range(seq_num): + for k in range(window_size): + sample_idx = word_idx * window_size + k + local_sum += samples[seq_idx][sample_idx] + + expected.append(local_sum & accum_mask) + + return expected + + + def run(self): + self.gen_input_samples() + + self.expected = self.calculate_expected( self.samples, self.window_size, self.ACCUM_WIDTH) + + return self.expected \ No newline at end of file diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/reg_map.py b/designs/full_new_reflectometer_without_clk_wiz/tests/reg_map.py new file mode 100644 index 0000000..349a119 --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/reg_map.py @@ -0,0 +1,54 @@ +# Register indexes from axi4l_reg_map_controller_pkg.sv +REG_CONTROL = 0 +REG_STATUS = 1 +REG_DAC_WIDTH = 2 +REG_DAC_PERIOD = 3 +REG_DAC_PULSE_NUM = 4 +REG_DAC_PULSE_HEIGHT = 5 +REG_ADC_PERIOD = 6 +REG_WINDOW_SIZE = 7 +REG_ERROR = 8 +REG_DESC_READ_ADDR = 9 +REG_DESC_READ_LEN = 10 +REG_DESC_READ_CONFIG = 11 +REG_READ_STATUS = 12 +REG_DESC_WRITE_ADDR = 13 +REG_DESC_WRITE_LEN_AND_TAG = 14 +REG_STATUS_WRITE_LEN = 15 +REG_STATUS_WRITE_CONFIG = 16 + + +# REG_CONTROL pulse bits +CTRL_START = 1 << 0 +CTRL_RST_SOFT = 1 << 1 +CTRL_CFG_BUS_VALID = 1 << 2 +CTRL_SEND_DESC_READ = 1 << 3 +CTRL_SEND_DESC_WRITE = 1 << 4 +CTRL_TAKE_STATUS_READ = 1 << 5 +CTRL_TAKE_STATUS_WRITE = 1 << 6 + +# REG_STATUS bits +STATUS_BUSY = 1 << 0 +STATUS_PROCESSING_DONE = 1 << 1 + +STATUS_DESC_READ_BUSY = 1 << 2 +STATUS_DESC_WRITE_BUSY = 1 << 3 +STATUS_STATUS_READ_BUSY = 1 << 4 +STATUS_STATUS_WRITE_BUSY = 1 << 5 + +STATUS_DESC_READ_HS = 1 << 6 +STATUS_DESC_WRITE_HS = 1 << 7 +STATUS_STATUS_READ_HS = 1 << 8 +STATUS_STATUS_WRITE_HS = 1 << 9 + +# PARAMETERS for accumulator reference model + +DAC_DATA_WIDTH = 14 +ADC_DATA_WIDTH = 12 +PACK_FACTOR = 1 +PROCESS_MODE = 0 +ZERO_LEVEL = 8192 +ACCUM_WIDTH = 32 +N_MAX = 4096 +PACKET_SIZE = 1024 +RD_FIFO_WIDTH = 32 \ No newline at end of file diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/scoreboard.py b/designs/full_new_reflectometer_without_clk_wiz/tests/scoreboard.py new file mode 100644 index 0000000..e73bd8f --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/scoreboard.py @@ -0,0 +1,40 @@ +from reg_map import * + +class Scoreboard: + def __init__(self): + + self.test_passed = False + + + def bytes_to_words(self, data: bytes, word_width: int = 32): + word_bytes = word_width // 8 + + if len(data) % word_bytes != 0: + raise ValueError(f"Data length {len(data)} is not divisible " f"by word size {word_bytes}" ) + + words = [] + + for i in range(0, len(data), word_bytes): + word = int.from_bytes(data[i:i + word_bytes], byteorder="little" ) + words.append(word) + + return words + + + def check_results(self, expected, received): + + assert len(received) == len(expected), ( + f"Number of words mismatch: " + f"expected={len(expected)}, " + f"received={len(received)}" ) + + for i, (exp, rec) in enumerate(zip(expected, received)): + assert rec == exp, ( + f"Payload mismatch at index {i}: " + f"expected=0x{exp:08X}, " + f"received=0x{rec:08X}" ) + self.test_passed = True + + print( f"Payload check passed: " f"{len(expected)} words") + + return True diff --git a/designs/full_new_reflectometer_without_clk_wiz/tests/tb_full_reflectometer.sv b/designs/full_new_reflectometer_without_clk_wiz/tests/tb_full_reflectometer.sv new file mode 100644 index 0000000..e961be4 --- /dev/null +++ b/designs/full_new_reflectometer_without_clk_wiz/tests/tb_full_reflectometer.sv @@ -0,0 +1,263 @@ + +import dma_reg_pkg::*; + +module tb_full_reflectometer #( + // parameters for base reflectometer works + parameter int unsigned DAC_DATA_WIDTH = 14, + parameter int unsigned ADC_DATA_WIDTH = 12, + parameter int unsigned PACK_FACTOR = 1, + parameter int unsigned PROCESS_MODE = 0, + parameter int unsigned ZERO_LEVEL = 8192, + parameter int unsigned ACCUM_WIDTH = 32, + parameter int unsigned N_MAX = 4096, + parameter int unsigned PACKET_SIZE = 64, + parameter int unsigned RD_FIFO_WIDTH = 32, + + // parameters for DMA and interfaces + parameter int unsigned ADDR_W = 16, + parameter int unsigned DATA_W = 32, + parameter int unsigned USER_W = 1, + + parameter int unsigned AXI_DATA_WIDTH = 32, + + parameter int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8, + parameter int unsigned AXI_USER_WIDTH = 1, + parameter int unsigned AXI_ID_WIDTH = 8, + parameter int unsigned AXI_MAX_BURST_LEN = 16, + + parameter int unsigned AXIS_DATA_WIDTH = AXI_DATA_WIDTH, + parameter int unsigned AXIS_KEEP_ENABLE = AXIS_DATA_WIDTH > 8, + parameter int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8, + parameter int unsigned AXIS_LAST_ENABLE = 1, + parameter int unsigned AXIS_ID_ENABLE = 1, + parameter int unsigned AXIS_DEST_ENABLE = 0, + parameter int unsigned AXIS_USER_ENABLE = 1, + + parameter int unsigned ENABLE_SG = 0, + parameter int unsigned ENABLE_UNALIGNED = 0, + + parameter int unsigned PIPELINE_OUTPUT = 0 +)( + input logic ctrl_clk, + input logic clk_sampler, + input logic clk_generator, + input logic rst, + + input logic [ADDR_W-1:0] s_axil_awaddr, + input logic [2:0] s_axil_awprot, + input logic s_axil_awvalid, + output logic s_axil_awready, + + input logic [DATA_W-1:0] s_axil_wdata, + input logic [DATA_W/8-1:0] s_axil_wstrb, + input logic s_axil_wvalid, + output logic s_axil_wready, + + output logic [1:0] s_axil_bresp, + output logic s_axil_bvalid, + input logic s_axil_bready, + + input logic [ADDR_W-1:0] s_axil_araddr, + input logic [2:0] s_axil_arprot, + input logic s_axil_arvalid, + output logic s_axil_arready, + + output logic [DATA_W-1:0] s_axil_rdata, + output logic [1:0] s_axil_rresp, + output logic s_axil_rvalid, + input logic s_axil_rready, + + output wire [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep, + output wire m_axis_read_data_tvalid, + input wire m_axis_read_data_tready, + output wire m_axis_read_data_tlast, + output wire [dma_reg_pkg::AXIS_ID_WIDTH-1:0] m_axis_read_data_tid, + output wire [dma_reg_pkg::AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest, + output wire [dma_reg_pkg::AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser +); + +logic rst_n; +logic dac_wrt; + + OBUF OBUF_pulse_clk ( + .I(clk_generator), + .O(dac_wrt) + ); + +assign rst_n = ~rst; + +wire adc_otr; +wire [ADC_DATA_WIDTH-1:0] adc_data; + +wire [DAC_DATA_WIDTH-1:0] dac_data; + +real signal_voltage; + +virtual_dac_model #( // default voltage range is +/- 5V + .DAC_DATA_WIDTH(DAC_DATA_WIDTH) + ) virtual_dac ( + .clk_i(clk_generator), + .wrt_i(dac_wrt), + .data_i(dac_data), + .voltage_o(signal_voltage) + ); + +virtual_adc_model #( // default voltage range is +/- 5V + .ADC_DATA_WIDTH(ADC_DATA_WIDTH) + ) virtual_adc ( + .clk_i(clk_sampler), + .voltage_i(signal_voltage), + .otr_o(adc_otr), + .data_o(adc_data) + ); + + // --------------------------------------------------------------------------- + // AXI-Lite flat -> axi4l_if + // --------------------------------------------------------------------------- + + axi4l_if #( + .ADDR_W(ADDR_W), + .DATA_W(DATA_W), + .USER_W(USER_W) + ) axil_bus ( + .aclk(ctrl_clk), + .aresetn(rst_n) + ); + + axi4l_flat_to_if #( + .ADDR_W(ADDR_W), + .DATA_W(DATA_W), + .USER_W(USER_W) + ) u_axil_flat_to_if ( + .s_axil_awaddr (s_axil_awaddr), + .s_axil_awprot (s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + + .s_axil_wdata (s_axil_wdata), + .s_axil_wstrb (s_axil_wstrb), + .s_axil_wvalid (s_axil_wvalid), + .s_axil_wready (s_axil_wready), + + .s_axil_bresp (s_axil_bresp), + .s_axil_bvalid (s_axil_bvalid), + .s_axil_bready (s_axil_bready), + + .s_axil_araddr (s_axil_araddr), + .s_axil_arprot (s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + + .s_axil_rdata (s_axil_rdata), + .s_axil_rresp (s_axil_rresp), + .s_axil_rvalid (s_axil_rvalid), + .s_axil_rready (s_axil_rready), + + .m_axil(axil_bus) + ); + + // --------------------------------------------------------------------------- + // AXIS interfaces for the updated controller_wrapper_axil + // --------------------------------------------------------------------------- + +// AXIS READ DMA MASTER output + axis_if #( + .DATA_W (AXIS_DATA_WIDTH), + .KEEP_W (AXIS_KEEP_WIDTH), + .ID_W (AXIS_ID_WIDTH), + .DEST_W (dma_reg_pkg::AXIS_DEST_WIDTH), + .USER_W (dma_reg_pkg::AXIS_USER_WIDTH) + ) dma_read_data ( + .aclk(ctrl_clk), + .aresetn(rst_n) + ); + +logic [AXIS_KEEP_WIDTH-1:0] unused_read_tstrb; + +axis_if_to_flat #( + .DATA_W (AXIS_DATA_WIDTH), + .KEEP_W (AXIS_KEEP_WIDTH), + .ID_W (AXIS_ID_WIDTH), + .DEST_W (dma_reg_pkg::AXIS_DEST_WIDTH), + .USER_W (dma_reg_pkg::AXIS_USER_WIDTH) +) u_read_data_if_to_flat ( + .s_axis(dma_read_data), + + .m_axis_tdata (m_axis_read_data_tdata), + .m_axis_tkeep (m_axis_read_data_tkeep), + .m_axis_tstrb (unused_read_tstrb), + .m_axis_tlast (m_axis_read_data_tlast), + .m_axis_tid (m_axis_read_data_tid), + .m_axis_tdest (m_axis_read_data_tdest), + .m_axis_tuser (m_axis_read_data_tuser), + .m_axis_tvalid(m_axis_read_data_tvalid), + .m_axis_tready(m_axis_read_data_tready) +); + +axi4_if #( + .ADDR_W(dma_reg_pkg::AXI_ADDR_WIDTH), + .DATA_W(AXI_DATA_WIDTH), + .ID_W (AXI_ID_WIDTH), + .USER_W(AXI_USER_WIDTH) + ) m_axi ( + .aclk(ctrl_clk), + .aresetn(rst_n) + ); + + reflectometer_and_dma_wrapper #( + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_USER_WIDTH(AXI_USER_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN), + .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH), + .AXIS_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .AXIS_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .AXIS_LAST_ENABLE(AXIS_LAST_ENABLE), + .AXIS_ID_ENABLE(AXIS_ID_ENABLE), + .AXIS_DEST_ENABLE(AXIS_DEST_ENABLE), + .AXIS_USER_ENABLE(AXIS_USER_ENABLE), + .ENABLE_SG(ENABLE_SG), + .ENABLE_UNALIGNED(ENABLE_UNALIGNED), + + .DAC_DATA_WIDTH(DAC_DATA_WIDTH), + .ADC_DATA_WIDTH(ADC_DATA_WIDTH), + .PACK_FACTOR(PACK_FACTOR), + .PROCESS_MODE(PROCESS_MODE), + .ZERO_LEVEL(ZERO_LEVEL), + .ACCUM_WIDTH(ACCUM_WIDTH), + .N_MAX(N_MAX), + .PACKET_SIZE(PACKET_SIZE), + .RD_FIFO_WIDTH(RD_FIFO_WIDTH) + ) dut ( + .ctrl_clk(ctrl_clk), + .clk_sampler(clk_sampler), + .clk_generator(clk_generator), + .ctrl_rst_n(rst_n), + .s_axil(axil_bus), + + .m_axi(m_axi), + + .m_axis_read_data(dma_read_data), + + .dac_data(dac_data), + .adc_data(adc_data), + .adc_otr(adc_otr) + ); + + axi_ram_wrapper +#( + .DATA_WIDTH(AXI_DATA_WIDTH), + .ADDR_WIDTH(dma_reg_pkg::AXI_ADDR_WIDTH), + .ID_WIDTH(dma_reg_pkg::AXIS_ID_WIDTH), + .PIPELINE_OUTPUT(PIPELINE_OUTPUT) +) axi_ram_wrapper_inst +( + .clk(ctrl_clk), + .rst(rst), + .s_axi(m_axi) +); + + + +endmodule : tb_full_reflectometer