test and build the project to verify the functionality of the bundle accum-dma
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
TOPLEVEL_LANG = verilog
|
||||||
|
SIM ?= verilator
|
||||||
|
|
||||||
|
PWD := $(shell pwd)
|
||||||
|
|
||||||
|
WRAP_DIR = $(PWD)/../src
|
||||||
|
RTL_DIR = $(PWD)/../../controller_new/src
|
||||||
|
RTL_ACCUM_DIR = $(PWD)/../src/accum/src
|
||||||
|
|
||||||
|
LIBS_DIR = $(PWD)/../../../external/rtl_libs
|
||||||
|
XPM_DIR = /mnt/c/Xilinx/Vivado/2021.2/data/ip/xpm
|
||||||
|
|
||||||
|
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/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_DIR)/controller.sv
|
||||||
|
VERILOG_SOURCES += $(RTL_DIR)/dma_controller.sv
|
||||||
|
VERILOG_SOURCES += $(RTL_DIR)/shaper_axis_desc.sv
|
||||||
|
VERILOG_SOURCES += $(RTL_DIR)/shaper_axis_status.sv
|
||||||
|
VERILOG_SOURCES += $(RTL_DIR)/controller_wrapper_axil.sv
|
||||||
|
VERILOG_SOURCES += $(RTL_DIR)/axi4l_reg_map_controller_pkg.sv
|
||||||
|
VERILOG_SOURCES += $(RTL_DIR)/axis_defaults_helper.sv
|
||||||
|
VERILOG_SOURCES += $(RTL_DIR)/axi4l_reg_map_controller.sv
|
||||||
|
VERILOG_SOURCES += $(WRAP_DIR)/wrapper_controller_dma_accum.sv
|
||||||
|
VERILOG_SOURCES += $(WRAP_DIR)/axi_dma_wrapper_if.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 += $(XPM_DIR)/xpm_fifo/hdl/xpm_fifo.sv
|
||||||
|
VERILOG_SOURCES += $(XPM_DIR)/xpm_memory/hdl/xpm_memory.sv
|
||||||
|
VERILOG_SOURCES += $(XPM_DIR)/xpm_cdc/hdl/xpm_cdc.sv
|
||||||
|
|
||||||
|
VERILOG_SOURCES += $(PWD)/tb_controller_dma_accum_wrapper_axil.sv
|
||||||
|
|
||||||
|
TOPLEVEL = tb_controller_dma_accum_wrapper_axil
|
||||||
|
MODULE = test_controller_dma_accum
|
||||||
|
|
||||||
|
|
||||||
|
ifeq ($(SIM),verilator)
|
||||||
|
EXTRA_ARGS += --trace --trace-structs
|
||||||
|
EXTRA_ARGS += -I$(LIBS_DIR)/axi/rtl/
|
||||||
|
COMPILE_ARGS += -Wno-fatal
|
||||||
|
COMPILE_ARGS += -I$(LIBS_DIR)/axi/rtl/
|
||||||
|
EXTRA_ARGS += --trace
|
||||||
|
EXTRA_ARGS += --trace-structs
|
||||||
|
EXTRA_ARGS += --public-flat-rw
|
||||||
|
EXTRA_ARGS += -Wno-fatal
|
||||||
|
EXTRA_ARGS += --timing
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
+568
@@ -0,0 +1,568 @@
|
|||||||
|
import cocotb
|
||||||
|
import random
|
||||||
|
from cocotb.clock import Clock
|
||||||
|
from cocotb.triggers import RisingEdge
|
||||||
|
from cocotbext.axi import AxiLiteBus, AxiLiteMaster
|
||||||
|
from cocotbext.axi import AxiBus, AxiRam, AxiStreamBus, AxiStreamSource, AxiStreamSink, AxiStreamFrame
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
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 TB:
|
||||||
|
def __init__(self, dut):
|
||||||
|
self.dut = dut
|
||||||
|
|
||||||
|
cocotb.start_soon(Clock(dut.ctrl_clk, 10, units="ns").start())
|
||||||
|
|
||||||
|
cocotb.start_soon(Clock(dut.adc_clk_in, 15.3846, units="ns").start())
|
||||||
|
|
||||||
|
cocotb.start_soon(Clock(dut.dac_clk_in, 8.333, units="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(5):
|
||||||
|
await RisingEdge(self.dut.ctrl_clk)
|
||||||
|
|
||||||
|
self.dut.rst.value = 0
|
||||||
|
for _ in range(5):
|
||||||
|
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
|
||||||
|
):
|
||||||
|
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)
|
||||||
|
|
||||||
|
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):
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
status = await self.get_status()
|
||||||
|
|
||||||
|
if status[field] == value:
|
||||||
|
return
|
||||||
|
|
||||||
|
await RisingEdge(self.dut.ctrl_clk)
|
||||||
|
|
||||||
|
async def wait_processing_done(self):
|
||||||
|
await self.wait_status("processing_done")
|
||||||
|
|
||||||
|
|
||||||
|
async def wait_finish(self):
|
||||||
|
await self.wait_status("busy", False)
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
await self.pulse_control(CTRL_TAKE_STATUS_WRITE)
|
||||||
|
|
||||||
|
return (
|
||||||
|
await self.read_reg(REG_STATUS_WRITE_LEN),
|
||||||
|
await self.read_reg(REG_STATUS_WRITE_CONFIG) )
|
||||||
|
|
||||||
|
async def take_read_status(self):
|
||||||
|
|
||||||
|
await self.pulse_control( CTRL_TAKE_STATUS_READ)
|
||||||
|
return await self.read_reg(REG_READ_STATUS)
|
||||||
|
|
||||||
|
async def wait_dma_write_done(self):
|
||||||
|
await self.wait_status("desc_write_busy", True)
|
||||||
|
await self.wait_status("desc_write_busy", False)
|
||||||
|
|
||||||
|
async def wait_dma_read_done(self):
|
||||||
|
await self.wait_status("desc_read_busy", True)
|
||||||
|
await self.wait_status("desc_read_busy", False)
|
||||||
|
|
||||||
|
async def wait_status_read_handshake(self):
|
||||||
|
await self.wait_status("status_read_hs")
|
||||||
|
|
||||||
|
async def wait_status_write_handshake(self):
|
||||||
|
await self.wait_status("status_write_hs")
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# Accum transaction generator
|
||||||
|
|
||||||
|
def generate_samples(
|
||||||
|
self,
|
||||||
|
seq_num: int,
|
||||||
|
smp_num: int,
|
||||||
|
data_width: int,
|
||||||
|
seed: int | None = None):
|
||||||
|
if seq_num <= 0:
|
||||||
|
raise ValueError(f"seq_num must be > 0, got {seq_num}")
|
||||||
|
|
||||||
|
if smp_num <= 0:
|
||||||
|
raise ValueError(f"smp_num must be > 0, got {smp_num}")
|
||||||
|
|
||||||
|
rng = random.Random(seed)
|
||||||
|
|
||||||
|
max_value = (1 << data_width) - 1
|
||||||
|
|
||||||
|
samples = []
|
||||||
|
|
||||||
|
for _ in range(seq_num):
|
||||||
|
seq_samples = []
|
||||||
|
|
||||||
|
for _ in range(smp_num):
|
||||||
|
seq_samples.append( rng.randint(0, max_value))
|
||||||
|
|
||||||
|
samples.append(seq_samples)
|
||||||
|
|
||||||
|
return 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
|
||||||
|
|
||||||
|
async def send_samples(self, samples):
|
||||||
|
self.dut.sampler_m_axis_tvalid.value = 0
|
||||||
|
self.dut.sampler_m_axis_tdata.value = 0
|
||||||
|
|
||||||
|
for seq_samples in samples:
|
||||||
|
|
||||||
|
for sample in seq_samples:
|
||||||
|
|
||||||
|
self.dut.sampler_m_axis_tdata.value = sample
|
||||||
|
self.dut.sampler_m_axis_tvalid.value = 1
|
||||||
|
|
||||||
|
await RisingEdge(self.dut.adc_clk_in)
|
||||||
|
|
||||||
|
self.dut.sampler_m_axis_tdata.value = 0
|
||||||
|
self.dut.sampler_m_axis_tvalid.value = 0
|
||||||
|
|
||||||
|
await RisingEdge(self.dut.adc_clk_in)
|
||||||
|
await RisingEdge(self.dut.adc_clk_in)
|
||||||
|
|
||||||
|
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}" )
|
||||||
|
|
||||||
|
print( f"Payload check passed: " f"{len(expected)} words")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@cocotb.test()
|
||||||
|
async def simple_axil_write_read(dut):
|
||||||
|
tb = TB(dut)
|
||||||
|
await tb.reset()
|
||||||
|
|
||||||
|
await tb.write_reg(REG_DAC_WIDTH, 0x0000_0123)
|
||||||
|
|
||||||
|
value = await tb.read_reg(REG_DAC_WIDTH)
|
||||||
|
assert value == 0x0000_0123
|
||||||
|
|
||||||
|
|
||||||
|
@cocotb.test()
|
||||||
|
async def simple_controller_config_write(dut):
|
||||||
|
tb = TB(dut)
|
||||||
|
await tb.reset()
|
||||||
|
|
||||||
|
await tb.write_reg(REG_DAC_WIDTH, 0x10)
|
||||||
|
await tb.write_reg(REG_DAC_PERIOD, 0x40)
|
||||||
|
await tb.write_reg(REG_DAC_PULSE_NUM, 3)
|
||||||
|
await tb.write_reg(REG_DAC_PULSE_HEIGHT, 0x7FF)
|
||||||
|
await tb.write_reg(REG_ADC_PERIOD, 0x80)
|
||||||
|
await tb.write_reg(REG_WINDOW_SIZE, 16)
|
||||||
|
|
||||||
|
assert await tb.read_reg(REG_DAC_WIDTH) == 0x10
|
||||||
|
assert await tb.read_reg(REG_WINDOW_SIZE) == 16
|
||||||
|
|
||||||
|
# write data: set cfg_bus_valid signal
|
||||||
|
await tb.write_reg(REG_CONTROL, 1 << CTRL_CFG_BUS_VALID)
|
||||||
|
|
||||||
|
# wait
|
||||||
|
for _ in range(30):
|
||||||
|
await RisingEdge(dut.ctrl_clk)
|
||||||
|
|
||||||
|
# check config
|
||||||
|
assert int(dut.dac_pulse_width) == 0x10
|
||||||
|
assert int(dut.dac_pulse_period) == 0x40
|
||||||
|
assert int(dut.dac_pulse_num) == 3
|
||||||
|
assert int(dut.dac_pulse_height) == 0x7FF
|
||||||
|
assert int(dut.adc_pulse_period) == 0x80
|
||||||
|
assert int(dut.adc_window_size) == 16
|
||||||
|
|
||||||
|
await RisingEdge(dut.ctrl_clk)
|
||||||
|
|
||||||
|
|
||||||
|
@cocotb.test()
|
||||||
|
async def simple_controller_start_check(dut):
|
||||||
|
tb = TB(dut)
|
||||||
|
await tb.reset()
|
||||||
|
await tb.write_reg(REG_CONTROL, 1 << CTRL_START)
|
||||||
|
|
||||||
|
await RisingEdge(dut.dac_start)
|
||||||
|
|
||||||
|
@cocotb.test()
|
||||||
|
async def simple_dma_mem_to_mem(dut):
|
||||||
|
tb = TB(dut)
|
||||||
|
await tb.reset()
|
||||||
|
|
||||||
|
SRC_ADDR = 0x1000
|
||||||
|
LENGTH = 8
|
||||||
|
|
||||||
|
tx_data = bytes([ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 ])
|
||||||
|
|
||||||
|
## write part
|
||||||
|
|
||||||
|
await tb.write_reg(REG_DESC_WRITE_ADDR, SRC_ADDR)
|
||||||
|
await tb.write_reg(REG_DESC_WRITE_LEN_AND_TAG, LENGTH)
|
||||||
|
|
||||||
|
await tb.write_reg(REG_CONTROL, 1 << CTRL_SEND_DESC_WRITE)
|
||||||
|
|
||||||
|
await tb.axis_source.send(AxiStreamFrame(tx_data))
|
||||||
|
|
||||||
|
for _ in range(20):
|
||||||
|
await RisingEdge(dut.ctrl_clk)
|
||||||
|
|
||||||
|
await tb.write_reg(REG_CONTROL, 1 << CTRL_TAKE_STATUS_WRITE)
|
||||||
|
|
||||||
|
for _ in range(10):
|
||||||
|
await RisingEdge(dut.ctrl_clk)
|
||||||
|
|
||||||
|
|
||||||
|
assert await tb.read_reg(REG_STATUS_WRITE_CONFIG) == 0
|
||||||
|
assert await tb.read_reg(REG_STATUS_WRITE_LEN) == 0x8
|
||||||
|
|
||||||
|
## read part
|
||||||
|
|
||||||
|
await tb.write_reg(REG_DESC_READ_ADDR, SRC_ADDR)
|
||||||
|
await tb.write_reg(REG_DESC_READ_LEN, LENGTH)
|
||||||
|
await tb.write_reg(REG_DESC_READ_CONFIG, 0x0000_0001)
|
||||||
|
|
||||||
|
await tb.write_reg(REG_CONTROL, 1 << CTRL_SEND_DESC_READ)
|
||||||
|
|
||||||
|
rx_frame = await tb.axis_sink.recv()
|
||||||
|
|
||||||
|
for _ in range(20):
|
||||||
|
await RisingEdge(dut.ctrl_clk)
|
||||||
|
|
||||||
|
await tb.write_reg(REG_CONTROL, 1 << CTRL_TAKE_STATUS_READ)
|
||||||
|
|
||||||
|
for _ in range(10):
|
||||||
|
await RisingEdge(dut.ctrl_clk)
|
||||||
|
|
||||||
|
assert await tb.read_reg(REG_READ_STATUS) == 0x1
|
||||||
|
|
||||||
|
assert bytes(rx_frame) == tx_data
|
||||||
|
|
||||||
|
|
||||||
|
@cocotb.test()
|
||||||
|
async def dma_accum_connection(dut):
|
||||||
|
tb = TB(dut)
|
||||||
|
await tb.reset()
|
||||||
|
|
||||||
|
SEQ_NUM = 3
|
||||||
|
SMP_NUM = 0x40 # 64 samples per sequence
|
||||||
|
WINDOW_SIZE = 16
|
||||||
|
|
||||||
|
PULSE_WIDTH = 0x10
|
||||||
|
PULSE_PERIOD = 0x40
|
||||||
|
PULSE_HEIGHT = 0x7FF
|
||||||
|
ADC_PERIOD = 0x80
|
||||||
|
|
||||||
|
RANDOM_SEED = 12345
|
||||||
|
|
||||||
|
await tb.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 )
|
||||||
|
|
||||||
|
samples = tb.generate_samples(
|
||||||
|
seq_num=SEQ_NUM,
|
||||||
|
smp_num=SMP_NUM,
|
||||||
|
data_width=ADC_DATA_WIDTH,
|
||||||
|
seed=RANDOM_SEED )
|
||||||
|
|
||||||
|
|
||||||
|
expected = tb.calculate_expected(
|
||||||
|
samples=samples,
|
||||||
|
window_size=WINDOW_SIZE,
|
||||||
|
accum_width=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 * (ACCUM_WIDTH // 8)
|
||||||
|
|
||||||
|
print(f"result bytes = {RESULT_BYTES}")
|
||||||
|
assert RESULT_WORDS == 4
|
||||||
|
assert RESULT_BYTES == 16
|
||||||
|
|
||||||
|
|
||||||
|
await tb.send_start()
|
||||||
|
|
||||||
|
await tb.send_samples(samples)
|
||||||
|
|
||||||
|
await tb.wait_processing_done()
|
||||||
|
|
||||||
|
RESULT_ADDR = 0x1000
|
||||||
|
|
||||||
|
await tb.send_desc_write( addr=RESULT_ADDR, length_tag=RESULT_BYTES )
|
||||||
|
|
||||||
|
await tb.wait_dma_write_done()
|
||||||
|
|
||||||
|
status_write_len, status_write_config = \
|
||||||
|
await tb.take_status_write()
|
||||||
|
|
||||||
|
assert status_write_config == 0
|
||||||
|
assert status_write_len == RESULT_BYTES
|
||||||
|
|
||||||
|
await tb.send_desc_read(addr=RESULT_ADDR, length=RESULT_BYTES, config=0x0000_0001 )
|
||||||
|
|
||||||
|
received_data = await tb.receive_axis_data()
|
||||||
|
|
||||||
|
await tb.wait_dma_read_done()
|
||||||
|
|
||||||
|
read_status = await tb.take_read_status()
|
||||||
|
|
||||||
|
assert read_status == 0x1
|
||||||
|
|
||||||
|
received = tb.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.check_results(
|
||||||
|
expected=expected,
|
||||||
|
received=received
|
||||||
|
)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print("========================================")
|
||||||
|
print("ACCUMULATOR RANDOM TEST PASSED")
|
||||||
|
print("========================================")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user