change control_loop to m4 scripts, add common makefile

This commit is contained in:
Peter McGoron 2023-03-15 18:30:08 +00:00
parent 7af907ffb4
commit 953e42b80c
11 changed files with 145 additions and 132 deletions

View File

@ -9,12 +9,8 @@ See also [Dan Gisselquist][1]'s rules for FPGA development.
* Stick to Verilog 2005. F4PGA will accept SystemVerilog but yosys sometimes * Stick to Verilog 2005. F4PGA will accept SystemVerilog but yosys sometimes
synthesizes it incorrectly. synthesizes it incorrectly.
* Do not use parameters that are calculated from other parameters (yosys * Do not use parameters that are calculated from other parameters (yosys
will not parse them correctly). Use macros instead. will not parse them correctly). Use m4 macros instead.
* Only use Verlog macros for basic expression replacement (replacing calculated * Do not use Verilog macros. Use m4.
parameters). For more advanced code generation, use m4 (see `base.m4` as an
example).
* Add `undefineall` at the end of *every single module*. If you do not,
you will get many confusing errors.
* Do all code and test generation in Makefiles. * Do all code and test generation in Makefiles.
* Simulate *every* module, even the trivial ones using Verilator. * Simulate *every* module, even the trivial ones using Verilator.
Simulation must be simulatable with open-source software (Verilator is Simulation must be simulatable with open-source software (Verilator is

View File

@ -1,5 +1,5 @@
.PHONY: lint .PHONY: lint
base.v: base.m4 include ../common.makefile
m4 -P --synclines base.m4 | awk -v filename=base.m4 '/^#line/ {printf("`line %s %d 0\n", filename, $$2); next} {print}' > base.v base.v: base.v.m4
lint: base.v lint: base.v
verilator --lint-only base.v -I../spi -I../control_loop -I../waveform verilator --lint-only base.v -I../spi -I../control_loop -I../waveform

View File

@ -135,7 +135,7 @@ m4_define(m4_adc_switch, ⟨
`include "control_loop_cmds.vh" `include "control_loop_cmds.vh"
module base #( module base #(
parameter DAC_PORTS = 2, parameter DAC_PORTS = 2,
`define DAC_PORTS_CONTROL_LOOP (DAC_PORTS + 1) m4_define(DAC_PORTS_CONTROL_LOOP, (DAC_PORTS + 1))
parameter DAC_NUM = 8, parameter DAC_NUM = 8,
parameter DAC_WID = 24, parameter DAC_WID = 24,
@ -156,7 +156,7 @@ module base #(
parameter WF_RAM_WORD_INCR = 2, parameter WF_RAM_WORD_INCR = 2,
parameter ADC_PORTS = 1, parameter ADC_PORTS = 1,
`define ADC_PORTS_CONTROL_LOOP (ADC_PORTS + 1) m4_define(ADC_PORTS_CONTROL_LOOP, (ADC_PORTS + 1))
parameter ADC_NUM = 8, parameter ADC_NUM = 8,
/* Three types of ADC. For now assume that their electronics /* Three types of ADC. For now assume that their electronics
* are similar enough, just need different numbers for the width. * are similar enough, just need different numbers for the width.
@ -179,8 +179,8 @@ module base #(
parameter CL_CONSTS_FRAC = 43, parameter CL_CONSTS_FRAC = 43,
parameter CL_CONSTS_SIZ = 7, parameter CL_CONSTS_SIZ = 7,
parameter CL_DELAY_WID = 16, parameter CL_DELAY_WID = 16,
`define CL_CONSTS_WID (CL_CONSTS_WHOLE + CL_CONSTS_FRAC) m4_define(CL_CONSTS_WID, (CL_CONSTS_WHOLE + CL_CONSTS_FRAC))
`define CL_DATA_WID `CL_CONSTS_WID m4_define(CL_DATA_WID, CL_CONSTS_WID)
parameter CL_READ_DAC_DELAY = 5, parameter CL_READ_DAC_DELAY = 5,
parameter CL_CYCLE_COUNT_WID = 18 parameter CL_CYCLE_COUNT_WID = 18
) ( ) (
@ -195,7 +195,7 @@ module base #(
input [ADC_NUM-1:0] adc_sdo, input [ADC_NUM-1:0] adc_sdo,
output [ADC_NUM-1:0] adc_sck, output [ADC_NUM-1:0] adc_sck,
m4_dac_wires(`DAC_PORTS_CONTROL_LOOP, 0), m4_dac_wires(DAC_PORTS_CONTROL_LOOP, 0),
m4_dac_wires(DAC_PORTS, 1), m4_dac_wires(DAC_PORTS, 1),
m4_dac_wires(DAC_PORTS, 2), m4_dac_wires(DAC_PORTS, 2),
m4_dac_wires(DAC_PORTS, 3), m4_dac_wires(DAC_PORTS, 3),
@ -204,7 +204,7 @@ module base #(
m4_dac_wires(DAC_PORTS, 6), m4_dac_wires(DAC_PORTS, 6),
m4_dac_wires(DAC_PORTS, 7), m4_dac_wires(DAC_PORTS, 7),
input [`ADC_PORTS_CONTROL_LOOP-1:0] adc_sel_0, input [ADC_PORTS_CONTROL_LOOP-1:0] adc_sel_0,
m4_adc_wires(ADC_TYPE1_WID, 0), m4_adc_wires(ADC_TYPE1_WID, 0),
m4_adc_wires(ADC_TYPE1_WID, 1), m4_adc_wires(ADC_TYPE1_WID, 1),
@ -217,8 +217,8 @@ module base #(
output cl_in_loop, output cl_in_loop,
input [`CONTROL_LOOP_CMD_WIDTH-1:0] cl_cmd, input [`CONTROL_LOOP_CMD_WIDTH-1:0] cl_cmd,
input [`CL_DATA_WID-1:0] cl_word_in, input [CL_DATA_WID-1:0] cl_word_in,
output reg [`CL_DATA_WID-1:0] cl_word_out, output reg [CL_DATA_WID-1:0] cl_word_out,
input cl_start_cmd, input cl_start_cmd,
output reg cl_finish_cmd output reg cl_finish_cmd
); );
@ -226,7 +226,7 @@ module base #(
wire [ADC_NUM-1:0] adc_conv_L; wire [ADC_NUM-1:0] adc_conv_L;
assign adc_conv = ~adc_conv_L; assign adc_conv = ~adc_conv_L;
m4_dac_switch(`DAC_PORTS_CONTROL_LOOP, 0); m4_dac_switch(DAC_PORTS_CONTROL_LOOP, 0);
m4_dac_switch(DAC_PORTS, 1); m4_dac_switch(DAC_PORTS, 1);
m4_dac_switch(DAC_PORTS, 2); m4_dac_switch(DAC_PORTS, 2);
m4_dac_switch(DAC_PORTS, 3); m4_dac_switch(DAC_PORTS, 3);
@ -237,14 +237,14 @@ m4_dac_switch(DAC_PORTS, 7);
/* 1st adc is Type 1 (18 bit) */ /* 1st adc is Type 1 (18 bit) */
wire [`ADC_PORTS_CONTROL_LOOP-1:0] adc_conv_L_port_0; wire [ADC_PORTS_CONTROL_LOOP-1:0] adc_conv_L_port_0;
wire [`ADC_PORTS_CONTROL_LOOP-1:0] adc_sdo_port_0; wire [ADC_PORTS_CONTROL_LOOP-1:0] adc_sdo_port_0;
wire [`ADC_PORTS_CONTROL_LOOP-1:0] adc_sck_port_0; wire [ADC_PORTS_CONTROL_LOOP-1:0] adc_sck_port_0;
wire [`ADC_PORTS_CONTROL_LOOP-1:0] adc_mosi_port_0_unassigned; wire [ADC_PORTS_CONTROL_LOOP-1:0] adc_mosi_port_0_unassigned;
wire adc_mosi_unassigned; wire adc_mosi_unassigned;
spi_switch #( spi_switch #(
.PORTS(`ADC_PORTS_CONTROL_LOOP) .PORTS(ADC_PORTS_CONTROL_LOOP)
) switch_adc_0 ( ) switch_adc_0 (
.select(adc_sel_0), .select(adc_sel_0),
.mosi(adc_mosi_unassigned), .mosi(adc_mosi_unassigned),
@ -327,4 +327,3 @@ m4_adc_switch(ADC_TYPE1_WID, 6);
m4_adc_switch(ADC_TYPE1_WID, 7); m4_adc_switch(ADC_TYPE1_WID, 7);
endmodule endmodule
`undefineall

View File

@ -0,0 +1,3 @@
# Generate verilog from m4 file
%.v: %.v.m4
m4 -P --synclines $< | awk -v filename=$< '/^#line/ {printf("`line %d %s 0\n", $$2, filename); next} {print}' > $@

View File

@ -1,11 +1,14 @@
# Makefile for tests and hardware verification. # Makefile for tests and hardware verification.
.PHONY: test clean .PHONY: test clean codegen all
all: test codegen
####### Tests ######## ####### Tests ########
COMMON_CPP = control_loop_math_implementation.cpp COMMON_CPP = control_loop_math_implementation.cpp
COMMON= ${COMMON_CPP} control_loop_math_implementation.h COMMON= ${COMMON_CPP} control_loop_math_implementation.h
control_loop_math_verilog = control_loop_math.v boothmul.v intsat.v sign_extend.v
CONSTS_FRAC=43 CONSTS_FRAC=43
E_WID=21 E_WID=21
@ -16,7 +19,7 @@ clean:
rm -rf obj_dir *.fst rm -rf obj_dir *.fst
obj_dir/Vcontrol_loop_math.mk: control_loop_math_sim.cpp ${COMMON} \ obj_dir/Vcontrol_loop_math.mk: control_loop_math_sim.cpp ${COMMON} \
control_loop_math.v ${control_loop_math_verilog}
verilator --cc --exe -Wall --trace --trace-fst \ verilator --cc --exe -Wall --trace --trace-fst \
--top-module control_loop_math \ --top-module control_loop_math \
-GCONSTS_FRAC=${CONSTS_FRAC} -DDEBUG_CONTROL_LOOP_MATH \ -GCONSTS_FRAC=${CONSTS_FRAC} -DDEBUG_CONTROL_LOOP_MATH \
@ -31,7 +34,9 @@ obj_dir/Vcontrol_loop_sim_top.mk: control_loop_sim.cpp ${COMMON} \
adc_sim.v dac_sim.v \ adc_sim.v dac_sim.v \
../spi/spi_master_ss.v \ ../spi/spi_master_ss.v \
../spi/spi_slave_no_write.v \ ../spi/spi_slave_no_write.v \
control_loop_sim_top.v control_loop_sim_top.v control_loop_sim_top.v control_loop_sim_top.v \
control_loop_cmds.vh control_loop.v \
${control_loop_math_verilog}
verilator --cc --exe -Wall --trace --trace-fst \ verilator --cc --exe -Wall --trace --trace-fst \
--top-module control_loop_sim_top \ --top-module control_loop_sim_top \
-GCONSTS_FRAC=${CONSTS_FRAC} \ -GCONSTS_FRAC=${CONSTS_FRAC} \
@ -45,6 +50,10 @@ obj_dir/Vcontrol_loop_sim_top: obj_dir/Vcontrol_loop_sim_top.mk control_loop_cmd
####### Codegen ######## ####### Codegen ########
control_loop_cmds.h: control_loop_cmds.vh include ../common.makefile
codegen: control_loop_cmds.h boothmul.v control_loop_math.v control_loop.v control_loop_cmds.vh
control_loop_cmds.vh: control_loop_cmds.m4
m4 -P control_loop_cmds.vh.m4 > control_loop_cmds.vh
control_loop_cmds.h: control_loop_cmds.m4
echo '#pragma once' > control_loop_cmds.h echo '#pragma once' > control_loop_cmds.h
sed 's/`define/#define/g; s/`//g' control_loop_cmds.vh >> control_loop_cmds.h m4 -P control_loop_cmds.h.m4 >> control_loop_cmds.h

View File

@ -1,3 +1,5 @@
m4_changequote(`⟨', `⟩')
m4_changecom(⟨/*⟩, ⟨*/⟩)
/* Booth Multiplication v1.0 /* Booth Multiplication v1.0
* Written by Peter McGoron, 2022. * Written by Peter McGoron, 2022.
* *
@ -28,11 +30,12 @@ module boothmul
input arm, input arm,
input [A1_LEN-1:0] a1, input [A1_LEN-1:0] a1,
input [A2_LEN-1:0] a2, input [A2_LEN-1:0] a2,
output [A1_LEN+A2_LEN-1:0] outn, m4_define(M4_OUT_LEN, (A1_LEN + A2_LEN))
output [M4_OUT_LEN-1:0] outn,
`ifdef DEBUG `ifdef DEBUG
output [A1_LEN+A2_LEN+1:0] debug_a, output [M4_OUT_LEN+1:0] debug_a,
output [A1_LEN+A2_LEN+1:0] debug_s, output [M4_OUT_LEN+1:0] debug_s,
output [A1_LEN+A2_LEN+1:0] debug_p, output [M4_OUT_LEN+1:0] debug_p,
output [A2LEN_SIZ-1:0] debug_state, output [A2LEN_SIZ-1:0] debug_state,
`endif `endif
output reg fin output reg fin
@ -42,8 +45,7 @@ module boothmul
* Booth Parameters * Booth Parameters
**********************/ **********************/
`define OUT_LEN (A1_LEN + A2_LEN) m4_define(M4_REG_LEN, (M4_OUT_LEN + 2))
`define REG_LEN (`OUT_LEN + 2)
/* The Booth multiplication algorithm is a sequential algorithm for /* The Booth multiplication algorithm is a sequential algorithm for
* twos-compliment integers. * twos-compliment integers.
@ -71,24 +73,24 @@ module boothmul
reg [A1_LEN-1:0] a1_reg; reg [A1_LEN-1:0] a1_reg;
wire [`REG_LEN-1:0] a; wire [M4_REG_LEN-1:0] a;
assign a[A2_LEN:0] = 0; assign a[A2_LEN:0] = 0;
assign a[`REG_LEN-2:A2_LEN+1] = a1_reg; assign a[M4_REG_LEN-2:A2_LEN+1] = a1_reg;
assign a[`REG_LEN-1] = a1_reg[A1_LEN-1]; assign a[M4_REG_LEN-1] = a1_reg[A1_LEN-1];
wire signed [`REG_LEN-1:0] a_signed; wire signed [M4_REG_LEN-1:0] a_signed;
assign a_signed = a; assign a_signed = a;
wire [`REG_LEN-1:0] s; wire [M4_REG_LEN-1:0] s;
assign s[A2_LEN:0] = 0; assign s[A2_LEN:0] = 0;
assign s[`REG_LEN-1:A2_LEN+1] = ~{a1_reg[A1_LEN-1],a1_reg} + 1; assign s[M4_REG_LEN-1:A2_LEN+1] = ~{a1_reg[A1_LEN-1],a1_reg} + 1;
wire signed [`REG_LEN-1:0] s_signed; wire signed [M4_REG_LEN-1:0] s_signed;
assign s_signed = s; assign s_signed = s;
reg [`REG_LEN-1:0] p; reg [M4_REG_LEN-1:0] p;
wire signed [`REG_LEN-1:0] p_signed; wire signed [M4_REG_LEN-1:0] p_signed;
assign p_signed = p; assign p_signed = p;
assign outn = p[`REG_LEN-2:1]; assign outn = p[M4_REG_LEN-2:1];
/********************** /**********************
* Loop Implementation * Loop Implementation
@ -110,7 +112,7 @@ always @ (posedge clk) begin
end else if (loop_accul == 0) begin end else if (loop_accul == 0) begin
p[0] <= 0; p[0] <= 0;
p[A2_LEN:1] <= a2; p[A2_LEN:1] <= a2;
p[`REG_LEN-1:A2_LEN+1] <= 0; p[M4_REG_LEN-1:A2_LEN+1] <= 0;
a1_reg <= a1; a1_reg <= a1;
@ -142,4 +144,3 @@ end
`endif `endif
endmodule endmodule
`undefineall

View File

@ -1,4 +1,7 @@
`include "control_loop_cmds.vh" m4_changequote(`⟨', `⟩')
m4_changecom(⟨/*⟩, ⟨*/⟩)
m4_define(generate_macro, ⟨m4_define(M4_$1, $2)⟩)
m4_include(control_loop_cmds.m4)
module control_loop module control_loop
#( #(
@ -17,9 +20,9 @@ module control_loop
parameter CONSTS_WHOLE = 21, parameter CONSTS_WHOLE = 21,
parameter CONSTS_FRAC = 43, parameter CONSTS_FRAC = 43,
parameter CONSTS_SIZ = 7, parameter CONSTS_SIZ = 7,
`define CONSTS_WID (CONSTS_WHOLE + CONSTS_FRAC) m4_define(M4_CONSTS_WID, (CONSTS_WHOLE + CONSTS_FRAC))
parameter DELAY_WID = 16, parameter DELAY_WID = 16,
`define DATA_WID `CONSTS_WID m4_define(M4_DATA_WID, M4_CONSTS_WID)
parameter READ_DAC_DELAY = 5, parameter READ_DAC_DELAY = 5,
parameter CYCLE_COUNT_WID = 18, parameter CYCLE_COUNT_WID = 18,
parameter DAC_WID = 24, parameter DAC_WID = 24,
@ -29,7 +32,7 @@ module control_loop
*/ */
parameter DAC_WID_SIZ = 5, parameter DAC_WID_SIZ = 5,
parameter DAC_DATA_WID = 20, parameter DAC_DATA_WID = 20,
`define E_WID (DAC_DATA_WID + 1) m4_define(M4_E_WID, (DAC_DATA_WID + 1))
parameter DAC_POLARITY = 0, parameter DAC_POLARITY = 0,
parameter DAC_PHASE = 1, parameter DAC_PHASE = 1,
parameter DAC_CYCLE_HALF_WAIT = 10, parameter DAC_CYCLE_HALF_WAIT = 10,
@ -50,9 +53,9 @@ module control_loop
output adc_sck, output adc_sck,
/* Hacky ad-hoc read-write interface. */ /* Hacky ad-hoc read-write interface. */
input [`CONTROL_LOOP_CMD_WIDTH-1:0] cmd, input [M4_CONTROL_LOOP_CMD_WIDTH-1:0] cmd,
input [`DATA_WID-1:0] word_in, input [M4_DATA_WID-1:0] word_in,
output reg [`DATA_WID-1:0] word_out, output reg [M4_DATA_WID-1:0] word_out,
input start_cmd, input start_cmd,
output reg finish_cmd output reg finish_cmd
); );
@ -125,12 +128,12 @@ reg signed [ADC_WID-1:0] setpt = 0;
reg signed [ADC_WID-1:0] setpt_buffer = 0; reg signed [ADC_WID-1:0] setpt_buffer = 0;
/* Integral parameter */ /* Integral parameter */
reg signed [`CONSTS_WID-1:0] cl_I_reg = 0; reg signed [M4_CONSTS_WID-1:0] cl_I_reg = 0;
reg signed [`CONSTS_WID-1:0] cl_I_reg_buffer = 0; reg signed [M4_CONSTS_WID-1:0] cl_I_reg_buffer = 0;
/* Proportional parameter */ /* Proportional parameter */
reg signed [`CONSTS_WID-1:0] cl_p_reg = 0; reg signed [M4_CONSTS_WID-1:0] cl_p_reg = 0;
reg signed [`CONSTS_WID-1:0] cl_p_reg_buffer = 0; reg signed [M4_CONSTS_WID-1:0] cl_p_reg_buffer = 0;
/* Delay parameter (to make the loop run slower) */ /* Delay parameter (to make the loop run slower) */
reg [DELAY_WID-1:0] dely = 0; reg [DELAY_WID-1:0] dely = 0;
@ -143,11 +146,11 @@ reg running = 0;
reg signed [DAC_DATA_WID-1:0] stored_dac_val = 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] last_timer = 0;
reg [CYCLE_COUNT_WID-1:0] counting_timer = 0; reg [CYCLE_COUNT_WID-1:0] counting_timer = 0;
reg [`CONSTS_WID-1:0] adjval_prev = 0; reg [M4_CONSTS_WID-1:0] adjval_prev = 0;
reg signed [`E_WID-1:0] err_prev = 0; reg signed [M4_E_WID-1:0] err_prev = 0;
wire signed [`E_WID-1:0] e_cur; wire signed [M4_E_WID-1:0] e_cur;
wire signed [`CONSTS_WID-1:0] adj_val; wire signed [M4_CONSTS_WID-1:0] adj_val;
wire signed [DAC_DATA_WID-1:0] new_dac_val; wire signed [DAC_DATA_WID-1:0] new_dac_val;
reg arm_math = 0; reg arm_math = 0;
@ -244,78 +247,78 @@ reg dirty_bit = 0;
always @ (posedge clk) begin always @ (posedge clk) begin
if (start_cmd && !finish_cmd) begin if (start_cmd && !finish_cmd) begin
case (cmd) case (cmd)
`CONTROL_LOOP_NOOP: M4_CONTROL_LOOP_NOOP:
finish_cmd <= 1; finish_cmd <= 1;
`CONTROL_LOOP_NOOP | `CONTROL_LOOP_WRITE_BIT: M4_CONTROL_LOOP_NOOP | M4_CONTROL_LOOP_WRITE_BIT:
finish_cmd <= 1; finish_cmd <= 1;
`CONTROL_LOOP_STATUS: begin M4_CONTROL_LOOP_STATUS: begin
word_out[`DATA_WID-1:1] <= 0; word_out[M4_DATA_WID-1:1] <= 0;
word_out[0] <= running; word_out[0] <= running;
finish_cmd <= 1; finish_cmd <= 1;
end end
`CONTROL_LOOP_STATUS | `CONTROL_LOOP_WRITE_BIT: M4_CONTROL_LOOP_STATUS | M4_CONTROL_LOOP_WRITE_BIT:
if (write_control) begin if (write_control) begin
running <= word_in[0]; running <= word_in[0];
finish_cmd <= 1; finish_cmd <= 1;
dirty_bit <= 1; dirty_bit <= 1;
end end
`CONTROL_LOOP_SETPT: begin M4_CONTROL_LOOP_SETPT: begin
word_out[`DATA_WID-1:ADC_WID] <= 0; word_out[M4_DATA_WID-1:ADC_WID] <= 0;
word_out[ADC_WID-1:0] <= setpt; word_out[ADC_WID-1:0] <= setpt;
finish_cmd <= 1; finish_cmd <= 1;
end end
`CONTROL_LOOP_SETPT | `CONTROL_LOOP_WRITE_BIT: M4_CONTROL_LOOP_SETPT | M4_CONTROL_LOOP_WRITE_BIT:
if (write_control) begin if (write_control) begin
setpt_buffer <= word_in[ADC_WID-1:0]; setpt_buffer <= word_in[ADC_WID-1:0];
finish_cmd <= 1; finish_cmd <= 1;
dirty_bit <= 1; dirty_bit <= 1;
end end
`CONTROL_LOOP_P: begin M4_CONTROL_LOOP_P: begin
word_out <= cl_p_reg; word_out <= cl_p_reg;
finish_cmd <= 1; finish_cmd <= 1;
end end
`CONTROL_LOOP_P | `CONTROL_LOOP_WRITE_BIT: begin M4_CONTROL_LOOP_P | M4_CONTROL_LOOP_WRITE_BIT: begin
if (write_control) begin if (write_control) begin
cl_p_reg_buffer <= word_in; cl_p_reg_buffer <= word_in;
finish_cmd <= 1; finish_cmd <= 1;
dirty_bit <= 1; dirty_bit <= 1;
end end
end end
`CONTROL_LOOP_I: begin M4_CONTROL_LOOP_I: begin
word_out <= cl_I_reg; word_out <= cl_I_reg;
finish_cmd <= 1; finish_cmd <= 1;
end end
`CONTROL_LOOP_I | `CONTROL_LOOP_WRITE_BIT: begin M4_CONTROL_LOOP_I | M4_CONTROL_LOOP_WRITE_BIT: begin
if (write_control) begin if (write_control) begin
cl_I_reg_buffer <= word_in; cl_I_reg_buffer <= word_in;
finish_cmd <= 1; finish_cmd <= 1;
dirty_bit <= 1; dirty_bit <= 1;
end end
end end
`CONTROL_LOOP_DELAY: begin M4_CONTROL_LOOP_DELAY: begin
word_out[`DATA_WID-1:DELAY_WID] <= 0; word_out[M4_DATA_WID-1:DELAY_WID] <= 0;
word_out[DELAY_WID-1:0] <= dely; word_out[DELAY_WID-1:0] <= dely;
finish_cmd <= 1; finish_cmd <= 1;
end end
`CONTROL_LOOP_DELAY | `CONTROL_LOOP_WRITE_BIT: begin M4_CONTROL_LOOP_DELAY | M4_CONTROL_LOOP_WRITE_BIT: begin
if (write_control) begin if (write_control) begin
dely_buffer <= word_in[DELAY_WID-1:0]; dely_buffer <= word_in[DELAY_WID-1:0];
finish_cmd <= 1; finish_cmd <= 1;
dirty_bit <= 1; dirty_bit <= 1;
end end
end end
`CONTROL_LOOP_ERR: begin M4_CONTROL_LOOP_ERR: begin
word_out[`DATA_WID-1:`E_WID] <= 0; word_out[M4_DATA_WID-1:M4_E_WID] <= 0;
word_out[`E_WID-1:0] <= err_prev; word_out[M4_E_WID-1:0] <= err_prev;
finish_cmd <= 1; finish_cmd <= 1;
end end
`CONTROL_LOOP_Z: begin M4_CONTROL_LOOP_Z: begin
word_out[`DATA_WID-1:DAC_DATA_WID] <= 0; word_out[M4_DATA_WID-1:DAC_DATA_WID] <= 0;
word_out[DAC_DATA_WID-1:0] <= stored_dac_val; word_out[DAC_DATA_WID-1:0] <= stored_dac_val;
finish_cmd <= 1; finish_cmd <= 1;
end end
`CONTROL_LOOP_CYCLES: begin M4_CONTROL_LOOP_CYCLES: begin
word_out[`DATA_WID-1:CYCLE_COUNT_WID] <= 0; word_out[M4_DATA_WID-1:CYCLE_COUNT_WID] <= 0;
word_out[CYCLE_COUNT_WID-1:0] <= last_timer; word_out[CYCLE_COUNT_WID-1:0] <= last_timer;
finish_cmd <= 0; finish_cmd <= 0;
end end
@ -403,4 +406,3 @@ always @ (posedge clk) begin
end end
endmodule endmodule
`undefineall

View File

@ -0,0 +1,11 @@
generate_macro(CONTROL_LOOP_NOOP, 0)
generate_macro(CONTROL_LOOP_STATUS, 1)
generate_macro(CONTROL_LOOP_SETPT, 2)
generate_macro(CONTROL_LOOP_P, 3)
generate_macro(CONTROL_LOOP_I, 4)
generate_macro(CONTROL_LOOP_ERR, 5)
generate_macro(CONTROL_LOOP_Z, 6)
generate_macro(CONTROL_LOOP_CYCLES, 7)
generate_macro(CONTROL_LOOP_DELAY, 8)
generate_macro(CONTROL_LOOP_CMD_WIDTH, 8)
generate_macro(CONTROL_LOOP_WRITE_BIT, (1 << (M4_CONTROL_LOOP_CMD_WIDTH-1)))

View File

@ -1,11 +0,0 @@
`define CONTROL_LOOP_NOOP 0
`define CONTROL_LOOP_STATUS 1
`define CONTROL_LOOP_SETPT 2
`define CONTROL_LOOP_P 3
`define CONTROL_LOOP_I 4
`define CONTROL_LOOP_ERR 5
`define CONTROL_LOOP_Z 6
`define CONTROL_LOOP_CYCLES 7
`define CONTROL_LOOP_DELAY 8
`define CONTROL_LOOP_CMD_WIDTH 8
`define CONTROL_LOOP_WRITE_BIT (1 << (`CONTROL_LOOP_CMD_WIDTH-1))

View File

@ -1,3 +1,5 @@
m4_changequote(`⟨', `⟩')
m4_changecom(⟨/*⟩, ⟨*/⟩)
/*************** Precision ************** /*************** Precision **************
* The control loop is designed around these values, but generally * The control loop is designed around these values, but generally
* does not hardcode them. * does not hardcode them.
@ -25,18 +27,18 @@
module control_loop_math #( module control_loop_math #(
parameter CONSTS_WHOLE = 21, parameter CONSTS_WHOLE = 21,
parameter CONSTS_FRAC = 43, parameter CONSTS_FRAC = 43,
`define CONSTS_WID (CONSTS_WHOLE + CONSTS_FRAC) m4_define(M4_CONSTS_WID, (CONSTS_WHOLE + CONSTS_FRAC))
parameter CONSTS_SIZ=7, parameter CONSTS_SIZ=7,
parameter ADC_WID = 18, parameter ADC_WID = 18,
parameter [`CONSTS_WID-1:0] SEC_PER_CYCLE = 'b10101011110011000, parameter [M4_CONSTS_WID-1:0] SEC_PER_CYCLE = 'b10101011110011000,
/* The conversion between the ADC bit (20/2**18) and DAC bit (20.48/2**20) /* The conversion between the ADC bit (20/2**18) and DAC bit (20.48/2**20)
* is 0.256. * is 0.256.
*/ */
parameter [`CONSTS_WID-1:0] ADC_TO_DAC = 64'b0100000110001001001101110100101111000110101, parameter [M4_CONSTS_WID-1:0] ADC_TO_DAC = 64'b0100000110001001001101110100101111000110101,
parameter CYCLE_COUNT_WID = 18, parameter CYCLE_COUNT_WID = 18,
parameter DAC_WID = 20 parameter DAC_WID = 20
`define E_WID (DAC_WID + 1) m4_define(M4_E_WID, (DAC_WID + 1))
) ( ) (
input clk, input clk,
input arm, input arm,
@ -44,23 +46,23 @@ module control_loop_math #(
input signed [ADC_WID-1:0] setpt, input signed [ADC_WID-1:0] setpt,
input signed [ADC_WID-1:0] measured, input signed [ADC_WID-1:0] measured,
input signed [`CONSTS_WID-1:0] cl_P, input signed [M4_CONSTS_WID-1:0] cl_P,
input signed [`CONSTS_WID-1:0] cl_I, input signed [M4_CONSTS_WID-1:0] cl_I,
input signed [CYCLE_COUNT_WID-1:0] cycles, input signed [CYCLE_COUNT_WID-1:0] cycles,
input signed [`E_WID-1:0] e_prev, input signed [M4_E_WID-1:0] e_prev,
input signed [`CONSTS_WID-1:0] adjval_prev, input signed [M4_CONSTS_WID-1:0] adjval_prev,
input signed [DAC_WID-1:0] stored_dac_val, input signed [DAC_WID-1:0] stored_dac_val,
`ifdef DEBUG_CONTROL_LOOP_MATH `ifdef DEBUG_CONTROL_LOOP_MATH
output reg [`CONSTS_WID-1:0] dt_reg, output reg [M4_CONSTS_WID-1:0] dt_reg,
output reg [`CONSTS_WID-1:0] idt_reg, output reg [M4_CONSTS_WID-1:0] idt_reg,
output reg [`CONSTS_WID-1:0] epidt_reg, output reg [M4_CONSTS_WID-1:0] epidt_reg,
output reg [`CONSTS_WID-1:0] ep_reg, output reg [M4_CONSTS_WID-1:0] ep_reg,
`endif `endif
output reg signed [`E_WID-1:0] e_cur, output reg signed [M4_E_WID-1:0] e_cur,
output signed [DAC_WID-1:0] new_dac_val, output signed [DAC_WID-1:0] new_dac_val,
output signed [`CONSTS_WID-1:0] adj_val output signed [M4_CONSTS_WID-1:0] adj_val
); );
/******* /*******
@ -69,16 +71,16 @@ module control_loop_math #(
* to be a 64 bit output, according to fixed-point rules. * to be a 64 bit output, according to fixed-point rules.
*/ */
reg signed [`CONSTS_WID-1:0] a1; reg signed [M4_CONSTS_WID-1:0] a1;
reg signed [`CONSTS_WID-1:0] a2; reg signed [M4_CONSTS_WID-1:0] a2;
/* verilator lint_off UNUSED */ /* verilator lint_off UNUSED */
wire signed [`CONSTS_WID+`CONSTS_WID-1:0] out_untrunc; wire signed [M4_CONSTS_WID+M4_CONSTS_WID-1:0] out_untrunc;
wire mul_fin; wire mul_fin;
reg mul_arm = 0; reg mul_arm = 0;
boothmul #( boothmul #(
.A1_LEN(`CONSTS_WID), .A1_LEN(M4_CONSTS_WID),
.A2_LEN(`CONSTS_WID), .A2_LEN(M4_CONSTS_WID),
.A2LEN_SIZ(CONSTS_SIZ) .A2LEN_SIZ(CONSTS_SIZ)
) multiplier ( ) multiplier (
.a1(a1), .a1(a1),
@ -95,11 +97,11 @@ boothmul #(
* Q(2X).Y * Q(2X).Y
*/ */
`define OUT_RTRUNC_WID (`CONSTS_WID+`CONSTS_WID-CONSTS_FRAC) m4_define(M4_OUT_RTRUNC_WID, (M4_CONSTS_WID+M4_CONSTS_WID-CONSTS_FRAC))
wire signed [`OUT_RTRUNC_WID-1:0] out_rtrunc wire signed [M4_OUT_RTRUNC_WID-1:0] out_rtrunc
= out_untrunc[`CONSTS_WID+`CONSTS_WID-1:CONSTS_FRAC]; = out_untrunc[M4_CONSTS_WID+M4_CONSTS_WID-1:CONSTS_FRAC];
wire signed [`CONSTS_WID-1:0] mul_out; wire signed [M4_CONSTS_WID-1:0] mul_out;
/*************************** /***************************
* Saturate higher X bits away. * Saturate higher X bits away.
@ -107,7 +109,7 @@ wire signed [`CONSTS_WID-1:0] mul_out;
*/ */
intsat #( intsat #(
.IN_LEN(`OUT_RTRUNC_WID), .IN_LEN(M4_OUT_RTRUNC_WID),
.LTRUNC(CONSTS_WHOLE) .LTRUNC(CONSTS_WHOLE)
) multiplier_saturate ( ) multiplier_saturate (
.inp(out_rtrunc), .inp(out_rtrunc),
@ -118,11 +120,11 @@ intsat #(
* Safely get rid of high bit in addition. * Safely get rid of high bit in addition.
************************/ ************************/
reg signed [`CONSTS_WID+1-1:0] add_sat; reg signed [M4_CONSTS_WID+1-1:0] add_sat;
wire signed [`CONSTS_WID-1:0] saturated_add; wire signed [M4_CONSTS_WID-1:0] saturated_add;
intsat #( intsat #(
.IN_LEN(`CONSTS_WID + 1), .IN_LEN(M4_CONSTS_WID + 1),
.LTRUNC(1) .LTRUNC(1)
) addition_saturate ( ) addition_saturate (
.inp(add_sat), .inp(add_sat),
@ -168,8 +170,8 @@ localparam CALCULATE_NEW_DAC_VALUE_PART_2 = 11;
localparam WAIT_ON_DISARM = 8; localparam WAIT_ON_DISARM = 8;
reg [4:0] state = WAIT_ON_ARM; reg [4:0] state = WAIT_ON_ARM;
reg signed [`CONSTS_WID+1-1:0] tmpstore = 0; reg signed [M4_CONSTS_WID+1-1:0] tmpstore = 0;
wire signed [`CONSTS_WID-1:0] tmpstore_view = tmpstore[`CONSTS_WID-1:0]; wire signed [M4_CONSTS_WID-1:0] tmpstore_view = tmpstore[M4_CONSTS_WID-1:0];
always @ (posedge clk) begin always @ (posedge clk) begin
@ -185,8 +187,8 @@ always @ (posedge clk) begin
end end
CALCULATE_ERR: begin CALCULATE_ERR: begin
/* Sign-extend */ /* Sign-extend */
a1[`CONSTS_WID-1:CONSTS_FRAC + ADC_WID + 1] <= a1[M4_CONSTS_WID-1:CONSTS_FRAC + ADC_WID + 1] <=
{(`CONSTS_WID-(CONSTS_FRAC + ADC_WID + 1)){a1[ADC_WID+1-1+CONSTS_FRAC]}}; {(M4_CONSTS_WID-(CONSTS_FRAC + ADC_WID + 1)){a1[ADC_WID+1-1+CONSTS_FRAC]}};
a2 <= ADC_TO_DAC; a2 <= ADC_TO_DAC;
mul_arm <= 1; mul_arm <= 1;
state <= CALCULATE_DAC_E; state <= CALCULATE_DAC_E;
@ -195,7 +197,7 @@ always @ (posedge clk) begin
if (mul_fin) begin if (mul_fin) begin
/* Discard other bits. This works without saturation because /* Discard other bits. This works without saturation because
* CONSTS_WHOLE = E_WID. */ * CONSTS_WHOLE = E_WID. */
e_cur <= mul_out[`CONSTS_WID-1:CONSTS_FRAC]; e_cur <= mul_out[M4_CONSTS_WID-1:CONSTS_FRAC];
a1 <= SEC_PER_CYCLE; a1 <= SEC_PER_CYCLE;
/* No sign extension, cycles is positive */ /* No sign extension, cycles is positive */
@ -228,7 +230,7 @@ always @ (posedge clk) begin
idt_reg <= mul_out; idt_reg <= mul_out;
`endif `endif
a2 <= {{(CONSTS_WHOLE-`E_WID){e_cur[`E_WID-1]}},e_cur, {(CONSTS_FRAC){1'b0}}}; a2 <= {{(CONSTS_WHOLE-M4_E_WID){e_cur[M4_E_WID-1]}},e_cur, {(CONSTS_FRAC){1'b0}}};
state <= CALCULATE_EPIDT; state <= CALCULATE_EPIDT;
end end
CALCULATE_EPIDT: CALCULATE_EPIDT:
@ -237,14 +239,14 @@ always @ (posedge clk) begin
mul_arm <= 1; mul_arm <= 1;
end else if (mul_fin) begin end else if (mul_fin) begin
mul_arm <= 0; mul_arm <= 0;
tmpstore <= {mul_out[`CONSTS_WID-1],mul_out}; tmpstore <= {mul_out[M4_CONSTS_WID-1],mul_out};
`ifdef DEBUG_CONTROL_LOOP_MATH `ifdef DEBUG_CONTROL_LOOP_MATH
epidt_reg <= mul_out; epidt_reg <= mul_out;
`endif `endif
a1 <= cl_P; a1 <= cl_P;
a2 <= {{(CONSTS_WHOLE-`E_WID){e_prev[`E_WID-1]}},e_prev, {(CONSTS_FRAC){1'b0}}}; a2 <= {{(CONSTS_WHOLE-M4_E_WID){e_prev[M4_E_WID-1]}},e_prev, {(CONSTS_FRAC){1'b0}}};
state <= CALCULATE_EP; state <= CALCULATE_EP;
end end
CALCULATE_EP: CALCULATE_EP:
@ -268,7 +270,7 @@ always @ (posedge clk) begin
state <= CALCULATE_NEW_DAC_VALUE_PART_1; state <= CALCULATE_NEW_DAC_VALUE_PART_1;
end end
CALCULATE_NEW_DAC_VALUE_PART_1: begin CALCULATE_NEW_DAC_VALUE_PART_1: begin
adj_sat <= saturated_add[`CONSTS_WID-1:CONSTS_FRAC]; adj_sat <= saturated_add[M4_CONSTS_WID-1:CONSTS_FRAC];
adj_val <= saturated_add; adj_val <= saturated_add;
state <= CALCULATE_NEW_DAC_VALUE_PART_2; state <= CALCULATE_NEW_DAC_VALUE_PART_2;
end end
@ -296,4 +298,3 @@ end
`endif `endif
endmodule endmodule
`undefineall

View File

@ -19,6 +19,7 @@ module control_loop_sim_top #(
parameter DELAY_WID = 16 parameter DELAY_WID = 16
)( )(
input clk, input clk,
output in_loop,
output [DAC_DATA_WID-1:0] curset, output [DAC_DATA_WID-1:0] curset,
output dac_err, output dac_err,
@ -104,6 +105,7 @@ control_loop #(
.DAC_PHASE(DAC_PHASE) .DAC_PHASE(DAC_PHASE)
) cloop ( ) cloop (
.clk(clk), .clk(clk),
.in_loop(in_loop),
.dac_mosi(dac_mosi), .dac_mosi(dac_mosi),
.dac_miso(dac_miso), .dac_miso(dac_miso),
.dac_ss_L(dac_ss_L), .dac_ss_L(dac_ss_L),