control loop simulator passes lint

This commit is contained in:
Peter McGoron 2022-11-21 21:41:50 -05:00
parent 0114c449c3
commit 5909f548d5
10 changed files with 401 additions and 318 deletions

View File

@ -1,14 +1,17 @@
# Makefile for tests and hardware verification.
.PHONY: test clean
####### Tests ########
COMMON_CPP = control_loop_math_implementation.cpp
COMMON= ${COMMON_CPP} control_loop_math_implementation.h
CONSTS_FRAC=43
E_WID=21
test: obj_dir/Vcontrol_loop_math
obj_dir/Vcontrol_loop_math
test: obj_dir/Vcontrol_loop_sim_top obj_dir/Vcontrol_loop_math
# obj_dir/Vcontrol_loop_math
clean:
rm -rf obj_dir *.fst
@ -24,3 +27,24 @@ obj_dir/Vcontrol_loop_math.mk: control_loop_math_sim.cpp ${COMMON} \
obj_dir/Vcontrol_loop_math: obj_dir/Vcontrol_loop_math.mk
cd obj_dir && make -f Vcontrol_loop_math.mk
obj_dir/Vcontrol_loop_sim_top.mk: control_loop_sim.cpp ${COMMON} \
adc_sim.v dac_sim.v \
../spi/spi_master_ss.v \
../spi/spi_slave_no_write.v \
control_loop_sim_top.v control_loop_sim_top.v
verilator --cc --exe -Wall --trace --trace-fst \
--top-module control_loop_sim_top \
-GCONSTS_FRAC=${CONSTS_FRAC} \
-CFLAGS -DCONSTS_FRAC=${CONSTS_FRAC} \
-CFLAGS -DE_WID=${E_WID} -I../spi \
control_loop_sim_top.v control_loop.v control_loop_sim.cpp \
${COMMON_CPP} adc_sim.v dac_sim.v ../spi/spi_master_ss.v \
../spi/spi_slave_no_read.v ../spi/spi_slave.v
obj_dir/Vcontrol_loop_sim_top: obj_dir/Vcontrol_loop_sim_top.mk control_loop_cmds.h
cd obj_dir && make -f Vcontrol_loop_sim_top.mk
####### Codegen ########
control_loop_cmds.h: control_loop_cmds.vh
echo '#pragma once' > control_loop_cmds.h
sed 's/`define/#define/g; s/`//g' control_loop_cmds.vh >> control_loop_cmds.h

View File

