control loop simulator passes lint
This commit is contained in:
parent
0114c449c3
commit
5909f548d5
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -106,3 +106,4 @@ always @ (posedge clk) begin
|
|||
end
|
||||
|
||||
endmodule
|
||||
`undefineall
|
||||
|
|
|
@ -146,3 +146,4 @@ always @ (posedge clk) begin
|
|||
end
|
||||
|
||||
endmodule
|
||||
`undefineall
|
||||
|
|
Loading…
Reference in New Issue