test for reflectometer without clk wizard

This commit is contained in:
otroubi
2026-09-07 13:59:39 +03:00
parent 4a092a0ece
commit f6139daf11
9 changed files with 1151 additions and 0 deletions
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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("========================================")
@@ -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
@@ -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
@@ -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
@@ -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