@ -0,0 +1,68 @@
module adc_sim #(
parameter POLARITY = 1,
parameter PHASE = 0,
parameter WID = 18,
parameter WID_LEN = 5
) (
input clk,
input [WID-1:0] indat,
output reg request,
input fulfilled,
output err,
output miso,
input sck,
input ss_L
);
wire ss = !ss_L;
reg ss_raised = 0;
reg fulfilled_raised = 0;
reg ss_buf_L = 1;
reg [WID-1:0] data = 0;
reg rdy = 0;
wire spi_fin;
always @ (posedge clk) begin
if (ss && !ss_raised) begin
request <= 1;
ss_raised <= 1;
end else if (ss_raised && !ss) begin
ss_raised <= 0;
ss_buf_L <= 1;
rdy <= 0;
request <= 0;
fulfilled_raised <= 0;
end else if (ss_raised && request && fulfilled && !fulfilled_raised) begin
data <= indat;
fulfilled_raised <= 1;
request <= 0;
end else if (ss_raised && request && !fulfilled && fulfilled_raised) begin
rdy <= 1;
fulfilled_raised <= 0;
ss_buf_L <= 0;
end else if (spi_fin) begin
rdy <= 0;
end
end
spi_slave_no_read #(
.WID(WID),
.WID_LEN(WID_LEN),
.POLARITY(POLARITY),
.PHASE(PHASE)
) spi (
.clk(clk),
.sck(sck),
.ss_L(ss_buf_L),
.miso(miso),
.to_master(data),
.finished(spi_fin),
.rdy(rdy),
.err(err)
);
endmodule
`undefineall

View File

@ -1,5 +1,4 @@
`include control_loop_cmds.vh
`define ERR_WID (ADC_WID + 1)
`include "control_loop_cmds.vh"
module control_loop
#(
@ -17,14 +16,10 @@ module control_loop
parameter CONSTS_WHOLE = 21,
parameter CONSTS_FRAC = 43,
parameter CONSTS_SIZ = 7,
`define CONSTS_WID (CONSTS_WHOLE + CONSTS_FRAC)
parameter DELAY_WID = 16,
/* [ERR_WID_SIZ-1:0] must be able to store
* ERR_WID (= ADC_WID + 1).
*/
parameter ERR_WID_SIZ = 6,
`define DATA_WID `CONSTS_WID
`define E_WID (ADC_WID + 1)
parameter READ_DAC_DELAY = 5,
parameter CYCLE_COUNT_WID = 18,
parameter DAC_WID = 24,
@ -34,6 +29,7 @@ module control_loop
*/
parameter DAC_WID_SIZ = 5,
parameter DAC_DATA_WID = 20,
`define E_WID (DAC_DATA_WID + 1)
parameter DAC_POLARITY = 0,
parameter DAC_PHASE = 1,
parameter DAC_CYCLE_HALF_WAIT = 10,
@ -49,13 +45,13 @@ module control_loop
output dac_sck,
input adc_miso,
output adc_conv,
output adc_conv_L,
output adc_sck,
/* Hacky ad-hoc read-write interface. */
input reg [CONTROL_LOOP_CMD_WIDTH-1:0] cmd,
input reg [DATA_WIDTH-1:0] word_in,
output reg [DATA_WIDTH-1:0] word_out,
input reg [`CONTROL_LOOP_CMD_WIDTH-1:0] cmd,
input reg [`DATA_WID-1:0] word_in,
output reg [`DATA_WID-1:0] word_out,
input start_cmd,
output reg finish_cmd
);
@ -64,8 +60,13 @@ module control_loop
reg dac_arm;
reg dac_finished;
reg dac_ss = 0;
assign dac_ss_L = !dac_ss;
reg [DAC_WID-1:0] to_dac;
/* verilator lint_off UNUSED */
wire [DAC_WID-1:0] from_dac;
/* verilator lint_on UNUSED */
spi_master_ss #(
.WID(DAC_WID),
.WID_LEN(DAC_WID_SIZ),
@ -77,7 +78,6 @@ spi_master_ss #(
.SS_WAIT_TIMER_LEN(DAC_SS_WAIT_SIZ)
) dac_master (
.clk(clk),
.arm(dac_arm),
.mosi(dac_mosi),
.miso(dac_miso),
.sck_wire(dac_sck),
@ -92,7 +92,7 @@ reg adc_arm;
reg adc_finished;
wire [ADC_WID-1:0] measured_value;
localparam [3-1:0] DAC_REGISTER = 3b'001;
localparam [3-1:0] DAC_REGISTER = 3'b001;
spi_master_ss_no_write #(
.WID(ADC_WID),
@ -109,7 +109,7 @@ spi_master_ss_no_write #(
.from_slave(measured_value),
.miso(adc_miso),
.sck_wire(adc_sck),
.ss_L(!ss_conv),
.ss_L(adc_conv_L),
.finished(adc_finished)
);
@ -143,12 +143,13 @@ reg running = 0;
reg signed [DAC_DATA_WID-1:0] stored_dac_val = 0;
reg [CYCLE_COUNT_WID-1:0] last_timer = 0;
reg [CYCLE_COUNT_WID-1:0] debug_timer = 0;
reg [CYCLE_COUNT_WID-1:0] counting_timer = 0;
reg [`CONSTS_WID-1:0] adjval_prev = 0;
reg signed [`E_WID-1:0] err_prev = 0;
wire signed [`E_WID-1:0] e_cur = 0;
wire signed [`CONSTS_WID-1:0] adj_val = 0;
wire signed [`E_WID-1:0] e_cur;
wire signed [`CONSTS_WID-1:0] adj_val;
wire signed [DAC_DATA_WID-1:0] new_dac_val;
reg arm_math = 0;
reg math_finished = 0;
@ -157,7 +158,10 @@ control_loop_math #(
.CONSTS_FRAC(CONSTS_FRAC),
.CONSTS_SIZ(CONSTS_SIZ),
.ADC_WID(ADC_WID),
.CYCLE_COUNT_WID(CYCLE_COUNT_WID)
.DAC_WID(DAC_DATA_WID),
.CYCLE_COUNT_WID(CYCLE_COUNT_WID),
.SEC_PER_CYCLE('b10101011110011000),
.ADC_TO_DAC({32'b01000001100, 32'b01001001101110100101111000110101})
) math (
.clk(clk),
.arm(arm_math),
@ -169,6 +173,8 @@ control_loop_math #(
.cycles(last_timer),
.e_prev(err_prev),
.adjval_prev(adjval_prev),
.stored_dac_val(stored_dac_val),
.new_dac_val(new_dac_val),
.e_cur(e_cur),
.adj_val(adj_val)
);
@ -208,12 +214,13 @@ control_loop_math #(
localparam CYCLE_START = 0;
localparam WAIT_ON_ADC = 1;
localparam WAIT_ON_MATH = 2;
localparam WAIT_ON_DAC = 6;
localparam INIT_READ_FROM_DAC = 3;
localparam WAIT_FOR_DAC_READ = 4;
localparam WAIT_FOR_DAC_RESPONSE = 5;
localparam STATESIZ = 3;
reg [STATESIZ-1:0] state = CYCLE_START;
reg [STATESIZ-1:0] state = INIT_READ_FROM_DAC;
reg [DELAY_WID-1:0] timer = 0;
@ -232,85 +239,88 @@ end
* the main loop is clearing the dirty bit.
*/
wire write_control = state == CYCLE_START;
wire write_control = state == CYCLE_START || !running;
reg dirty_bit = 0;
always @ (posedge clk) begin
if (start_cmd && !finish_cmd) begin
case (cmd)
CONTROL_LOOP_NOOP: CONTROL_LOOP_NOOP | CONTROL_LOOP_WRITE_BIT:
`CONTROL_LOOP_NOOP:
finish_cmd <= 1;
CONTROL_LOOP_STATUS: begin
word_out[DATA_WID-1:1] <= 0;
`CONTROL_LOOP_NOOP | `CONTROL_LOOP_WRITE_BIT:
finish_cmd <= 1;
`CONTROL_LOOP_STATUS: begin
word_out[`DATA_WID-1:1] <= 0;
word_out[0] <= running;
finish_cmd <= 1;
end
CONTROL_LOOP_STATUS | CONTROL_LOOP_WRITE_BIT:
`CONTROL_LOOP_STATUS | `CONTROL_LOOP_WRITE_BIT:
if (write_control) begin
running <= word_in[0];
finish_cmd <= 1;
dirty_bit <= 1;
end
CONTROL_LOOP_SETPT: begin
word_out[DATA_WID-1:ADC_WID] <= 0;
`CONTROL_LOOP_SETPT: begin
word_out[`DATA_WID-1:ADC_WID] <= 0;
word_out[ADC_WID-1:0] <= setpt;
finish_cmd <= 1;
end
CONTROL_LOOP_SETPT | CONTROL_LOOP_WRITE_BIT:
`CONTROL_LOOP_SETPT | `CONTROL_LOOP_WRITE_BIT:
if (write_control) begin
setpt_buffer <= word_in[ADC_WID-1:0];
finish_cmd <= 1;
dirty_bit <= 1;
end
CONTROL_LOOP_P: begin
`CONTROL_LOOP_P: begin
word_out <= cl_p_reg;
finish_cmd <= 1;
end
CONTROL_LOOP_P | CONTROL_LOOP_WRITE_BIT: begin
`CONTROL_LOOP_P | `CONTROL_LOOP_WRITE_BIT: begin
if (write_control) begin
cl_p_reg_buffer <= word_in;
finish_cmd <= 1;
dirty_bit <= 1;
end
end
CONTROL_LOOP_I: begin
`CONTROL_LOOP_I: begin
word_out <= cl_I_reg;
finish_cmd <= 1;
end
CONTROL_LOOP_I | CONTROL_LOOP_WRITE_BIT: begin
`CONTROL_LOOP_I | `CONTROL_LOOP_WRITE_BIT: begin
if (write_control) begin
cl_I_reg_buffer <= word_in;
finish_cmd <= 1;
dirty_bit <= 1;
end
end
CONTROL_LOOP_DELAY: begin
word_out[DATA_WID-1:DELAY_WID] <= 0;
`CONTROL_LOOP_DELAY: begin
word_out[`DATA_WID-1:DELAY_WID] <= 0;
word_out[DELAY_WID-1:0] <= dely;
finish_cmd <= 1;
end
CONTROL_LOOP_DELAY | CONTROL_LOOP_WRITE_BIT: begin
`CONTROL_LOOP_DELAY | `CONTROL_LOOP_WRITE_BIT: begin
if (write_control) begin
dely_buffer <= word_in[DELAY_WID-1:0];
finish_cmd <= 1;
dirty_bit <= 1;
end
end
CONTROL_LOOP_ERR: begin
word_out[DATA_WID-1:ERR_WID] <= 0;
word_out[ERR_WID-1:0] <= err_prev;
`CONTROL_LOOP_ERR: begin
word_out[`DATA_WID-1:`E_WID] <= 0;
word_out[`E_WID-1:0] <= err_prev;
finish_cmd <= 1;
end
CONTROL_LOOP_Z: begin
word_out[DATA_WID-1:DAC_DATA_WID] <= 0;
`CONTROL_LOOP_Z: begin
word_out[`DATA_WID-1:DAC_DATA_WID] <= 0;
word_out[DAC_DATA_WID-1:0] <= stored_dac_val;
finish_cmd <= 1;
end
CONTROL_LOOP_CYCLES: begin
word_out[DATA_WID-1:CYCLE_COUNT_WID] <= 0;
`CONTROL_LOOP_CYCLES: begin
word_out[`DATA_WID-1:CYCLE_COUNT_WID] <= 0;
word_out[CYCLE_COUNT_WID-1:0] <= last_timer;
finish_cmd <= 0;
end
endcase
end else if (!start_cmd) begin
finish_cmd <= 0;
end
@ -320,7 +330,7 @@ always @ (posedge clk) begin
case (state)
INIT_READ_FROM_DAC: begin
if (running) begin
to_dac <= {1, DAC_REGISTER, 20b'0};
to_dac <= {1'b1, DAC_REGISTER, 20'b0};
dac_arm <= 1;
state <= WAIT_FOR_DAC_READ;
end
@ -337,13 +347,13 @@ always @ (posedge clk) begin
timer <= timer + 1;
end else if (timer == READ_DAC_DELAY) begin
dac_arm <= 1;
to_dac <= 24b'0;
to_dac <= 24'b0;
timer <= 0;
end else if (dac_finished) begin
state <= CYCLE_START;
dac_ss <= 0;
dac_arm <= 0;
stored_dac_val <= from_dac;
stored_dac_val <= from_dac[DAC_DATA_WID-1:0];
end
end
CYCLE_START: begin
@ -355,10 +365,10 @@ always @ (posedge clk) begin
/* On change of constants, previous values are invalidated. */
if (dirty_bit) begin
setpt <= setpt_buffer;
dely <= dely_buf;
cl_alpha_reg <= cl_alpha_reg_buffer;
dely <= dely_buffer;
cl_I_reg <= cl_I_reg_buffer;
cl_p_reg <= cl_p_reg_buffer;
adj_prev <= 0;
adjval_prev <= 0;
err_prev <= 0;
dirty_bit <= 0;
@ -377,19 +387,19 @@ always @ (posedge clk) begin
WAIT_ON_MATH: if (math_finished) begin
arm_math <= 0;
dac_arm <= 1;
stored_dac_val <= (stored_dac_val + adj_val[`CONSTS_WID-1:CONSTS_FRAC]);
to_dac <= {0, DAC_REGISTER, (dac_adj_val + adj_val[`CONSTS_WID-1:CONSTS_FRAC]);
stored_dac_val <= new_dac_val;
to_dac <= {1'b0, DAC_REGISTER, new_dac_val};
state <= WAIT_ON_DAC;
end
WAIT_ON_DAC: if (dac_finished) begin
state <= CYCLE_START;
dac_ss <= 0;
dac_arm <= 0;
err_prev <= err_cur;
adj_old <= newadj;
end
err_prev <= e_cur;
adjval_prev <= adj_val;
end
endcase
end
endmodule
`undefineall

View File

@ -6,5 +6,6 @@
`define CONTROL_LOOP_ERR 5
`define CONTROL_LOOP_Z 6
`define CONTROL_LOOP_CYCLES 7
`define CONTROL_LOOP_WRITE_BIT (1 << (CONTROL_LOOP_CMD_WIDTH-1))
`define CONTROL_LOOP_DELAY 8
`define CONTROL_LOOP_CMD_WIDTH 8
`define CONTROL_LOOP_WRITE_BIT (1 << (`CONTROL_LOOP_CMD_WIDTH-1))

View File

@ -33,7 +33,7 @@ module control_loop_math #(
/* The conversion between the ADC bit (20/2**18) and DAC bit (20.48/2**20)
* is 0.256.
*/
parameter logic [`CONSTS_WID-1:0] ADC_TO_DAC = {32'b01000001100, 32'b01001001101110100101111000110101},
parameter [`CONSTS_WID-1:0] ADC_TO_DAC = 64'b0100000110001001001101110100101111000110101,
parameter CYCLE_COUNT_WID = 18,
parameter DAC_WID = 20
`define E_WID (DAC_WID + 1)
@ -49,6 +49,7 @@ module control_loop_math #(
input signed [CYCLE_COUNT_WID-1:0] cycles,
input signed [`E_WID-1:0] e_prev,
input signed [`CONSTS_WID-1:0] adjval_prev,
input signed [DAC_WID-1:0] stored_dac_val,
`ifdef DEBUG_CONTROL_LOOP_MATH
output reg [`CONSTS_WID-1:0] dt_reg,
@ -58,6 +59,7 @@ module control_loop_math #(
`endif
output reg signed [`E_WID-1:0] e_cur,
output signed [DAC_WID-1:0] new_dac_val,
output signed [`CONSTS_WID-1:0] adj_val
);
@ -127,6 +129,18 @@ intsat #(
.outp(saturated_add)
);
/************************
* Safely calculate new DAC value.
************************/
reg signed [DAC_WID+1-1:0] add_sat_dac;
intsat #(
.IN_LEN(DAC_WID+1),
.LTRUNC(1)
) dac_saturate (
.inp(add_sat_dac),
.outp(new_dac_val)
);
localparam WAIT_ON_ARM = 0;
localparam CALCULATE_ERR = 9;
localparam CALCULATE_DAC_E = 7;
@ -136,6 +150,7 @@ localparam CALCULATE_EPIDT = 3;
localparam CALCULATE_EP = 4;
localparam CALCULATE_A_PART_1 = 5;
localparam CALCULATE_A_PART_2 = 6;
localparam CALCULATE_NEW_DAC_VALUE = 10;
localparam WAIT_ON_DISARM = 8;
reg [4:0] state = WAIT_ON_ARM;
@ -236,6 +251,12 @@ always @ (posedge clk) begin
end
CALCULATE_A_PART_2: begin
add_sat <= tmpstore;
state <= CALCULATE_NEW_DAC_VALUE;
end
CALCULATE_NEW_DAC_VALUE: begin
add_sat_dac <= saturated_add[CONSTS_FRAC+DAC_WID-1:CONSTS_FRAC]
+ stored_dac_val;
adj_val <= saturated_add;
state <= WAIT_ON_DISARM;
end
WAIT_ON_DISARM: begin

View File

@ -8,205 +8,77 @@
#include <unistd.h>
#include <verilated.h>
#include "Vcontrol_loop.h"
#include "control_loop_math_implementation.h"
#include "control_loop_cmds.h"
#include "Vcontrol_loop_sim_top.h"
using ModType = Vcontrol_loop_sim_top;
Vcontrol_loop *mod;
uint32_t main_time = 0;
double sc_time_stamp() {
return main_time;
}
/* Very simple simulation of measurement.
* A transfer function defines the mapping from the DAC values
* -2**(20) -> 2**(20)-1
* to the values -2**(18) -> 2**18 - 1.
*
* The transfer function has Gaussian noise which is added at each
* measurement.
*/
ModType *mod;
class Transfer {
std::default_random_engine generator;
std::normal_distribution dist;
double scale;
double sample() {return scale*dist(rd);}
public:
Transfer(double scale, double mean, double dev, double m, double b, int seed)
: scale{scale}, dist{mean,dev}, generator{} {
if (seed < 0) {
std::random_device rd;
generator.seed(rd());
} else {
generator.seed(seed);
static void run_clock() {
for (int i = 0; i < 2; i++) {
mod->clk = !mod->clk;
mod->eval();
main_time++;
}
}
double val(double x) {
return m*x + b + sample();
}
};
/* Each constant is 48 bits long, with 15 whole bits.
constexpr auto CONSTS_WHOLE_WID = 15;
constexpr auto CONSTS_WID = 48;
constexpr auto CONSTS_FRAC_WID = CONSTS_WID - CONSTS_WHOLE_WID;
constexpr auto CONSTS_FRAC_MASK = (1 << CONSTS_FRAC_WID) - 1;
constexpr uint64_t fractional_base_conv(uint64_t input) {
/* Fractional base conversion algorithm.
Given an integer in base M (i.e. 10) there is an expansion in base N (i.e. 2):
0.abcdefgh... = 0.ijklmnop...
where abcdefgh... are in base M and ijklmnop... are in base N. The algorithm
computes the digits in base N.
Multiply the converted number by N. Then there are new numbers:
A.BCDEFGH... = i.jklmnop...
Since 0.abcdefgh < 1, A.BCDEFGH < N. Therefore
the digit "A" must be a number less than N. Then i = A. Cutting off all the
other digits,
0.BCDEFGH... = 0.jklmnop...
continue until there are no more digits left.
*/
/* Calculate the lowest power of 10 greater than input.
This can be done with logarithms, but floating point is not available on
some embedded platforms. This makes the code more portable.
*/
uint64_t pow10 = 1;
while (input / pow10 > 0)
pow10 *= 10;
uint64_t out = 0;
for (unsigned i = 0; i < CONSTS_FRAC_WID; i++) {
input *= 2;
uint64_t dig = input / pow10, mod = input % pow10;
out = dig | (out << 1);
input = mod;
}
return out;
}
static int64_t multiply_unity(uint64_t i, int sign) {
if (sign > 0) {
return std::reinterpret_cast<int64_t>(i);
} else {
return std::reinterpret_cast<int64_t>(~i + 1);
}
}
constexpr uint64_t SCALE_WHOLE = 12820;
constexpr uint64_t SCALE_FRAC = fractional_base_conv(51282051282);
constexpr uint64_t SCALE_NUM = (SCALE_WHOLE << CONSTS_FRAC_WID) | SCALE_FRAC;
static int64_t signed_to_fxp(char *s) {
// Skip whitespace.
while (isspace(*c++));
// Check if number is negative.
int sign = 1;
if (*s == '-') {
pos = -1;
s++;
}
// Split the number into whole and fractional components.
char *p = strchr(s, '.');
if (!p)
return multiply_unity(strtoull(s, NULL, 10), sign);
*p = 0;
// s now points to a NUL terminated string with the whole number
// component.
uint64_t whole = strtoull(s, NULL, 10);
p++;
// p is the start of the fractional component.
uint64_t frac_decimal = strtoull(p, NULL, 10);
uint64_t final = ((whole << CONSTS_FRAC_WID) | fractional_base_conv(frac_decimal, CONSTS_FRAC_WID))
* SCALE_NUM;
return multiply_unity(final, sign);
}
static std::string fxp_to_str(int64_t inum, unsigned decdigs) {
std::string s = "";
uint64_t num;
if (inum < 0) {
num = std::reinterpret_cast<uint64_t>(~inum) + 1;
s.insert(0,1, '-');
} else {
num = std::reinterpet_cast<uint64_t>(num);
}
s += std::to_string(num >> CONSTS_FRAC_WID);
int64_t frac = num & CONSTS_FRAC_MASK;
if (frac == 0 || decdigs == 0)
return;
s += ".";
/* Applying the algorithm in fractional_base_conv() backwards. */
while (decdigs > 0 && frac != 0) {
num *= 2;
s += std::to_string(num >> CONSTS_FRAC_WID);
num = num & CONSTS_FRAC_WID;
}
return s;
}
static int64_t I_const, dt_const, P_const, setpt;
static unsigned long seed, ;
static void usage(char *argv0, int code) {
std::cout << argv0 << " -d deviation -m mean -I I -t dt -d delay -s seed -S setpt -P p [+verilator...]" << std::endl;
exit(code);
}
static void parse_args(int argc, char *argv[]) {
const char *optstring = "I:t:s:P:d:m:h";
int opt;
static void init(int argc, char **argv) {
Verilated::commandArgs(argc, argv);
while ((opt = getopt(argc, argv, optstring)) != -1) {
switch (opt) {
case 'm':
noise_mean = strtod(optstring, NULL);
break;
case 'd':
dev_mean = strtod(optstring, NULL);
break;
case 'I':
I_const = signed_to_fxp(optarg);
break;
case 't':
dt_const = signed_to_fxp(optarg);
break;
case 'S':
setpt = signed_to_fxp(optarg);
break;
case 's':
seed = strtoul(optarg, NULL, 10);
break;
case 'P':
P_const = strtoul(optarg, NULL, 10);
break;
case 'd':
dely = strtoul(optarg, NULL, 10);
break;
case 'h':
usage(argv[0], 0);
break;
default:
usage(argv[1], 1);
break;
}
}
}
Vtop *mod;
int main(int argc, char **argv) {
parse_args(argc, argv);
mod = new Vtop;
Verilated::traceEverOn(true);
mod = new ModType;
mod->clk = 0;
}
static void set_value(V val, unsigned name) {
mod->cmd = CONTROL_LOOP_WRITE_BIT | name;
mod->word_into_loop = val;
mod->start_cmd = 1;
do { run_clock(); } while (!mod->finish_cmd);
mod->start_cmd = 0;
run_clock();
}
int main(int argc, char **argv) {
init(argc, argv);
mod = new ModType;
Transfer func = Transfer{150, 0, 2, 1.1, 10, -1};
mod->clk = 0;
set_value(10000, CONTROL_LOOP_STATUS);
set_value(0b11010111000010100011110101110000101000111, CONTROL_LOOP_P);
set_value((V)12 << CONSTS_FRAC, CONTROL_LOOP_I);
set_value(20, CONTROL_LOOP_DELAY);
set_value(1, CONTROL_LOOP_STATUS);
for (int tick = 0; tick < 10000; tick++) {
std::cout << tick << std::endl;
run_clock();
if (mod->request && !mod->fulfilled) {
mod->measured_value = func.val(mod->curset);
mod->fulfilled = 1;
} else if (mod->fulfilled && !mod->request) {
mod->fulfilled = 0;
}
if (tick == 5000) {
mod->cmd = CONTROL_LOOP_WRITE_BIT | CONTROL_LOOP_P;
mod->word_into_loop = 0b010111000010100011110101110000101000111;
mod->start_cmd = 1;
}
if (mod->finish_cmd) {
mod->start_cmd = 0;
}
}
mod->final();
delete mod;
return 0;
}

View File

@ -1,117 +1,131 @@
`include control_loop_cmds.vh
`include "control_loop_cmds.vh"
module top
#(
module control_loop_sim_top #(
parameter ADC_WID = 18,
parameter ADC_WID_LEN = 5,
parameter ADC_WID_SIZ = 5,
parameter ADC_POLARITY = 1,
parameter ADC_PHASE = 0,
parameter ADC_CYCLE_HALF_WAIT = 5,
parameter ADC_TIMER_LEN = 3,
parameter DAC_POLARITY = 0,
parameter DAC_PHASE = 1,
parameter DAC_DATA_WID = 20,
parameter DAC_WID = 24,
parameter DAC_WID_LEN = 5,
parameter DAC_CYCLE_HALF_WAIT = 10,
parameter DAC_TIMER_LEN = 4,
parameter DAC_WID_SIZ = 5,
parameter CONSTS_WID = 48,
parameter CONSTS_WHOLE = 21,
parameter CONSTS_FRAC = 43,
`define CONSTS_WID (CONSTS_WHOLE + CONSTS_FRAC)
parameter CONSTS_SIZ = 7,
parameter DELAY_WID = 16
)(
input clk,
input signed [ADC_WID-1:0] measured_data,
input [DAC_DATA_WID-1:0] dac_in,
output [DAC_DATA_WID-1:0] dac_out,
output dac_input_ready,
output [DAC_DATA_WID-1:0] curset,
output dac_err,
input [CONSTS_WID-1:0] word_into_loop,
output [CONSTS_WID-1:0] word_outof_loop,
input [ADC_WID-1:0] measured_value,
output request,
input fulfilled,
output adc_err,
input [`CONSTS_WID-1:0] word_into_loop,
output [`CONSTS_WID-1:0] word_outof_loop,
input start_cmd,
output finish_cmd,
input [CONTROL_LOOP_CMD_WIDTH-1:0] cmd
input [`CONTROL_LOOP_CMD_WIDTH-1:0] cmd
);
wire dac_miso;
wire dac_mosi;
wire dac_sck;
wire ss_L;
spi_master #(
.WID(DAC_WID),
.WID_LEN(DAC_WID_LEN),
.POLARITY(DAC_POLARITY),
.PHASE(DAC_PHASE),
.CYCLE_HALF_WAIT(DAC_CYCLE_HALF_WAIT),
.TIMER_LEN(DAC_TIMER_LEN)
) dac_master (
.clk(clk),
.from_slave(dac_set_data),
.miso(dac_miso),
.to_slave(
.mosi(dac_mosi),
wire adc_sck;
wire adc_ss;
wire adc_miso;
reg adc_finished = 0;
wire dac_mosi;
wire dac_sck;
wire dac_ss;
reg dac_finished = 0;
/* Emulate a control loop environment with simulator controlled
SPI interfaces.
*/
wire adc_miso;
wire adc_sck;
wire adc_ss_L;
/* ADC */
spi_slave_no_write #(
adc_sim #(
.WID(ADC_WID),
.WID_LEN(5),
.ADC_POLARITY(ADC_POLARITY),
.ADC_PHASE(ADC_PHASE)
)(
.POLARITY(ADC_POLARITY),
.PHASE(ADC_PHASE)
) adc (
.clk(clk),
.to_master(measured_data),
.sck(adc_sck),
.ss_L(!adc_ss),
.indat(measured_value),
.request(request),
.fulfilled(fulfilled),
.err(adc_err),
.miso(adc_miso),
.rdy(!dac_ss),
.finished(adc_finished)
.sck(adc_sck),
.ss_L(adc_ss_L)
);
wire dac_miso;
wire dac_mosi;
wire dac_ss_L;
wire dac_sck;
/* DAC */
spi_slave_no_read #(
dac_sim #(
.WID(DAC_WID),
.DATA_WID(DAC_DATA_WID),
.WID_LEN(5),
.DAC_POLARITY(DAC_POLARITY),
.DAC_PHASE(DAC_PHASE)
)(
.POLARITY(DAC_POLARITY),
.PHASE(DAC_PHASE)
) dac (
.clk(clk),
.from_master(output_data),
.curset(curset),
.mosi(dac_mosi),
.miso(dac_miso),
.sck(dac_sck),
.ss_L(!dac_ss),
.rdy(!dac_ss),
.finished(dac_finished)
.ss_L(dac_ss_L),
.err(dac_err)
);
control_loop #(
.ADC_WID(ADC_WID),
.DAC_WID(DAC_WID),
.DAC_DATA_WID(DAC_DATA_WID),
.CONSTS_WID(CONSTS_WID),
.DELAY_WID(DELAY_WID),
.ADC_WID_SIZ(ADC_WID_SIZ),
.ADC_POLARITY(ADC_POLARITY),
.ADC_PHASE(ADC_PHASE),
/* Keeping cycle half wait and conv wait the same
* since it doesn't matter for this simulation */
.CONSTS_WHOLE(CONSTS_WHOLE),
.CONSTS_FRAC(CONSTS_FRAC),
.CONSTS_SIZ(CONSTS_SIZ),
.DELAY_WID(DELAY_WID),
.DAC_WID(DAC_WID),
.DAC_WID_SIZ(DAC_WID_SIZ),
.DAC_DATA_WID(DAC_DATA_WID),
.DAC_POLARITY(DAC_POLARITY),
.DAC_PHASE(DAC_PHASE)
) cloop (
.clk(clk),
.dac_mosi(dac_mosi),
.dac_miso(dac_miso),
.dac_ss_L(dac_ss_L),
.dac_sck(dac_sck),
.adc_miso(adc_miso),
.adc_conv_L(adc_ss_L),
.adc_sck(adc_sck),
.word_in(word_into_loop),
.word_out(word_outof_loop),
.start_cmd(start_cmd),
.finish_cmd(finish_cmd),
.cmd(cmd)
);
`ifdef VERILATOR
initial begin
$dumpfile("control_loop.fst");
$dumpvars;
end
`endif
endmodule
`undefineall

View File

@ -0,0 +1,71 @@
module dac_sim #(
parameter POLARITY = 0,
parameter PHASE = 1,
parameter WID = 24,
parameter DATA_WID = 20,
parameter WID_LEN = 5
) (
input clk,
output reg [DATA_WID-1:0] curset,
input mosi,
output miso,
input sck,
input ss_L,
output err
);
wire [WID-1:0] from_master;
reg [WID-1:0] to_master = 0;
reg rdy = 1;
wire spi_fin;
reg [WID-4-1:0] ctrl_register = 0;
always @ (posedge clk) begin
if (spi_fin) begin
rdy <= 0;
/* read current value. TODO: lower bit DACs have zero
* padding between register and DAC value. */
case (from_master[WID-1:WID-4])
4'b1001: begin
to_master <= {4'b1001, curset};
end
4'b0001: begin
curset <= from_master [DATA_WID-1:0];
to_master <= 0;
end
4'b0010: begin
ctrl_register <= to_master[WID-1-4:0];
to_master <= 0;
end
4'b1010: begin
to_master <= {4'b1010, ctrl_register};
end
default: ;
endcase
end else if (!rdy) begin
rdy <= 1;
end
end
spi_slave #(
.WID(WID),
.WID_LEN(WID_LEN),
.POLARITY(POLARITY),
.PHASE(PHASE)
) spi (
.clk(clk),
.sck(sck),
.ss_L(ss_L),
.miso(miso),
.mosi(mosi),
.from_master(from_master),
.to_master(to_master),
.finished(spi_fin),
.rdy(rdy),
.err(err)
);
endmodule
`undefineall

View File

@ -106,3 +106,4 @@ always @ (posedge clk) begin
end
endmodule
`undefineall

View File

@ -146,3 +146,4 @@ always @ (posedge clk) begin
end
endmodule
`undefineall