waveform: write and start simulation
This commit is contained in:
parent
35f55c8e1d
commit
1d85e2307d
|
@ -97,6 +97,8 @@ verilator-execute:
|
||||||
make clean && \
|
make clean && \
|
||||||
make test \
|
make test \
|
||||||
'
|
'
|
||||||
|
verilator-copy-waveform:
|
||||||
|
docker cp upsilon-verilator:/home/user/upsilon/gateware/rtl/waveform/waveform.fst ./
|
||||||
verilator-clean:
|
verilator-clean:
|
||||||
-docker container stop upsilon-verilator
|
-docker container stop upsilon-verilator
|
||||||
-docker container rm upsilon-verilator
|
-docker container rm upsilon-verilator
|
||||||
|
|
|
@ -29,9 +29,13 @@ mmio.py csr.json build/digilent_arty/digilent_arty.bit: soc.py
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build csr.json arty.dts arty.dtb mmio.py
|
rm -rf build csr.json arty.dts arty.dtb mmio.py
|
||||||
|
cd rtl && make clean
|
||||||
|
|
||||||
arty.dts: csr.json
|
arty.dts: csr.json
|
||||||
litex_json2dts_linux csr.json > arty.dts
|
litex_json2dts_linux csr.json > arty.dts
|
||||||
|
|
||||||
arty.dtb: arty.dts
|
arty.dtb: arty.dts
|
||||||
dtc -O dtb -o arty.dtb arty.dts
|
dtc -O dtb -o arty.dtb arty.dts
|
||||||
|
|
||||||
|
test:
|
||||||
|
cd rtl && make test
|
||||||
|
|
|
@ -8,6 +8,45 @@ from migen import *
|
||||||
from litex.soc.interconnect.wishbone import Interface
|
from litex.soc.interconnect.wishbone import Interface
|
||||||
|
|
||||||
from util import *
|
from util import *
|
||||||
|
from region import *
|
||||||
|
|
||||||
|
'''
|
||||||
|
class Waveform(LiteXModule):
|
||||||
|
""" Read from a memory location and output the values in that location
|
||||||
|
to a DAC. """
|
||||||
|
|
||||||
|
def __init__(self, max_size=16):
|
||||||
|
# Interface used by Waveform to read and write data
|
||||||
|
self.bus = Interface(data_width = 32, address_width = 32, addressing="byte")
|
||||||
|
|
||||||
|
# Interface used by Main CPU to control Waveform
|
||||||
|
self.mainbus = Interface(data_width = 32, address_width = 32, addressing="byte")
|
||||||
|
self.mmap = MemoryMap()
|
||||||
|
|
||||||
|
self.start = Signal()
|
||||||
|
self.cntr = Signal(max_size)
|
||||||
|
self.loop = Signal()
|
||||||
|
self.finished = Signal()
|
||||||
|
self.ready = Signal()
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
If(self.mainbus.cyc & self.mainbus.stb & ~self.mainbus.ack,
|
||||||
|
Cases(self.mainbus.adr[0:4], {
|
||||||
|
0x0: self.mainbus.dat_r.eq(self.finished << 1 | self.ready),
|
||||||
|
0x4: If(self.mainbus.we,
|
||||||
|
self.start.eq(self.mainbus.dat_w[0]),
|
||||||
|
self.loop.eq(self.mainbus.dat_w[1]),
|
||||||
|
).Else(
|
||||||
|
self.mainbus.dat_r.eq(self.start << 1 | self.loop),
|
||||||
|
),
|
||||||
|
0x8: self.mainbus.dat_r.eq(self.cntr)
|
||||||
|
}),
|
||||||
|
self.mainbus.ack.eq(1),
|
||||||
|
).Elif(~self.mainbus.cyc,
|
||||||
|
self.mainbus.ack.eq(0),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
'''
|
||||||
|
|
||||||
class SPIMaster(Module):
|
class SPIMaster(Module):
|
||||||
AD5791_PARAMS = {
|
AD5791_PARAMS = {
|
||||||
|
@ -28,29 +67,48 @@ class SPIMaster(Module):
|
||||||
"enable_mosi" : 0,
|
"enable_mosi" : 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
width = 0x10
|
width = 0x20
|
||||||
|
|
||||||
public_registers = {
|
public_registers = {
|
||||||
"finished_or_ready": {
|
# The first bit is the "finished" bit, when the master is
|
||||||
|
# armed and finished with a transmission.
|
||||||
|
# The second bit is the "ready" bit, when the master is
|
||||||
|
# not armed and ready to be armed.
|
||||||
|
"ready_or_finished": {
|
||||||
"origin" : 0,
|
"origin" : 0,
|
||||||
"width" : 4,
|
"width" : 4,
|
||||||
"rw": False,
|
"rw": False,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# One bit to initiate a transmission cycle. Transmission
|
||||||
|
# cycles CANNOT be interrupted.
|
||||||
"arm" : {
|
"arm" : {
|
||||||
"origin": 4,
|
"origin": 4,
|
||||||
"width": 4,
|
"width": 4,
|
||||||
"rw": True,
|
"rw": True,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# Data sent from the SPI slave.
|
||||||
"from_slave": {
|
"from_slave": {
|
||||||
"origin": 8,
|
"origin": 8,
|
||||||
"width": 4,
|
"width": 4,
|
||||||
"rw": False,
|
"rw": False,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# Data sent to the SPI slave.
|
||||||
"to_slave": {
|
"to_slave": {
|
||||||
"origin": 0xC,
|
"origin": 0xC,
|
||||||
"width": 4,
|
"width": 4,
|
||||||
"rw": True
|
"rw": True
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# Same as ready_or_finished, but halts until ready or finished
|
||||||
|
# goes high. Dangerous, might cause cores to hang!
|
||||||
|
"wait_ready_or_finished": {
|
||||||
|
"origin": 0x10,
|
||||||
|
"width": 4,
|
||||||
|
"rw" : False,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
""" Wrapper for the SPI master verilog code. """
|
""" Wrapper for the SPI master verilog code. """
|
||||||
|
@ -83,11 +141,60 @@ class SPIMaster(Module):
|
||||||
|
|
||||||
self.bus = Interface(data_width = 32, address_width=32, addressing="byte")
|
self.bus = Interface(data_width = 32, address_width=32, addressing="byte")
|
||||||
|
|
||||||
|
from_slave = Signal(spi_wid)
|
||||||
|
to_slave = Signal(spi_wid)
|
||||||
|
finished_or_ready = Signal(2)
|
||||||
|
arm = Signal()
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.bus.err.eq(0),
|
self.bus.err.eq(0),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.specials += Instance("spi_master_ss_wb",
|
self.sync += [
|
||||||
|
If(self.bus.cyc & self.bus.stb & ~self.bus.ack,
|
||||||
|
Cases(self.bus.adr[0:5], {
|
||||||
|
0x0: [
|
||||||
|
self.bus.dat_r[2:].eq(0),
|
||||||
|
self.bus.dat_r[0:2].eq(finished_or_ready),
|
||||||
|
self.bus.ack.eq(1),
|
||||||
|
],
|
||||||
|
0x4: [
|
||||||
|
If(self.bus.we,
|
||||||
|
arm.eq(self.bus.dat_w[0]),
|
||||||
|
).Else(
|
||||||
|
self.bus.dat_r[1:].eq(0),
|
||||||
|
self.bus.dat_r[0].eq(arm),
|
||||||
|
),
|
||||||
|
self.bus.ack.eq(1),
|
||||||
|
]
|
||||||
|
0x8: [
|
||||||
|
self.bus.dat_r[wid:].eq(0),
|
||||||
|
self.bus.dat_r[0:wid].eq(from_slave),
|
||||||
|
self.bus.ack.eq(1),
|
||||||
|
],
|
||||||
|
0xC: [
|
||||||
|
If(self.bus.we,
|
||||||
|
to_slave.eq(self.bus.dat_r[0:wid]),
|
||||||
|
).Else(
|
||||||
|
self.bus.dat_r[wid:].eq(0),
|
||||||
|
self.bus.dat_r[0:wid].eq(to_slave),
|
||||||
|
),
|
||||||
|
self.bus.ack.eq(1),
|
||||||
|
]
|
||||||
|
0x10: If(finished | ready_to_arm,
|
||||||
|
self.bus.dat_r[1:].eq(0),
|
||||||
|
self.bus.dat_r.eq(finished_or_ready),
|
||||||
|
),
|
||||||
|
"default":
|
||||||
|
# 0xSPI00SPI
|
||||||
|
self.bus.dat_r.eq(0x57100571),
|
||||||
|
}),
|
||||||
|
).Elif(~self.bus.clk,
|
||||||
|
self.bus.ack.eq(0)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
self.specials += Instance("spi_master_ss",
|
||||||
p_SS_WAIT = ss_wait,
|
p_SS_WAIT = ss_wait,
|
||||||
p_SS_WAIT_TIMER_LEN = minbits(ss_wait),
|
p_SS_WAIT_TIMER_LEN = minbits(ss_wait),
|
||||||
p_CYCLE_HALF_WAIT = spi_cycle_half_wait,
|
p_CYCLE_HALF_WAIT = spi_cycle_half_wait,
|
||||||
|
@ -106,12 +213,9 @@ class SPIMaster(Module):
|
||||||
o_sck_wire = sck,
|
o_sck_wire = sck,
|
||||||
o_ss_L = ss_L,
|
o_ss_L = ss_L,
|
||||||
|
|
||||||
i_wb_cyc = self.bus.cyc,
|
o_from_slave = from_slave,
|
||||||
i_wb_stb = self.bus.stb,
|
i_to_slave = to_slave,
|
||||||
i_wb_we = self.bus.we,
|
o_finished = finished_or_ready[1],
|
||||||
i_wb_sel = self.bus.sel,
|
o_ready_to_arm = finished_or_ready[0],
|
||||||
i_wb_addr = self.bus.adr,
|
i_arm = arm,
|
||||||
i_wb_dat_w = self.bus.dat_w,
|
|
||||||
o_wb_ack = self.bus.ack,
|
|
||||||
o_wb_dat_r = self.bus.dat_r,
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,6 +8,13 @@ all: make_spi
|
||||||
make_spi:
|
make_spi:
|
||||||
cd spi && make codegen
|
cd spi && make codegen
|
||||||
|
|
||||||
|
test_waveform:
|
||||||
|
cd waveform && make test
|
||||||
|
|
||||||
|
test: test_waveform
|
||||||
|
|
||||||
|
clean:
|
||||||
|
cd waveform && make clean
|
||||||
|
|
||||||
#make_bram:
|
#make_bram:
|
||||||
# cd bram && make codegen
|
# cd bram && make codegen
|
||||||
|
|
|
@ -37,6 +37,10 @@ module spi_master_ss_wb
|
||||||
input [BUS_WID-1:0] wb_dat_w,
|
input [BUS_WID-1:0] wb_dat_w,
|
||||||
output reg wb_ack,
|
output reg wb_ack,
|
||||||
output reg [BUS_WID-1:0] wb_dat_r,
|
output reg [BUS_WID-1:0] wb_dat_r,
|
||||||
|
|
||||||
|
/* Used in Migen code */
|
||||||
|
output finished,
|
||||||
|
output ready_to_arm,
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Address map:
|
/* Address map:
|
||||||
|
@ -52,8 +56,6 @@ module spi_master_ss_wb
|
||||||
|
|
||||||
wire [WID-1:0] from_slave;
|
wire [WID-1:0] from_slave;
|
||||||
reg [WID-1:0] to_slave;
|
reg [WID-1:0] to_slave;
|
||||||
wire finished;
|
|
||||||
wire ready_to_arm;
|
|
||||||
reg arm;
|
reg arm;
|
||||||
|
|
||||||
spi_master_ss #(
|
spi_master_ss #(
|
||||||
|
|
|
@ -24,6 +24,15 @@ template <class TOP> class TB {
|
||||||
mod.final();
|
mod.final();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is called at the positive edge of ever clock
|
||||||
|
* cycle.
|
||||||
|
*
|
||||||
|
* It's intended use is for glue code, like a bus handler.
|
||||||
|
*
|
||||||
|
* The bulk of the simulation code (driving external inputs into the
|
||||||
|
* simulated module and observing results) should be done outside of
|
||||||
|
* this function.
|
||||||
|
*/
|
||||||
virtual void posedge() {}
|
virtual void posedge() {}
|
||||||
|
|
||||||
void run_clock() {
|
void run_clock() {
|
||||||
|
|
|
@ -1,41 +1,22 @@
|
||||||
# Copyright 2023 (C) Peter McGoron
|
# Copyright 2024 (C) Peter McGoron
|
||||||
# This file is a part of Upsilon, a free and open source software project.
|
# This file is a part of Upsilon, a free and open source software project.
|
||||||
# For license terms, refer to the files in `doc/copying` in the Upsilon
|
# For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||||
# source distribution.
|
# source distribution.
|
||||||
|
|
||||||
# Makefile for tests and hardware verification.
|
# Makefile for tests and hardware verification.
|
||||||
|
|
||||||
include ../common.makefile
|
.PHONY: test clean codegen all
|
||||||
|
|
||||||
.PHONY: test clean codegen
|
CPP_FILE=waveform.cpp
|
||||||
|
VERILOG=waveform_sim.v waveform.v
|
||||||
all: test codegen
|
|
||||||
test: obj_dir/Vbram_interface_sim obj_dir/Vwaveform_sim
|
|
||||||
CODEGEN_FILES=bram_interface_preprocessed.v waveform_preprocessed.v
|
|
||||||
|
|
||||||
codegen: ${CODEGEN_FILES}
|
|
||||||
|
|
||||||
bram_SRC= bram_interface_sim.v dma_sim.v bram_interface.v bram_interface_sim.cpp
|
|
||||||
|
|
||||||
obj_dir/Vbram_interface_sim.mk: $(bram_SRC)
|
|
||||||
verilator --cc --exe -Wall --trace --trace-fst \
|
|
||||||
-CFLAGS -DWORD_AMNT=2048 \
|
|
||||||
-CFLAGS -DRAM_WID=32 \
|
|
||||||
$(bram_SRC)
|
|
||||||
obj_dir/Vbram_interface_sim: obj_dir/Vbram_interface_sim.mk
|
|
||||||
cd obj_dir && make -f Vbram_interface_sim.mk
|
|
||||||
./obj_dir/Vbram_interface_sim
|
|
||||||
|
|
||||||
waveform_src = waveform_sim.v waveform.v bram_interface.v dma_sim.v waveform_sim.cpp ../spi/spi_slave_no_write.v
|
|
||||||
obj_dir/Vwaveform_sim.mk: $(waveform_src)
|
|
||||||
verilator --cc --exe -Wall --trace --trace-fst -I../spi \
|
|
||||||
-CFLAGS -DWORD_AMNT=2048 \
|
|
||||||
-CFLAGS -DRAM_WID=32 \
|
|
||||||
-DVERILATOR_SIMULATION \
|
|
||||||
$(waveform_src)
|
|
||||||
obj_dir/Vwaveform_sim: obj_dir/Vwaveform_sim.mk $(waveform_src)
|
|
||||||
cd obj_dir && make -f Vwaveform_sim.mk
|
|
||||||
./obj_dir/Vwaveform_sim
|
|
||||||
|
|
||||||
|
test: obj_dir/Vwaveform_sim
|
||||||
|
obj_dir/Vwaveform_sim
|
||||||
clean:
|
clean:
|
||||||
rm -rf obj_dir/ ${CODEGEN_FILES}
|
rm -rf obj_dir
|
||||||
|
obj_dir/Vwaveform_sim.mk: ${VERILOG} ${CPP_FILE}
|
||||||
|
verilator --cc --exe -Wall --trace --trace-fst \
|
||||||
|
${VERILOG} ${CPP_FILE}
|
||||||
|
|
||||||
|
obj_dir/Vwaveform_sim: obj_dir/Vwaveform_sim.mk
|
||||||
|
cd obj_dir && make -f Vwaveform_sim.mk
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
||||||
* source distribution.
|
|
||||||
*/
|
|
||||||
#include "bram_dma.hpp"
|
|
||||||
#include "../util.hpp"
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
BRAM_DMA_Sim::BRAM_DMA_Sim(uint32_t _start_addr,
|
|
||||||
size_t _word_amnt,
|
|
||||||
size_t _timer_max) {
|
|
||||||
my_assert(_start_addr / 4 == 0, "start addr %d not 16 bit aligned",
|
|
||||||
_start_addr);
|
|
||||||
start_addr = _start_addr;
|
|
||||||
word_amnt = _word_amnt;
|
|
||||||
timer_max = _timer_max;
|
|
||||||
|
|
||||||
ram = new uint32_t[word_amnt];
|
|
||||||
}
|
|
||||||
|
|
||||||
BRAM_DMA_SIM::~BRAM_DMA_Sim() {
|
|
||||||
delete[] ram;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BRAM_DMA_Sim::generate_random_data() {
|
|
||||||
for (size_t i = 0; i < word_amnt; i++) {
|
|
||||||
ram[i] = mask_extend(rand(), 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BRAM_DMA_Sim::execute_ram_access(uint32_t ram_dma_addr,
|
|
||||||
uint32_t &ram_word,
|
|
||||||
uint32_t &ram_valid) {
|
|
||||||
ram_valid = 1;
|
|
||||||
my_assert(ram_dma_addr < start_addr
|
|
||||||
|| ram_dma_addr >= start_addr + word_amnt*4,
|
|
||||||
"bad address %x\n", ram_dma_addr);
|
|
||||||
my_assert(ram_dma_addr >= start_addr, "left oob access %x",
|
|
||||||
tb->mod.ram_dma_addr);
|
|
||||||
my_assert(ram_dma_addr < start_addr + WORD_AMNT*4,
|
|
||||||
"right oob access %x", ram_dma_addr);
|
|
||||||
my_assert(ram_dma_addr % 2 == 0, "unaligned access %x",
|
|
||||||
ram_dma_addr);
|
|
||||||
|
|
||||||
const auto addr = (ram_dma_addr - start_addr) / 4;
|
|
||||||
if (tb->mod.ram_dma_addr % 4 == 0) {
|
|
||||||
ram_word = ram_refresh_data[addr] & 0xFFFF;
|
|
||||||
} else {
|
|
||||||
ram_word = ram_refresh_data[addr] >> 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BRAM_DMA_Sim::posedge(uint32_t ram_dma_addr, uint32_t &ram_word,
|
|
||||||
uint32_t ram_read, uint32_t &ram_valid) {
|
|
||||||
if (ram_read && timer < timer_max) {
|
|
||||||
timer++;
|
|
||||||
if (timer == timer_max)
|
|
||||||
execute_ram_access(ram_dma_addr, ram_word, ram_valid);
|
|
||||||
} else {
|
|
||||||
ram_valid = 0;
|
|
||||||
timer = 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
||||||
* source distribution.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
template<size_t WORD_AMNT, size_t TIMER_MAX>
|
|
||||||
class BRAM_DMA_Sim {
|
|
||||||
uint32_t *ram;
|
|
||||||
|
|
||||||
uint32_t start_addr;
|
|
||||||
size_t word_amnt;
|
|
||||||
size_t timer_max;
|
|
||||||
|
|
||||||
int sim_timer;
|
|
||||||
|
|
||||||
void execute_ram_access(uint32_t ram_dma_addr, uint32_t &ram_word,
|
|
||||||
uint32_t &ram_valid);
|
|
||||||
public:
|
|
||||||
void generate_random_data();
|
|
||||||
BRAM_DMA(uint32_t _start_addr = 0x12340,
|
|
||||||
size_t _word_amnt = 2048,
|
|
||||||
size_t _timer_max = 10);
|
|
||||||
~BRAM_DMA();
|
|
||||||
void posedge(uint32_t ram_dma_addr, uint32_t &ram_word,
|
|
||||||
uint32_t ram_read, uint32_t &ram_valid);
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,126 +0,0 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
||||||
* source distribution.
|
|
||||||
*/
|
|
||||||
module bram_interface #(
|
|
||||||
parameter WORD_WID = 24,
|
|
||||||
parameter WORD_AMNT_WID = 11,
|
|
||||||
/* This is the last INDEX, not the LENGTH of the word array. */
|
|
||||||
parameter [WORD_AMNT_WID-1:0] WORD_AMNT = 2047,
|
|
||||||
parameter RAM_WID = 32,
|
|
||||||
parameter RAM_WORD_WID = 16,
|
|
||||||
parameter RAM_WORD_INCR = 2
|
|
||||||
) (
|
|
||||||
input clk,
|
|
||||||
input rst_L,
|
|
||||||
|
|
||||||
/* autoapproach interface */
|
|
||||||
output reg [WORD_WID-1:0] word,
|
|
||||||
input word_next,
|
|
||||||
output reg word_last,
|
|
||||||
output reg word_ok,
|
|
||||||
input word_rst,
|
|
||||||
|
|
||||||
/* User interface */
|
|
||||||
input refresh_start,
|
|
||||||
input [RAM_WID-1:0] start_addr,
|
|
||||||
output reg refresh_finished,
|
|
||||||
|
|
||||||
/* RAM interface */
|
|
||||||
output reg [RAM_WID-1:0] ram_dma_addr,
|
|
||||||
input [RAM_WORD_WID-1:0] ram_word,
|
|
||||||
output reg ram_read,
|
|
||||||
input ram_valid
|
|
||||||
);
|
|
||||||
|
|
||||||
initial word = 0;
|
|
||||||
initial word_last = 0;
|
|
||||||
initial word_ok = 0;
|
|
||||||
initial refresh_finished = 0;
|
|
||||||
initial ram_dma_addr = 0;
|
|
||||||
initial ram_read = 0;
|
|
||||||
|
|
||||||
/* TODO: how to initialize? */
|
|
||||||
reg [WORD_WID-1:0] backing_buffer [WORD_AMNT:0];
|
|
||||||
|
|
||||||
localparam WAIT_ON_REFRESH = 0;
|
|
||||||
localparam READ_LOW_WORD = 1;
|
|
||||||
localparam READ_HIGH_WORD = 2;
|
|
||||||
localparam WAIT_ON_REFRESH_DEASSERT = 3;
|
|
||||||
|
|
||||||
reg [1:0] refresh_state = WAIT_ON_REFRESH;
|
|
||||||
reg [WORD_AMNT_WID-1:0] word_cntr_refresh = 0;
|
|
||||||
|
|
||||||
always @ (posedge clk) if (!rst_L) begin
|
|
||||||
word <= 0;
|
|
||||||
word_last <= 0;
|
|
||||||
word_ok <= 0;
|
|
||||||
refresh_finished <= 0;
|
|
||||||
ram_dma_addr <= 0;
|
|
||||||
ram_read <= 0;
|
|
||||||
/* Do not reset backing buffer because that would take too long */
|
|
||||||
refresh_state <= WAIT_ON_REFRESH;
|
|
||||||
word_cntr_refresh <= 0;
|
|
||||||
end else case (refresh_state)
|
|
||||||
WAIT_ON_REFRESH: if (refresh_start) begin
|
|
||||||
ram_dma_addr <= start_addr;
|
|
||||||
refresh_state <= READ_LOW_WORD;
|
|
||||||
word_cntr_refresh <= 0;
|
|
||||||
end
|
|
||||||
READ_LOW_WORD: if (!ram_read) begin
|
|
||||||
ram_read <= 1;
|
|
||||||
end else if (ram_valid) begin
|
|
||||||
refresh_state <= READ_HIGH_WORD;
|
|
||||||
ram_dma_addr <= ram_dma_addr + RAM_WORD_INCR;
|
|
||||||
ram_read <= 0;
|
|
||||||
backing_buffer[word_cntr_refresh][RAM_WORD_WID-1:0] <= ram_word;
|
|
||||||
end
|
|
||||||
READ_HIGH_WORD: if (!ram_read) begin
|
|
||||||
ram_read <= 1;
|
|
||||||
end else if (ram_valid) begin
|
|
||||||
ram_dma_addr <= ram_dma_addr + RAM_WORD_INCR;
|
|
||||||
ram_read <= 0;
|
|
||||||
word_cntr_refresh <= word_cntr_refresh + 1;
|
|
||||||
backing_buffer[word_cntr_refresh][WORD_WID-1:RAM_WORD_WID] <= ram_word[WORD_WID-RAM_WORD_WID-1:0];
|
|
||||||
|
|
||||||
if (word_cntr_refresh == WORD_AMNT)
|
|
||||||
refresh_state <= WAIT_ON_REFRESH_DEASSERT;
|
|
||||||
else
|
|
||||||
refresh_state <= READ_LOW_WORD;
|
|
||||||
end
|
|
||||||
WAIT_ON_REFRESH_DEASSERT: begin
|
|
||||||
if (!refresh_start) begin
|
|
||||||
refresh_finished <= 0;
|
|
||||||
refresh_state <= WAIT_ON_REFRESH;
|
|
||||||
end else begin
|
|
||||||
refresh_finished <= 1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
endcase
|
|
||||||
|
|
||||||
reg [WORD_AMNT_WID-1:0] auto_cntr = 0;
|
|
||||||
|
|
||||||
always @ (posedge clk) if (word_rst || !rst_L) begin
|
|
||||||
auto_cntr <= 0;
|
|
||||||
word_ok <= 0;
|
|
||||||
word_last <= 0;
|
|
||||||
word <= 0;
|
|
||||||
end else if (word_next && !word_ok) begin
|
|
||||||
if (refresh_state == WAIT_ON_REFRESH) begin
|
|
||||||
word <= backing_buffer[auto_cntr];
|
|
||||||
word_ok <= 1;
|
|
||||||
if (auto_cntr == WORD_AMNT) begin
|
|
||||||
auto_cntr <= 0;
|
|
||||||
word_last <= 1;
|
|
||||||
end else begin
|
|
||||||
auto_cntr <= auto_cntr + 1;
|
|
||||||
word_last <= 0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end else if (!word_next && word_ok) begin
|
|
||||||
word_ok <= 0;
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
`undefineall
|
|
|
@ -1,112 +0,0 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
||||||
* source distribution.
|
|
||||||
*/
|
|
||||||
#include <limits>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <random>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <verilated.h>
|
|
||||||
|
|
||||||
#include "Vbram_interface_sim.h"
|
|
||||||
#include "../testbench.hpp"
|
|
||||||
|
|
||||||
std::array<uint32_t, WORD_AMNT> ram_refresh_data;
|
|
||||||
TB<Vbram_interface_sim> *tb;
|
|
||||||
|
|
||||||
static void handle_read_aa(size_t &i) {
|
|
||||||
if (tb->mod.word_ok) {
|
|
||||||
uint32_t val = sign_extend(tb->mod.word, 20);
|
|
||||||
tb->mod.word_next = 0;
|
|
||||||
|
|
||||||
my_assert(val == ram_refresh_data[i], "received value %x (%zu) != %x", i, val, ram_refresh_data[i]);
|
|
||||||
i++;
|
|
||||||
} else if (!tb->mod.word_next) {
|
|
||||||
tb->mod.word_next = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test reading the entire array twice. */
|
|
||||||
static void test_aa_read_1() {
|
|
||||||
size_t ind = 0;
|
|
||||||
|
|
||||||
tb->mod.word_next = 1;
|
|
||||||
tb->run_clock();
|
|
||||||
while (!tb->mod.word_last || (tb->mod.word_last && tb->mod.word_next)) {
|
|
||||||
handle_read_aa(ind);
|
|
||||||
tb->run_clock();
|
|
||||||
}
|
|
||||||
my_assert(ind == WORD_AMNT, "read value %zu != %d\n", ind, WORD_AMNT);
|
|
||||||
|
|
||||||
tb->mod.word_next = 1;
|
|
||||||
tb->run_clock();
|
|
||||||
ind = 0;
|
|
||||||
while (!tb->mod.word_last || (tb->mod.word_last && tb->mod.word_next)) {
|
|
||||||
handle_read_aa(ind);
|
|
||||||
tb->run_clock();
|
|
||||||
}
|
|
||||||
my_assert(ind == WORD_AMNT, "second read value %zu != %d\n", ind, WORD_AMNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_aa_read_interrupted() {
|
|
||||||
size_t ind = 0;
|
|
||||||
|
|
||||||
tb->mod.word_next = 1;
|
|
||||||
tb->run_clock();
|
|
||||||
for (int i = 0; i < 100; i++) {
|
|
||||||
handle_read_aa(ind);
|
|
||||||
tb->run_clock();
|
|
||||||
my_assert(!tb->mod.word_last, "too many reads");
|
|
||||||
}
|
|
||||||
tb->mod.word_rst = 1;
|
|
||||||
tb->run_clock();
|
|
||||||
tb->mod.word_rst = 0;
|
|
||||||
tb->run_clock();
|
|
||||||
|
|
||||||
test_aa_read_1();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void refresh_data() {
|
|
||||||
for (size_t i = 0; i < WORD_AMNT; i++) {
|
|
||||||
uint32_t val = mask_extend(rand(), 20);
|
|
||||||
ram_refresh_data[i] = val;
|
|
||||||
tb->mod.backing_store[i*2] = val & 0xFFFF;
|
|
||||||
tb->mod.backing_store[i*2+1] = val >> 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
tb->mod.refresh_start = 1;
|
|
||||||
tb->mod.start_addr = 0x12340;
|
|
||||||
tb->run_clock();
|
|
||||||
|
|
||||||
while (!tb->mod.refresh_finished)
|
|
||||||
tb->run_clock();
|
|
||||||
|
|
||||||
tb->mod.refresh_start = 0;
|
|
||||||
tb->run_clock();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
Verilated::commandArgs(argc, argv);
|
|
||||||
Verilated::traceEverOn(true);
|
|
||||||
Verilated::fatalOnError(false);
|
|
||||||
|
|
||||||
tb = new TB<Vbram_interface_sim>();
|
|
||||||
tb->mod.rst_L = 1;
|
|
||||||
|
|
||||||
printf("test basic read/write\n");
|
|
||||||
refresh_data();
|
|
||||||
printf("\ttest 1\n");
|
|
||||||
test_aa_read_1();
|
|
||||||
refresh_data();
|
|
||||||
printf("\ttest 2\n");
|
|
||||||
test_aa_read_1();
|
|
||||||
|
|
||||||
printf("test resetting\n");
|
|
||||||
test_aa_read_interrupted();
|
|
||||||
|
|
||||||
printf("ok\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
||||||
* source distribution.
|
|
||||||
*/
|
|
||||||
module bram_interface_sim #(
|
|
||||||
parameter WORD_WID = 20,
|
|
||||||
parameter WORD_AMNT_WID = 11,
|
|
||||||
parameter [WORD_AMNT_WID-1:0] WORD_AMNT = 2047,
|
|
||||||
parameter RAM_WID = 32,
|
|
||||||
parameter RAM_WORD_WID = 16,
|
|
||||||
parameter RAM_REAL_START = 32'h12340,
|
|
||||||
parameter RAM_CNTR_LEN = 12,
|
|
||||||
parameter TOTAL_RAM_WORD_MINUS_ONE = 4095,
|
|
||||||
parameter DELAY_CNTR_LEN = 8,
|
|
||||||
parameter DELAY_TOTAL = 12,
|
|
||||||
parameter RAM_WORD_INCR = 2
|
|
||||||
) (
|
|
||||||
input clk,
|
|
||||||
input rst_L,
|
|
||||||
|
|
||||||
/* autoapproach interface */
|
|
||||||
output [WORD_WID-1:0] word,
|
|
||||||
input word_next,
|
|
||||||
output word_last,
|
|
||||||
output word_ok,
|
|
||||||
input word_rst,
|
|
||||||
|
|
||||||
/* User interface */
|
|
||||||
input refresh_start,
|
|
||||||
input [RAM_WID-1:0] start_addr,
|
|
||||||
output refresh_finished,
|
|
||||||
|
|
||||||
input[RAM_WORD_WID-1:0] backing_store [TOTAL_RAM_WORD_MINUS_ONE:0]
|
|
||||||
);
|
|
||||||
|
|
||||||
wire [RAM_WID-1:0] ram_dma_addr;
|
|
||||||
wire [RAM_WORD_WID-1:0] ram_word;
|
|
||||||
wire ram_read;
|
|
||||||
wire ram_valid;
|
|
||||||
|
|
||||||
dma_sim #(
|
|
||||||
.RAM_WID(RAM_WID),
|
|
||||||
.RAM_WORD_WID(RAM_WORD_WID),
|
|
||||||
.RAM_REAL_START(RAM_REAL_START),
|
|
||||||
.RAM_CNTR_LEN(RAM_CNTR_LEN),
|
|
||||||
.TOTAL_RAM_WORD_MINUS_ONE(TOTAL_RAM_WORD_MINUS_ONE),
|
|
||||||
.DELAY_CNTR_LEN(DELAY_CNTR_LEN),
|
|
||||||
.DELAY_TOTAL(DELAY_TOTAL)
|
|
||||||
) dma_sim (
|
|
||||||
.clk(clk),
|
|
||||||
.ram_dma_addr(ram_dma_addr),
|
|
||||||
.ram_word(ram_word),
|
|
||||||
.ram_read(ram_read),
|
|
||||||
.ram_valid(ram_valid),
|
|
||||||
.backing_store(backing_store)
|
|
||||||
);
|
|
||||||
|
|
||||||
bram_interface #(
|
|
||||||
.WORD_WID(WORD_WID),
|
|
||||||
.WORD_AMNT_WID(WORD_AMNT_WID),
|
|
||||||
.WORD_AMNT(WORD_AMNT),
|
|
||||||
.RAM_WID(RAM_WID),
|
|
||||||
.RAM_WORD_WID(RAM_WORD_WID),
|
|
||||||
.RAM_WORD_INCR(RAM_WORD_INCR)
|
|
||||||
) bram_interface (
|
|
||||||
.clk(clk),
|
|
||||||
.rst_L(rst_L),
|
|
||||||
.word(word),
|
|
||||||
.word_next(word_next),
|
|
||||||
.word_last(word_last),
|
|
||||||
.word_ok(word_ok),
|
|
||||||
.word_rst(word_rst),
|
|
||||||
.refresh_start(refresh_start),
|
|
||||||
.start_addr(start_addr),
|
|
||||||
.refresh_finished(refresh_finished),
|
|
||||||
.ram_dma_addr(ram_dma_addr),
|
|
||||||
.ram_word(ram_word),
|
|
||||||
.ram_read(ram_read),
|
|
||||||
.ram_valid(ram_valid)
|
|
||||||
);
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
$dumpfile("bram.fst");
|
|
||||||
$dumpvars();
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
|
@ -1,50 +0,0 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
||||||
* source distribution.
|
|
||||||
*/
|
|
||||||
/* This module is used to simulate direct memory access, where only
|
|
||||||
* a small amount of memory is valid to read.
|
|
||||||
*/
|
|
||||||
module dma_sim #(
|
|
||||||
parameter RAM_WID = 32,
|
|
||||||
parameter RAM_WORD_WID = 16,
|
|
||||||
parameter RAM_REAL_START = 32'h12340,
|
|
||||||
parameter RAM_CNTR_LEN = 12,
|
|
||||||
parameter TOTAL_RAM_WORD_MINUS_ONE = 4095,
|
|
||||||
parameter DELAY_CNTR_LEN = 8,
|
|
||||||
parameter DELAY_TOTAL = 12
|
|
||||||
) (
|
|
||||||
input clk,
|
|
||||||
|
|
||||||
/* DMA interface */
|
|
||||||
input [RAM_WID-1:0] ram_dma_addr,
|
|
||||||
output reg [RAM_WORD_WID-1:0] ram_word,
|
|
||||||
input ram_read,
|
|
||||||
output reg ram_valid,
|
|
||||||
|
|
||||||
/*- Verilator interface */
|
|
||||||
input [RAM_WORD_WID-1:0] backing_store[TOTAL_RAM_WORD_MINUS_ONE:0]
|
|
||||||
);
|
|
||||||
|
|
||||||
reg [DELAY_CNTR_LEN-1:0] delay_cntr = 0;
|
|
||||||
|
|
||||||
always @ (posedge clk) begin
|
|
||||||
if (!ram_read) begin
|
|
||||||
delay_cntr <= 0;
|
|
||||||
ram_valid <= 0;
|
|
||||||
end else if (delay_cntr < DELAY_TOTAL) begin
|
|
||||||
delay_cntr <= delay_cntr + 1;
|
|
||||||
end else if (!ram_valid) begin
|
|
||||||
if (ram_dma_addr < RAM_REAL_START || ram_dma_addr > RAM_REAL_START + 2*TOTAL_RAM_WORD_MINUS_ONE) begin
|
|
||||||
$display("ram_dma_addr %x out of bounds", ram_dma_addr);
|
|
||||||
$stop();
|
|
||||||
end else begin
|
|
||||||
ram_word <= backing_store[(RAM_CNTR_LEN)'((ram_dma_addr - RAM_REAL_START)/2)];
|
|
||||||
ram_valid <= 1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
`undefineall
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
#include <vector>
|
||||||
|
#include <random>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include "Vwaveform_sim.h"
|
||||||
|
#include "../testbench.hpp"
|
||||||
|
|
||||||
|
class WaveformTestbench : public TB<Vwaveform_sim> {
|
||||||
|
public:
|
||||||
|
void start_test(int, int, int, bool);
|
||||||
|
void halt_check(bool);
|
||||||
|
WaveformTestbench(int _bailout) : TB<Vwaveform_sim>(_bailout) {}
|
||||||
|
private:
|
||||||
|
std::vector<uint32_t> send_words;
|
||||||
|
std::vector<uint32_t> recv_words;
|
||||||
|
void fill_data(int num);
|
||||||
|
void check_data();
|
||||||
|
void posedge() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
void WaveformTestbench::posedge() {
|
||||||
|
if (!mod.ram_finished) {
|
||||||
|
if ((mod.enable & 0b10) != 0) {
|
||||||
|
recv_words.push_back(mod.spi_data);
|
||||||
|
mod.ram_finished = 1;
|
||||||
|
} else if ((mod.enable & 0b01) != 0) {
|
||||||
|
mod.ram_data = send_words.at(mod.offset);
|
||||||
|
mod.ram_finished = 1;
|
||||||
|
}
|
||||||
|
} else if ((mod.enable & 0b11) == 0) {
|
||||||
|
mod.ram_finished = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveformTestbench::fill_data(int num) {
|
||||||
|
auto engine = std::default_random_engine{};
|
||||||
|
auto distrib = std::uniform_int_distribution<uint32_t>(0,(1 << 20) - 1);
|
||||||
|
|
||||||
|
send_words.clear();
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
send_words.push_back(distrib(engine));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveformTestbench::check_data() {
|
||||||
|
auto len = send_words.size();
|
||||||
|
auto recv_size = recv_words.size();
|
||||||
|
|
||||||
|
for (decltype(len) i = 0; i < recv_size; i++) {
|
||||||
|
/* SPI message has an extra bit to access DAC register */
|
||||||
|
auto was_sent = 1 << 20 | send_words.at(i % len);
|
||||||
|
if (was_sent != recv_words.at(i % len)) {
|
||||||
|
std::cout << i << ":" << was_sent << "!=" << recv_words[i % len] << std::endl;
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveformTestbench::start_test(int wform_size, int spi_max_wait, int timer_spacing, bool do_loop) {
|
||||||
|
fill_data(wform_size);
|
||||||
|
|
||||||
|
mod.run = 1;
|
||||||
|
mod.wform_size = wform_size;
|
||||||
|
mod.spi_max_wait = spi_max_wait;
|
||||||
|
mod.timer_spacing = timer_spacing;
|
||||||
|
mod.do_loop = do_loop;
|
||||||
|
run_clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveformTestbench::halt_check(bool check_for_finish) {
|
||||||
|
if (check_for_finish) {
|
||||||
|
while (!mod.finished) {
|
||||||
|
run_clock();
|
||||||
|
}
|
||||||
|
mod.run = 0;
|
||||||
|
run_clock();
|
||||||
|
} else {
|
||||||
|
mod.run = 0;
|
||||||
|
run_clock();
|
||||||
|
while (!mod.ready) {
|
||||||
|
run_clock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
WaveformTestbench *tb;
|
||||||
|
|
||||||
|
void cleanup() {
|
||||||
|
delete tb;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
Verilated::commandArgs(argc, argv);
|
||||||
|
Verilated::traceEverOn(true);
|
||||||
|
tb = new WaveformTestbench(100000);
|
||||||
|
atexit(cleanup);
|
||||||
|
|
||||||
|
tb->start_test(64, 14, 20, false);
|
||||||
|
tb->halt_check(true);
|
||||||
|
|
||||||
|
tb->start_test(64, 14, 20, true);
|
||||||
|
tb->halt_check(false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,208 +1,184 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
/* Copyright 2024 (C) Peter McGoron
|
||||||
|
*
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
* This file is a part of Upsilon, a free and open source software project.
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||||
* source distribution.
|
* source distribution.
|
||||||
*/
|
*/
|
||||||
/* Write a waveform to a DAC. */
|
|
||||||
/* TODO: Add "how many values to go" counter. */
|
|
||||||
module waveform #(
|
module waveform #(
|
||||||
parameter DAC_WID = 24,
|
parameter RAM_START_ADDR = 32'h0,
|
||||||
parameter DAC_WID_SIZ = 5,
|
parameter SPI_START_ADDR = 32'h10000000,
|
||||||
parameter DAC_POLARITY = 0,
|
parameter COUNTER_MAX_WID = 16,
|
||||||
parameter DAC_PHASE = 1,
|
parameter TIMER_WID = 16
|
||||||
parameter DAC_CYCLE_HALF_WAIT = 10,
|
|
||||||
parameter DAC_CYCLE_HALF_WAIT_SIZ = 4,
|
|
||||||
parameter DAC_SS_WAIT = 5,
|
|
||||||
parameter DAC_SS_WAIT_SIZ = 3,
|
|
||||||
parameter TIMER_WID = 32,
|
|
||||||
parameter WORD_WID = 20,
|
|
||||||
parameter WORD_AMNT_WID = 11,
|
|
||||||
parameter [WORD_AMNT_WID-1:0] WORD_AMNT = 2047,
|
|
||||||
parameter RAM_WID = 32,
|
|
||||||
parameter RAM_WORD_WID = 16,
|
|
||||||
parameter RAM_WORD_INCR = 2
|
|
||||||
) (
|
) (
|
||||||
input clk,
|
input clk,
|
||||||
input rst_L,
|
|
||||||
input arm,
|
/* Waveform output control */
|
||||||
input halt_on_finish,
|
input run,
|
||||||
/* NOTE:
|
output reg[COUNTER_MAX_WID-1:0] cntr,
|
||||||
* finished is used when a module wants to wait for a
|
input do_loop,
|
||||||
* waveform with the halt_on_finish flag finishes
|
|
||||||
* one waveform.
|
|
||||||
*
|
|
||||||
* running is used when a module wants to know when
|
|
||||||
* the waveform module has finished running after
|
|
||||||
* deasserting arm.
|
|
||||||
*
|
|
||||||
* When in doubt, deassert arm and wait for running
|
|
||||||
* to be deasserted.
|
|
||||||
*/
|
|
||||||
output reg finished,
|
output reg finished,
|
||||||
output running,
|
output reg ready,
|
||||||
input [TIMER_WID-1:0] time_to_wait,
|
input [COUNTER_MAX_WID-1:0] wform_size,
|
||||||
|
output reg [TIMER_WID-1:0] timer,
|
||||||
|
input [TIMER_WID-1:0] timer_spacing,
|
||||||
|
|
||||||
/* User interface */
|
/* Bus master */
|
||||||
input refresh_start,
|
output reg [32-1:0] wb_adr,
|
||||||
input [RAM_WID-1:0] start_addr,
|
output reg wb_cyc,
|
||||||
output reg refresh_finished,
|
output reg wb_we,
|
||||||
|
output wb_stb,
|
||||||
/* RAM interface */
|
output [4-1:0] wb_sel,
|
||||||
output reg [RAM_WID-1:0] ram_dma_addr,
|
output reg [32-1:0] wb_dat_w,
|
||||||
input [RAM_WORD_WID-1:0] ram_word,
|
input [32-1:0] wb_dat_r,
|
||||||
output reg ram_read,
|
input wb_ack
|
||||||
input ram_valid,
|
|
||||||
|
|
||||||
/* DAC wires. */
|
|
||||||
output mosi,
|
|
||||||
output sck,
|
|
||||||
output ss_L
|
|
||||||
);
|
);
|
||||||
|
|
||||||
wire [WORD_WID-1:0] word;
|
/* When a Wishbone cycle starts, the output is stable. */
|
||||||
reg word_next = 0;
|
assign wb_stb = wb_cyc;
|
||||||
wire word_ok;
|
|
||||||
wire word_last;
|
|
||||||
reg word_rst = 1;
|
|
||||||
|
|
||||||
bram_interface #(
|
/* Always write 32 bits */
|
||||||
.WORD_WID(WORD_WID),
|
assign wb_sel = 4'b1111;
|
||||||
.WORD_AMNT_WID(WORD_AMNT_WID),
|
|
||||||
.WORD_AMNT(WORD_AMNT),
|
|
||||||
.RAM_WID(RAM_WID),
|
|
||||||
.RAM_WORD_WID(RAM_WORD_WID),
|
|
||||||
.RAM_WORD_INCR(RAM_WORD_INCR)
|
|
||||||
) bram (
|
|
||||||
.clk(clk),
|
|
||||||
.rst_L(rst_L),
|
|
||||||
.word(word),
|
|
||||||
.word_next(word_next),
|
|
||||||
.word_last(word_last),
|
|
||||||
.word_ok(word_ok),
|
|
||||||
.word_rst(word_rst),
|
|
||||||
|
|
||||||
.refresh_start(refresh_start),
|
localparam CHECK_START = 0;
|
||||||
.start_addr(start_addr),
|
localparam CHECK_LEN = 1;
|
||||||
.refresh_finished(refresh_finished),
|
localparam WAIT_FINISHED = 2;
|
||||||
|
localparam READ_RAM = 3;
|
||||||
|
localparam WAIT_RAM = 4;
|
||||||
|
localparam WRITE_DAC_DATA_ADR = 5;
|
||||||
|
localparam WAIT_DAC_DATA_ADR = 6;
|
||||||
|
localparam WRITE_DAC_ARM_ADR = 7;
|
||||||
|
localparam WAIT_DAC_ARM_ADR = 8;
|
||||||
|
localparam WRITE_DAC_DISARM_ADR = 9;
|
||||||
|
localparam WAIT_DAC_DISARM_ADR = 10;
|
||||||
|
localparam READ_DAC_FIN_ADR = 11;
|
||||||
|
localparam WAIT_DAC_FIN_ADR = 12;
|
||||||
|
localparam WAIT_PERIOD = 13;
|
||||||
|
|
||||||
.ram_dma_addr(ram_dma_addr),
|
reg [4-1:0] state = CHECK_START;
|
||||||
.ram_word(ram_word),
|
|
||||||
.ram_read(ram_read),
|
|
||||||
.ram_valid(ram_valid)
|
|
||||||
);
|
|
||||||
|
|
||||||
wire dac_finished;
|
always @ (posedge clk) case (state)
|
||||||
reg dac_arm = 0;
|
CHECK_START: if (run) begin
|
||||||
reg [DAC_WID-1:0] dac_out = 0;
|
cntr <= 0;
|
||||||
wire dac_ready_to_arm_unused;
|
ready <= 0;
|
||||||
|
state <= CHECK_LEN;
|
||||||
spi_master_ss_no_read #(
|
|
||||||
.WID(DAC_WID),
|
|
||||||
.WID_LEN(DAC_WID_SIZ),
|
|
||||||
.CYCLE_HALF_WAIT(DAC_CYCLE_HALF_WAIT),
|
|
||||||
.TIMER_LEN(DAC_CYCLE_HALF_WAIT_SIZ),
|
|
||||||
.POLARITY(DAC_POLARITY),
|
|
||||||
.PHASE(DAC_PHASE),
|
|
||||||
.SS_WAIT(DAC_SS_WAIT),
|
|
||||||
.SS_WAIT_TIMER_LEN(DAC_SS_WAIT_SIZ)
|
|
||||||
) dac_master (
|
|
||||||
.clk(clk),
|
|
||||||
.rst_L(rst_L),
|
|
||||||
.ready_to_arm(dac_ready_to_arm_unused),
|
|
||||||
.mosi(mosi),
|
|
||||||
.sck_wire(sck),
|
|
||||||
.ss_L(ss_L),
|
|
||||||
.finished(dac_finished),
|
|
||||||
.arm(dac_arm),
|
|
||||||
.to_slave(dac_out)
|
|
||||||
);
|
|
||||||
|
|
||||||
localparam WAIT_ON_ARM = 0;
|
|
||||||
localparam DO_WAIT = 1;
|
|
||||||
localparam RECV_WORD = 2;
|
|
||||||
localparam WAIT_ON_DAC = 3;
|
|
||||||
localparam WAIT_ON_DISARM = 4;
|
|
||||||
reg [2:0] state = WAIT_ON_ARM;
|
|
||||||
|
|
||||||
reg [TIMER_WID-1:0] wait_timer = 0;
|
|
||||||
|
|
||||||
assign running = state != WAIT_ON_ARM;
|
|
||||||
|
|
||||||
always @ (posedge clk) if (!rst_L) begin
|
|
||||||
state <= WAIT_ON_ARM;
|
|
||||||
wait_timer <= 0;
|
|
||||||
finished <= 0;
|
|
||||||
word_rst <= 1;
|
|
||||||
word_next <= 0;
|
|
||||||
dac_out <= 0;
|
|
||||||
dac_arm <= 0;
|
|
||||||
end else case (state)
|
|
||||||
WAIT_ON_ARM: begin
|
|
||||||
finished <= 0;
|
|
||||||
if (arm) begin
|
|
||||||
state <= DO_WAIT;
|
|
||||||
word_rst <= 0;
|
|
||||||
wait_timer <= time_to_wait;
|
|
||||||
end else begin
|
end else begin
|
||||||
word_rst <= 1;
|
ready <= 1;
|
||||||
end
|
end
|
||||||
end
|
CHECK_LEN: if (cntr >= wform_size) begin
|
||||||
DO_WAIT: if (!arm) begin
|
if (do_loop) begin
|
||||||
state <= WAIT_ON_ARM;
|
cntr <= 0;
|
||||||
end else if (wait_timer == 0) begin
|
state <= READ_RAM;
|
||||||
word_next <= 1;
|
|
||||||
state <= RECV_WORD;
|
|
||||||
wait_timer <= time_to_wait;
|
|
||||||
end else begin
|
end else begin
|
||||||
wait_timer <= wait_timer - 1;
|
state <= WAIT_FINISHED;
|
||||||
end
|
end
|
||||||
RECV_WORD: begin
|
end else begin
|
||||||
`ifdef VERILATOR_SIMULATION
|
state <= READ_RAM;
|
||||||
if (!word_next) begin
|
|
||||||
$error("RECV_WORD: word_next not asserted means hang");
|
|
||||||
end
|
end
|
||||||
`endif
|
WAIT_FINISHED: if (!run) begin
|
||||||
|
finished <= 0;
|
||||||
if (word_ok) begin
|
state <= CHECK_START;
|
||||||
dac_out <= {4'b0001, word};
|
end else if (do_loop) begin
|
||||||
dac_arm <= 1;
|
state <= READ_RAM;
|
||||||
|
finished <= 0;
|
||||||
word_next <= 0;
|
cntr <= 0;
|
||||||
state <= WAIT_ON_DAC;
|
end else begin
|
||||||
end
|
|
||||||
end
|
|
||||||
WAIT_ON_DAC: begin
|
|
||||||
`ifdef VERILATOR_SIMULATION
|
|
||||||
if (!dac_arm) begin
|
|
||||||
$error("WAIT_ON_DAC: dac_arm not asserted means hang");
|
|
||||||
end
|
|
||||||
`endif
|
|
||||||
|
|
||||||
if (dac_finished) begin
|
|
||||||
dac_arm <= 0;
|
|
||||||
/* Was the last word read *the* last word? */
|
|
||||||
if (word_last && halt_on_finish) begin
|
|
||||||
state <= WAIT_ON_DISARM;
|
|
||||||
finished <= 1;
|
finished <= 1;
|
||||||
end else begin
|
|
||||||
state <= DO_WAIT;
|
|
||||||
wait_timer <= time_to_wait;
|
|
||||||
end
|
end
|
||||||
|
READ_RAM: begin
|
||||||
|
wb_adr <= RAM_START_ADDR + {16'b0, cntr};
|
||||||
|
wb_cyc <= 1; /* Always assigned STB when CYC is */
|
||||||
|
wb_we <= 0;
|
||||||
|
state <= WAIT_RAM;
|
||||||
end
|
end
|
||||||
|
WAIT_RAM: if (wb_ack) begin
|
||||||
|
wb_cyc <= 0;
|
||||||
|
wb_dat_w <= 1 << 20 | wb_dat_r;
|
||||||
|
state <= WRITE_DAC_DATA_ADR;
|
||||||
end
|
end
|
||||||
WAIT_ON_DISARM: if (!arm) begin
|
WRITE_DAC_DATA_ADR: begin
|
||||||
state <= WAIT_ON_ARM;
|
wb_adr <= SPI_START_ADDR + 32'hC;
|
||||||
|
wb_cyc <= 1;
|
||||||
|
wb_we <= 1;
|
||||||
|
state <= WAIT_DAC_DATA_ADR;
|
||||||
end
|
end
|
||||||
endcase
|
WAIT_DAC_DATA_ADR: if (wb_ack) begin
|
||||||
|
wb_cyc <= 0;
|
||||||
/* Warning! This will crash verilator with a segmentation fault!
|
/* This is not needed, since the next bus cycle is also a write. */
|
||||||
`ifdef VERILATOR
|
/* wb_we <= 0; */
|
||||||
initial begin
|
state <= WRITE_DAC_ARM_ADR;
|
||||||
$dumpfile("waveform.fst");
|
|
||||||
$dumpvars();
|
|
||||||
end
|
end
|
||||||
`endif
|
WRITE_DAC_ARM_ADR: begin
|
||||||
|
wb_adr <= SPI_START_ADDR + 32'h4;
|
||||||
|
wb_dat_w[0] <= 1;
|
||||||
|
wb_cyc <= 1;
|
||||||
|
/* wb_we <= 1; */
|
||||||
|
state <= WAIT_DAC_ARM_ADR;
|
||||||
|
end
|
||||||
|
WAIT_DAC_ARM_ADR: if (wb_ack) begin
|
||||||
|
wb_cyc <= 0;
|
||||||
|
/* This is not needed, since the next bus cycle is also a write. */
|
||||||
|
/* wb_we <= 0; */
|
||||||
|
state <= WRITE_DAC_DISARM_ADR;
|
||||||
|
end
|
||||||
|
/*
|
||||||
|
* After arming the SPI core, immediately disarm it.
|
||||||
|
* The SPI core will continue to execute after being disarmed until
|
||||||
|
* it completes a transmission cycle. Otherwise the core would spend
|
||||||
|
* two extra clock cycles if it disarmed after checking that the SPI
|
||||||
|
* master was done.
|
||||||
*/
|
*/
|
||||||
|
WRITE_DAC_DISARM_ADR: begin
|
||||||
|
wb_adr <= SPI_START_ADDR + 32'h4;
|
||||||
|
wb_dat_w[0] <= 0;
|
||||||
|
wb_cyc <= 1;
|
||||||
|
/* This is not needed, since the previous bus cycle is also a write. */
|
||||||
|
/* wb_we <= 1; */
|
||||||
|
state <= WAIT_DAC_DISARM_ADR;
|
||||||
|
end
|
||||||
|
WAIT_DAC_DISARM_ADR: if (wb_ack) begin
|
||||||
|
wb_cyc <= 0;
|
||||||
|
/* Disable writes because next bus cycle is a write */
|
||||||
|
wb_we <= 0;
|
||||||
|
state <= READ_DAC_FIN_ADR;
|
||||||
|
end
|
||||||
|
/*
|
||||||
|
* This core reads from "wait_ready_or_finished", which will
|
||||||
|
* stall until the SPI core is ready to arm or finished with
|
||||||
|
* it's transmission.
|
||||||
|
* If the SPI device is disconnected it will just return 0 at all
|
||||||
|
* times (see PreemptiveInterface). To avoid getting the core stuck
|
||||||
|
* the FSM will continue without checking that the DAC is actually
|
||||||
|
* ready or finished.
|
||||||
|
*/
|
||||||
|
READ_DAC_FIN_ADR: begin
|
||||||
|
wb_adr <= SPI_START_ADDR + 32'h10;
|
||||||
|
wb_cyc <= 1;
|
||||||
|
state <= WAIT_DAC_FIN_ADR;
|
||||||
|
end
|
||||||
|
WAIT_DAC_FIN_ADR: if (wb_cyc) begin
|
||||||
|
wb_cyc <= 0;
|
||||||
|
state <= WAIT_PERIOD;
|
||||||
|
timer <= 0;
|
||||||
|
end
|
||||||
|
/* If the core tells the block to stop running, stop when SPI is not
|
||||||
|
* transmitting to the DAC.
|
||||||
|
*
|
||||||
|
* If you want the module to run until the end of the waveform, turn
|
||||||
|
* off looping and wait until the waveform ends. If you want the module
|
||||||
|
* to stop the waveform and keep the DAC in a known state, just turn
|
||||||
|
* the running flag off. If you need to stop it immediately, flip the
|
||||||
|
* master switch for the DAC to the main CPU.
|
||||||
|
*/
|
||||||
|
WAIT_PERIOD: if (!run) begin
|
||||||
|
finished <= 0;
|
||||||
|
state <= CHECK_START;
|
||||||
|
end else if (timer < timer_spacing) begin
|
||||||
|
timer <= timer + 1;
|
||||||
|
end else begin
|
||||||
|
cntr <= cntr + 1;
|
||||||
|
state <= CHECK_LEN;
|
||||||
|
end
|
||||||
|
default: state <= CHECK_START;
|
||||||
|
endcase
|
||||||
endmodule
|
endmodule
|
||||||
`undefineall
|
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
||||||
* source distribution.
|
|
||||||
*/
|
|
||||||
/* TODO: impleement reset for dma and test both separetely */
|
|
||||||
#include <vector>
|
|
||||||
#include "Vwaveform_sim.h"
|
|
||||||
#include "../testbench.hpp"
|
|
||||||
|
|
||||||
class WaveformTestbench : public TB<Vwaveform_sim> {
|
|
||||||
private:
|
|
||||||
void refresh_posedge();
|
|
||||||
void spi_posedge();
|
|
||||||
public:
|
|
||||||
std::array<uint32_t, WORD_AMNT> ram_refresh_data;
|
|
||||||
int cur_ind;
|
|
||||||
void posedge() override;
|
|
||||||
void refresh_data();
|
|
||||||
WaveformTestbench(int _bailout = 0) : TB<Vwaveform_sim>(_bailout)
|
|
||||||
, ram_refresh_data{}
|
|
||||||
, cur_ind{0} {}
|
|
||||||
};
|
|
||||||
|
|
||||||
void WaveformTestbench::refresh_data() {
|
|
||||||
for (size_t i = 0; i < WORD_AMNT; i++) {
|
|
||||||
uint32_t val = mask_extend(rand(), 20);
|
|
||||||
ram_refresh_data[i] = val;
|
|
||||||
mod.backing_store[i*2] = val & 0xFFFF;
|
|
||||||
mod.backing_store[i*2+1] = val >> 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
mod.refresh_start = 1;
|
|
||||||
mod.start_addr = 0x12340;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaveformTestbench::refresh_posedge() {
|
|
||||||
if (mod.refresh_finished) {
|
|
||||||
mod.refresh_start = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaveformTestbench::spi_posedge() {
|
|
||||||
if (mod.finished) {
|
|
||||||
mod.rdy = 0;
|
|
||||||
// Check for proper DAC register.
|
|
||||||
my_assert(mod.from_master >> 20 == 0x1, "%d", mod.from_master >> 20);
|
|
||||||
uint32_t val = mask_extend(mod.from_master & 0xFFFFF, 20);
|
|
||||||
my_assert(val == ram_refresh_data[cur_ind], "(%d) %X != %X",
|
|
||||||
cur_ind, val, ram_refresh_data[cur_ind]);
|
|
||||||
cur_ind++;
|
|
||||||
if (cur_ind == WORD_AMNT)
|
|
||||||
cur_ind = 0;
|
|
||||||
} else if (!mod.finished && !mod.rdy) {
|
|
||||||
mod.rdy = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaveformTestbench::posedge() {
|
|
||||||
refresh_posedge();
|
|
||||||
spi_posedge();
|
|
||||||
}
|
|
||||||
|
|
||||||
WaveformTestbench *tb;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
int j = 0;
|
|
||||||
Verilated::commandArgs(argc, argv);
|
|
||||||
// Verilated::traceEverOn(true);
|
|
||||||
Verilated::fatalOnError(false);
|
|
||||||
|
|
||||||
tb = new WaveformTestbench();
|
|
||||||
tb->mod.rdy = 1;
|
|
||||||
tb->mod.rst_L = 1;
|
|
||||||
tb->refresh_data();
|
|
||||||
tb->mod.time_to_wait = 10;
|
|
||||||
tb->mod.halt_on_finish = 1;
|
|
||||||
tb->mod.arm = 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
tb->run_clock();
|
|
||||||
} while (!tb->mod.refresh_finished);
|
|
||||||
|
|
||||||
printf("first run\n");
|
|
||||||
do {
|
|
||||||
tb->run_clock();
|
|
||||||
} while (!tb->mod.waveform_finished);
|
|
||||||
printf("waveform finished\n");
|
|
||||||
|
|
||||||
tb->mod.halt_on_finish = 0;
|
|
||||||
tb->mod.arm = 0;
|
|
||||||
tb->run_clock();
|
|
||||||
tb->mod.arm = 1;
|
|
||||||
tb->mod.halt_on_finish = 1;
|
|
||||||
|
|
||||||
printf("second run\n");
|
|
||||||
do {
|
|
||||||
tb->run_clock();
|
|
||||||
} while (!tb->mod.waveform_finished);
|
|
||||||
|
|
||||||
tb->mod.rdy = 0;
|
|
||||||
tb->mod.halt_on_finish = 0;
|
|
||||||
tb->mod.refresh_start = 1;
|
|
||||||
do {
|
|
||||||
tb->run_clock();
|
|
||||||
} while (!tb->mod.refresh_finished);
|
|
||||||
|
|
||||||
tb->mod.rdy = 1;
|
|
||||||
tb->mod.halt_on_finish = 1;
|
|
||||||
|
|
||||||
printf("third run\n");
|
|
||||||
do {
|
|
||||||
tb->run_clock();
|
|
||||||
} while (!tb->mod.waveform_finished);
|
|
||||||
|
|
||||||
delete tb;
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,132 +1,152 @@
|
||||||
/* Copyright 2023 (C) Peter McGoron
|
/* Copyright 2024 (C) Peter McGoron
|
||||||
|
*
|
||||||
* This file is a part of Upsilon, a free and open source software project.
|
* This file is a part of Upsilon, a free and open source software project.
|
||||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||||
* source distribution.
|
* source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module waveform_sim #(
|
module waveform_sim #(
|
||||||
parameter DAC_WID = 24,
|
parameter RAM_START_ADDR = 32'h0,
|
||||||
parameter DAC_WID_SIZ = 5,
|
parameter SPI_START_ADDR = 32'h10000000,
|
||||||
parameter DAC_POLARITY = 0,
|
parameter COUNTER_MAX_WID = 16,
|
||||||
parameter DAC_PHASE = 1,
|
parameter TIMER_WID = 16
|
||||||
parameter DAC_CYCLE_HALF_WAIT = 10,
|
|
||||||
parameter DAC_CYCLE_HALF_WAIT_SIZ = 4,
|
|
||||||
parameter DAC_SS_WAIT = 5,
|
|
||||||
parameter DAC_SS_WAIT_SIZ = 3,
|
|
||||||
parameter TIMER_WID = 32,
|
|
||||||
parameter WORD_WID = 20,
|
|
||||||
parameter WORD_AMNT_WID = 11,
|
|
||||||
parameter [WORD_AMNT_WID-1:0] WORD_AMNT = 2047,
|
|
||||||
parameter RAM_REAL_START = 32'h12340,
|
|
||||||
parameter RAM_CNTR_LEN = 12,
|
|
||||||
parameter TOTAL_RAM_WORD_MINUS_ONE = 4095,
|
|
||||||
parameter DELAY_CNTR_LEN = 8,
|
|
||||||
parameter DELAY_TOTAL = 12,
|
|
||||||
parameter RAM_WID = 32,
|
|
||||||
parameter RAM_WORD_WID = 16,
|
|
||||||
parameter RAM_WORD_INCR = 2
|
|
||||||
) (
|
) (
|
||||||
input clk,
|
input clk,
|
||||||
input rst_L,
|
|
||||||
input arm,
|
|
||||||
input halt_on_finish,
|
|
||||||
output waveform_finished,
|
|
||||||
output running,
|
|
||||||
input [TIMER_WID-1:0] time_to_wait,
|
|
||||||
|
|
||||||
/* User interface */
|
/* Waveform output control */
|
||||||
input refresh_start,
|
input run,
|
||||||
input [RAM_WID-1:0] start_addr,
|
output [COUNTER_MAX_WID-1:0] cntr,
|
||||||
output reg refresh_finished,
|
input do_loop,
|
||||||
|
|
||||||
output [DAC_WID-1:0] from_master,
|
|
||||||
output finished,
|
output finished,
|
||||||
input rdy,
|
output ready,
|
||||||
output spi_err,
|
input [COUNTER_MAX_WID-1:0] wform_size,
|
||||||
|
output [TIMER_WID-1:0] timer,
|
||||||
|
input [TIMER_WID-1:0] timer_spacing,
|
||||||
|
|
||||||
input[RAM_WORD_WID-1:0] backing_store [TOTAL_RAM_WORD_MINUS_ONE:0]
|
/* data requests to Verilator */
|
||||||
|
|
||||||
|
output reg [32-1:0] offset,
|
||||||
|
output reg [32-1:0] spi_data,
|
||||||
|
input [32-1:0] ram_data,
|
||||||
|
output reg [1:0] enable,
|
||||||
|
input ram_finished,
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
input [32-1:0] spi_max_wait
|
||||||
);
|
);
|
||||||
|
|
||||||
wire sck;
|
wire [32-1:0] wb_adr;
|
||||||
wire ss_L;
|
wire wb_cyc;
|
||||||
wire mosi;
|
wire wb_we;
|
||||||
|
wire wb_stb;
|
||||||
spi_slave_no_write #(
|
wire [32-1:0] wb_dat_w;
|
||||||
.WID(DAC_WID),
|
reg [32-1:0] wb_dat_r;
|
||||||
.WID_LEN(DAC_WID_SIZ),
|
wire [4-1:0] wb_sel;
|
||||||
.POLARITY(DAC_POLARITY),
|
reg wb_ack = 0;
|
||||||
.PHASE(DAC_PHASE)
|
|
||||||
) slave (
|
|
||||||
.clk(clk),
|
|
||||||
.sck(sck),
|
|
||||||
.rst_L(rst_L),
|
|
||||||
.ss_L(ss_L),
|
|
||||||
.mosi(mosi),
|
|
||||||
.from_master(from_master),
|
|
||||||
.finished(finished),
|
|
||||||
.rdy(rdy),
|
|
||||||
.err(spi_err)
|
|
||||||
);
|
|
||||||
|
|
||||||
wire [RAM_WID-1:0] ram_dma_addr;
|
|
||||||
wire [RAM_WORD_WID-1:0] ram_word;
|
|
||||||
wire ram_read;
|
|
||||||
wire ram_valid;
|
|
||||||
|
|
||||||
dma_sim #(
|
|
||||||
.RAM_WID(RAM_WID),
|
|
||||||
.RAM_WORD_WID(RAM_WORD_WID),
|
|
||||||
.RAM_REAL_START(RAM_REAL_START),
|
|
||||||
.RAM_CNTR_LEN(RAM_CNTR_LEN),
|
|
||||||
.TOTAL_RAM_WORD_MINUS_ONE(TOTAL_RAM_WORD_MINUS_ONE),
|
|
||||||
.DELAY_CNTR_LEN(DELAY_CNTR_LEN),
|
|
||||||
.DELAY_TOTAL(DELAY_TOTAL)
|
|
||||||
) dma_sim (
|
|
||||||
.clk(clk),
|
|
||||||
.ram_dma_addr(ram_dma_addr),
|
|
||||||
.ram_word(ram_word),
|
|
||||||
.ram_read(ram_read),
|
|
||||||
.ram_valid(ram_valid),
|
|
||||||
.backing_store(backing_store)
|
|
||||||
);
|
|
||||||
|
|
||||||
waveform #(
|
waveform #(
|
||||||
.DAC_WID(DAC_WID),
|
.RAM_START_ADDR(RAM_START_ADDR),
|
||||||
.DAC_WID_SIZ(DAC_WID_SIZ),
|
.SPI_START_ADDR(SPI_START_ADDR),
|
||||||
.DAC_POLARITY(DAC_POLARITY),
|
.COUNTER_MAX_WID(COUNTER_MAX_WID),
|
||||||
.DAC_PHASE(DAC_PHASE),
|
.TIMER_WID(TIMER_WID)
|
||||||
.DAC_CYCLE_HALF_WAIT(DAC_CYCLE_HALF_WAIT),
|
) wf (
|
||||||
.DAC_CYCLE_HALF_WAIT_SIZ(DAC_CYCLE_HALF_WAIT_SIZ),
|
|
||||||
.DAC_SS_WAIT(DAC_SS_WAIT),
|
|
||||||
.DAC_SS_WAIT_SIZ(DAC_SS_WAIT_SIZ),
|
|
||||||
.TIMER_WID(TIMER_WID),
|
|
||||||
.WORD_WID(WORD_WID),
|
|
||||||
.WORD_AMNT_WID(WORD_AMNT_WID),
|
|
||||||
.WORD_AMNT(WORD_AMNT),
|
|
||||||
.RAM_WID(RAM_WID),
|
|
||||||
.RAM_WORD_WID(RAM_WORD_WID),
|
|
||||||
.RAM_WORD_INCR(RAM_WORD_INCR)
|
|
||||||
) waveform (
|
|
||||||
.clk(clk),
|
.clk(clk),
|
||||||
.arm(arm),
|
.run(run),
|
||||||
.rst_L(rst_L),
|
.cntr(cntr),
|
||||||
.halt_on_finish(halt_on_finish),
|
.do_loop(do_loop),
|
||||||
.running(running),
|
.finished(finished),
|
||||||
.finished(waveform_finished),
|
.ready(ready),
|
||||||
.time_to_wait(time_to_wait),
|
.wform_size(wform_size),
|
||||||
|
.timer(timer),
|
||||||
.refresh_start(refresh_start),
|
.timer_spacing(timer_spacing),
|
||||||
.start_addr(start_addr),
|
.wb_adr(wb_adr),
|
||||||
.refresh_finished(refresh_finished),
|
.wb_cyc(wb_cyc),
|
||||||
|
.wb_we(wb_we),
|
||||||
.ram_dma_addr(ram_dma_addr),
|
.wb_stb(wb_stb),
|
||||||
.ram_word(ram_word),
|
.wb_dat_w(wb_dat_w),
|
||||||
.ram_read(ram_read),
|
.wb_dat_r(wb_dat_r),
|
||||||
.ram_valid(ram_valid),
|
.wb_ack(wb_ack),
|
||||||
|
.wb_sel(wb_sel)
|
||||||
.mosi(mosi),
|
|
||||||
.sck(sck),
|
|
||||||
.ss_L(ss_L)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
reg [32-1:0] spi_cntr = 0;
|
||||||
|
reg spi_armed = 0;
|
||||||
|
reg spi_running = 0;
|
||||||
|
reg spi_ready_to_arm = 1;
|
||||||
|
reg spi_finished = 0;
|
||||||
|
|
||||||
|
/* SPI Delay simulation */
|
||||||
|
|
||||||
|
always @ (posedge clk) if ((spi_armed || spi_running) && !spi_finished) begin
|
||||||
|
spi_running <= 1;
|
||||||
|
spi_ready_to_arm <= 0;
|
||||||
|
if (spi_cntr == spi_max_wait) begin
|
||||||
|
spi_cntr <= 0;
|
||||||
|
spi_finished <= 1;
|
||||||
|
end else begin
|
||||||
|
spi_cntr <= spi_cntr + 1;
|
||||||
|
end
|
||||||
|
end else if (spi_finished) begin
|
||||||
|
spi_running <= 0;
|
||||||
|
if (!spi_armed) begin
|
||||||
|
spi_finished <= 0;
|
||||||
|
spi_ready_to_arm <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
/* Bus handlers */
|
||||||
|
|
||||||
|
always @* if (wb_we && wb_sel != 4'b1111)
|
||||||
|
$error("Write request without writing entire word");
|
||||||
|
|
||||||
|
always @ (posedge clk) if (wb_cyc & wb_stb & ~wb_ack) begin
|
||||||
|
if (wb_adr >= SPI_START_ADDR) case (wb_adr)
|
||||||
|
SPI_START_ADDR | 32'h4: if (wb_we) begin
|
||||||
|
spi_armed <= wb_dat_w[0];
|
||||||
|
wb_ack <= 1;
|
||||||
|
end else begin
|
||||||
|
wb_dat_r[0] <= spi_armed;
|
||||||
|
wb_ack <= 1;
|
||||||
|
end
|
||||||
|
SPI_START_ADDR | 32'hC: begin
|
||||||
|
if (!wb_we) $error("Waveform should never read the to_slave register");
|
||||||
|
|
||||||
|
/* Write SPI Data to verilator immediately, but have a counter
|
||||||
|
* running in the background to simulate SPI transfer */
|
||||||
|
spi_data <= wb_dat_w;
|
||||||
|
enable <= 2'b10;
|
||||||
|
if (ram_finished) begin
|
||||||
|
enable <= 0;
|
||||||
|
wb_ack <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
SPI_START_ADDR | 32'h10: begin
|
||||||
|
if (wb_we) $error("Blocking SPI status check is read only register");
|
||||||
|
if (spi_ready_to_arm || spi_finished) begin
|
||||||
|
wb_dat_r[0] <= spi_ready_to_arm;
|
||||||
|
wb_dat_r[1] <= spi_finished;
|
||||||
|
wb_ack <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
default: begin
|
||||||
|
$error("Invalid SPI address");
|
||||||
|
end
|
||||||
|
endcase else begin
|
||||||
|
offset <= wb_adr;
|
||||||
|
enable <= 2'b01;
|
||||||
|
if (ram_finished) begin
|
||||||
|
wb_dat_r <= ram_data;
|
||||||
|
enable <= 0;
|
||||||
|
wb_ack <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end else if (~wb_cyc) begin
|
||||||
|
wb_ack <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$dumpfile("waveform.fst");
|
||||||
|
$dumpvars;
|
||||||
|
end
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
`undefineall
|
|
||||||
|
|
|
@ -285,7 +285,6 @@ class UpsilonSoC(SoCCore):
|
||||||
platform.add_source("rtl/picorv32/picorv32.v")
|
platform.add_source("rtl/picorv32/picorv32.v")
|
||||||
platform.add_source("rtl/spi/spi_master_preprocessed.v")
|
platform.add_source("rtl/spi/spi_master_preprocessed.v")
|
||||||
platform.add_source("rtl/spi/spi_master_ss.v")
|
platform.add_source("rtl/spi/spi_master_ss.v")
|
||||||
platform.add_source("rtl/spi/spi_master_ss_wb.v")
|
|
||||||
|
|
||||||
# SoCCore does not have sane defaults (no integrated rom)
|
# SoCCore does not have sane defaults (no integrated rom)
|
||||||
SoCCore.__init__(self,
|
SoCCore.__init__(self,
|
||||||
|
|
Loading…
Reference in New Issue