stuff
This commit is contained in:
parent
f88e0ef15c
commit
92091d0982
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue