This commit is contained in:
Peter McGoron 2023-02-25 21:17:04 +00:00
parent f88e0ef15c
commit 92091d0982
8 changed files with 366 additions and 12 deletions

View File

@ -183,3 +183,21 @@ In Verilog, in order to replace a macro identifier with the value of the
macro, you must put a backtick before the name: i.e.
`VALUE
## Forth scripting
The user controls the kernel through Forth scripts. The particular
implementation used is zForth.
Forth has the following memory access primitives:
* `addr` `@`: get value at `addr`
* `val` `addr` `!`: write `val` to `addr`
* `val` `,`: allocate a cell in "data space" (think the heap) and store
the data there.
* `addr` `#`: return the size of the value at addr (not standard Forth)
Each of these are not primitives in zForth. zForth allows for peeks and
pokes to get values of different lengths, and the standard operators are
for addressing a variable length value.

View File

@ -7,7 +7,13 @@ module autoapproach #(
parameter DAC_WID = 24,
parameter DAC_DATA_WID = 20,
parameter ADC_WID = 24,
parameter TIMER_WID = 32
parameter TIMER_WID = 32,
parameter WORD_WID = 24,
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 arm,
@ -18,20 +24,20 @@ module autoapproach #(
input [ADC_WID-1:0] setpoint,
input [TIMER_WID-1:0] time_to_wait,
/* BRAM memory interface. Each pulse returns the next value in
* the sequence, and also informs the module if the sequence
* is completed. The kernel interacts primarily with this interface.
*/
input [DAC_DATA_WID-1:0] word,
output word_next,
input word_last,
input word_ok,
output 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,
/* DAC wires. */
input dac_finished,
output dac_arm,
input [DAC_WID-1:0] dac_in,
output [DAC_WID-1:0] dac_out,
input adc_finished,
@ -39,6 +45,28 @@ module autoapproach #(
input [ADC_WID-1:0] measurement
);
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 (
.clk(clk),
.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)
);
localparam WAIT_ON_ARM = 0;
localparam DO_WAIT = 1;

View File

@ -0,0 +1,125 @@
#include <random>
#include <cmath>
#include "Vautoapproach_sim.h"
#include "../testbench.hpp"
/* TODO: generalize so bram_interface_sim can use it.
* This should make a triangle wave.
*/
class RefreshModule {
uint32_t *store_32;
size_t word_amnt;
bool pending_refresh_start;
public:
void posedge(uint8_t &refresh_start, uint32_t &start_addr,
uint8_t refresh_finished) {
if (refresh_start && refresh_finished) {
refresh_start = 0;
} else if (pending_refresh_start && !refresh_start) {
pending_refresh_start = false;
refresh_start = 1;
}
}
RefreshModule(size_t _word_amnt, size_t _start_addr)
: word_amnt{_word_amnt}
, start_addr{_start_addr} {
store_32 = new uint32_t[_start_addr];
for (size_t i = 0; i < start_addr; i++) {
/* 0xFFFFF is the maximum DAC value */
store_32[i] = 0xFFFFF*max(double)i/start_addr;
}
pending_refresh_start = true;
}
~RefreshModule() {
delete[] store_32;
}
};
/* TODO: make generic SPI delay class because this code has been duplicated
* many times over now. This function is also similar to the control loop
* ADC simulation. */
class GaussianZPiezo {
std::default_random_engine generator;
std::normal_distribution<> dist;
double scale;
double setpt;
double midpt;
double stretch;
double sample() {return scale*dist(generator);}
GaussianZPiezo(double scale, double mean, double dev, double setpt,
double midpt, double stretch, int seed,
uint16_t dac_wait_count,
uint16_t adc_wait_count)
: scale{scale}, dist{mean,dev}, generator{},
, setpt{setpt}, midpt{midpt}, stretch{stretch},
, dac_wait_count_max{dac_wait_count}
, adc_wait_count_max{adc_wait_count} {
if (seed < 0) {
std::random_device rd;
generator.seed(rd());
} else {
generator.seed(seed);
}
}
/* Sigmoid function. This function is
c(x-d)
f(x) = A*-------------------
sqrt(1+(c(x-d))^2)
where A is the setpoint and c is how compressed the sigmoid is.
*/
double f(uint32_t x) {
double x_shift = x - midpt + sample();
return setpt*stretch*x_shift/sqrt(fma(x_shift,x_shift,1));
}
public:
void posedge(uint8_t &dac_finished, uint8_t dac_arm,
uint32_t dac_out,
uint8_t &adc_finished, uint8_t adc_arm,
uint32_t &adc_out) {
if (adc_arm && adc_wait_counter == adc_wait_counter_max &&
!adc_finished) {
adc_finished = 1;
adc_out = sample();
} else if (!adc_arm) {
adc_finished = 0;
adc_wait_counter = 0;
} else {
adc_wait_counter++;
}
if (dac_arm && dac_wait_counter == dac_wait_counter_max &&
!dac_finished) {
dac_finished = 1;
}
};
class AA_TB : TB<Vautoapproach_sim> {
RefreshModule refresh;
GaussianZPiezo piezo;
void posedge() override;
AA_TB(size_t word_amnt, uint32_t start_addr)
: refresh{word_amnt, start_addr} {}
~AA_TB() {}
};
AA_TB::posedge() {
refresh.posedge(mod.refresh_start, mod.start_addr,
mod.refresh_finished);
}
int main(int argc, char **argv) {
return 0;
}

View File

@ -0,0 +1,96 @@
module autoapproach_sim #(
parameter DAC_WID = 24,
parameter DAC_DATA_WID = 20,
parameter ADC_WID = 24,
parameter TIMER_WID = 32,
parameter WORD_WID = 24,
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,
parameter TOTAL_RAM_WORD_MINUS_ONE = 4095
) (
input clk,
input arm,
output stopped,
output detected,
input polarity,
input [ADC_WID-1:0] setpoint,
input [TIMER_WID-1:0] time_to_wait,
/* User interface */
input refresh_start,
input [RAM_WID-1:0] start_addr,
output refresh_finished,
/* DAC wires. */
input dac_finished,
output dac_arm,
output [DAC_WID-1:0] dac_out,
input adc_finished,
output adc_arm,
input [ADC_WID-1:0] measurement
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)
);
autoapproach #(
.DAC_WID(DAC_WID),
.DAC_DATA_WID(DAC_DATA_WID),
.ADC_WID(ADC_WID),
.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)
) aa (
.clk(clk),
.arm(arm),
.stopped(stopped),
.detected(detected),
.polarity(polarity),
.setpoint(setpoint),
.time_to_wait(time_to_wait),
.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),
.dac_finished(dac_finished),
.dac_arm(dac_arm),
.dac_out(dac_out),
.adc_finished(adc_finished),
.adc_arm(adc_arm),
.measurement(measurement)
);
endmodule

View File

@ -0,0 +1,59 @@
#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;
}
}

View File

@ -0,0 +1,25 @@
#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);
};

View File

@ -63,7 +63,7 @@ static void test_aa_read_interrupted() {
}
static void refresh_data() {
for (size_t i = 0; i < RAM_WID; i++) {
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;

View File

@ -19,10 +19,13 @@ template <class TOP> class TB {
mod.final();
}
virtual void posedge() {}
void run_clock() {
mod.clk = !mod.clk;
mod.eval();
Verilated::timeInc(1);
posedge();
mod.clk = !mod.clk;
mod.eval();