2022-10-28 17:31:23 -04:00
|
|
|
|
/*************** Precision **************
|
|
|
|
|
* The control loop is designed around these values, but generally
|
|
|
|
|
* does not hardcode them.
|
|
|
|
|
*
|
|
|
|
|
* Since α and P are precalculated outside of the loop, their
|
|
|
|
|
* conversion to numbers the loop understands is done outside of
|
|
|
|
|
* the loop and in the kernel.
|
|
|
|
|
*
|
|
|
|
|
* The 18-bit ADC is twos-compliment, -10.24V to 10.24V,
|
|
|
|
|
* with 78μV per increment.
|
|
|
|
|
* The 20-bit DAC is twos-compliment, -10V to 10V.
|
|
|
|
|
*
|
|
|
|
|
* The `P` constant has a minimum value of 1e-7 with a precision
|
|
|
|
|
* of 1e-9, and a maxmimum value of 1.
|
|
|
|
|
*
|
|
|
|
|
* The `I` constant has a minimum value of 1e-4 with a precision
|
|
|
|
|
* of 1e-6 and a maximum value of 100.
|
|
|
|
|
*
|
|
|
|
|
* Δt is cycles/100MHz. This makes Δt at least 10 ns, with a
|
|
|
|
|
* maximum of 1 ms.
|
|
|
|
|
*
|
2022-11-13 18:03:55 -05:00
|
|
|
|
* [1 : sign][20: whole][43: fractional]
|
2022-10-28 17:31:23 -04:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
module control_loop_math #(
|
2022-11-13 18:03:55 -05:00
|
|
|
|
parameter CONSTS_WHOLE = 21,
|
|
|
|
|
parameter CONSTS_FRAC = 43,
|
2022-10-30 15:37:45 -04:00
|
|
|
|
`define CONSTS_WID (CONSTS_WHOLE + CONSTS_FRAC)
|
2022-11-13 18:03:55 -05:00
|
|
|
|
parameter CONSTS_SIZ=7,
|
2022-11-12 01:44:30 -05:00
|
|
|
|
|
2022-10-28 17:31:23 -04:00
|
|
|
|
parameter ADC_WID = 18,
|
2022-11-13 18:03:55 -05:00
|
|
|
|
parameter [`CONSTS_WID-1:0] SEC_PER_CYCLE = 'b10101011110011000,
|
|
|
|
|
parameter CYCLE_COUNT_WID = 18
|
2022-11-11 21:57:58 -05:00
|
|
|
|
`define E_WID (ADC_WID + 1)
|
2022-10-28 17:31:23 -04:00
|
|
|
|
) (
|
|
|
|
|
input clk,
|
|
|
|
|
input arm,
|
2022-11-11 21:57:58 -05:00
|
|
|
|
output reg finished,
|
2022-10-28 17:31:23 -04:00
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
input signed [ADC_WID-1:0] setpt,
|
|
|
|
|
input signed [ADC_WID-1:0] measured,
|
|
|
|
|
input signed [`CONSTS_WID-1:0] cl_P,
|
|
|
|
|
input signed [`CONSTS_WID-1:0] cl_I,
|
|
|
|
|
input signed [CYCLE_COUNT_WID-1:0] cycles,
|
|
|
|
|
input signed [`E_WID-1:0] e_prev,
|
|
|
|
|
input signed [`CONSTS_WID-1:0] adjval_prev,
|
|
|
|
|
|
|
|
|
|
`ifdef DEBUG_CONTROL_LOOP_MATH
|
|
|
|
|
output reg [`CONSTS_WID-1:0] dt_reg,
|
|
|
|
|
output reg [`CONSTS_WID-1:0] idt_reg,
|
|
|
|
|
output reg [`CONSTS_WID-1:0] epidt_reg,
|
|
|
|
|
output reg [`CONSTS_WID-1:0] ep_reg,
|
|
|
|
|
`endif
|
|
|
|
|
|
|
|
|
|
output reg signed [`E_WID-1:0] e_cur,
|
|
|
|
|
output signed [`CONSTS_WID-1:0] adj_val
|
2022-10-28 17:31:23 -04:00
|
|
|
|
);
|
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
/*******
|
|
|
|
|
* Multiplier segment.
|
|
|
|
|
* Multiplies two 64 bit numbers and right-saturate + truncates it
|
|
|
|
|
* to be a 64 bit output, according to fixed-point rules.
|
2022-10-28 17:31:23 -04:00
|
|
|
|
*/
|
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
reg signed [`CONSTS_WID-1:0] a1;
|
|
|
|
|
reg signed [`CONSTS_WID-1:0] a2;
|
|
|
|
|
/* verilator lint_off UNUSED */
|
|
|
|
|
wire signed [`CONSTS_WID+`CONSTS_WID-1:0] out_untrunc;
|
|
|
|
|
wire mul_fin;
|
|
|
|
|
reg mul_arm;
|
2022-11-11 21:57:58 -05:00
|
|
|
|
|
2022-10-28 17:31:23 -04:00
|
|
|
|
boothmul #(
|
2022-11-13 18:03:55 -05:00
|
|
|
|
.A1_LEN(`CONSTS_WID),
|
|
|
|
|
.A2_LEN(`CONSTS_WID),
|
|
|
|
|
.A2LEN_SIZ(CONSTS_SIZ)
|
|
|
|
|
) multiplier (
|
|
|
|
|
.a1(a1),
|
|
|
|
|
.a2(a2),
|
2022-10-28 17:31:23 -04:00
|
|
|
|
.clk(clk),
|
2022-11-13 18:03:55 -05:00
|
|
|
|
.outn(out_untrunc),
|
|
|
|
|
.fin(mul_fin),
|
|
|
|
|
.arm(mul_arm)
|
2022-10-28 17:31:23 -04:00
|
|
|
|
);
|
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
/****************************
|
|
|
|
|
* QX.Y * QX.Y = Q(2X).(2Y)
|
|
|
|
|
* This right-truncation gets rid of the lowest Y bits.
|
|
|
|
|
* Q(2X).Y
|
2022-10-28 17:31:23 -04:00
|
|
|
|
*/
|
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
`define OUT_RTRUNC_WID (`CONSTS_WID+`CONSTS_WID-CONSTS_FRAC)
|
|
|
|
|
wire signed [`OUT_RTRUNC_WID-1:0] out_rtrunc
|
|
|
|
|
= out_untrunc[`CONSTS_WID+`CONSTS_WID-1:CONSTS_FRAC];
|
2022-10-28 17:31:23 -04:00
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
wire signed [`CONSTS_WID-1:0] mul_out;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
/***************************
|
|
|
|
|
* Saturate higher X bits away.
|
|
|
|
|
* Q(2X).Y -> QX.Y
|
|
|
|
|
*/
|
2022-10-28 17:31:23 -04:00
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
intsat #(
|
|
|
|
|
.IN_LEN(`OUT_RTRUNC_WID),
|
|
|
|
|
.LTRUNC(CONSTS_WHOLE)
|
|
|
|
|
) multiplier_saturate (
|
|
|
|
|
.inp(out_rtrunc),
|
|
|
|
|
.outp(mul_out)
|
2022-10-28 17:31:23 -04:00
|
|
|
|
);
|
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
/*************************
|
|
|
|
|
* Safely get rid of high bit in addition.
|
|
|
|
|
************************/
|
2022-10-28 17:31:23 -04:00
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
reg signed [`CONSTS_WID+1-1:0] add_sat;
|
|
|
|
|
wire signed [`CONSTS_WID-1:0] saturated_add;
|
2022-11-11 21:57:58 -05:00
|
|
|
|
|
|
|
|
|
intsat #(
|
2022-11-13 18:03:55 -05:00
|
|
|
|
.IN_LEN(`CONSTS_WID + 1),
|
|
|
|
|
.LTRUNC(1)
|
|
|
|
|
) addition_saturate (
|
|
|
|
|
.inp(add_sat),
|
|
|
|
|
.outp(saturated_add)
|
2022-11-11 21:57:58 -05:00
|
|
|
|
);
|
|
|
|
|
|
2022-10-28 17:31:23 -04:00
|
|
|
|
localparam WAIT_ON_ARM = 0;
|
2022-11-13 18:03:55 -05:00
|
|
|
|
localparam WAIT_ON_CALCULATE_DT = 1;
|
|
|
|
|
localparam CALCULATE_IDT = 2;
|
|
|
|
|
localparam CALCULATE_EPIDT = 3;
|
|
|
|
|
localparam CALCULATE_EP = 4;
|
|
|
|
|
localparam CALCULATE_A_PART_1 = 5;
|
|
|
|
|
localparam CALCULATE_A_PART_2 = 6;
|
|
|
|
|
localparam WAIT_ON_DISARM = 7;
|
|
|
|
|
|
|
|
|
|
reg [4:0] state = WAIT_ON_ARM;
|
|
|
|
|
reg signed [`CONSTS_WID+1-1:0] tmpstore = 0;
|
|
|
|
|
wire signed [`CONSTS_WID-1:0] tmpstore_view = tmpstore[`CONSTS_WID-1:0];
|
2022-10-28 17:31:23 -04:00
|
|
|
|
|
|
|
|
|
always @ (posedge clk) begin
|
2022-11-13 18:03:55 -05:00
|
|
|
|
case (state)
|
|
|
|
|
WAIT_ON_ARM:
|
2022-10-28 17:31:23 -04:00
|
|
|
|
if (arm) begin
|
2022-11-13 18:03:55 -05:00
|
|
|
|
e_cur <= setpt - measured;
|
|
|
|
|
|
|
|
|
|
a1 <= SEC_PER_CYCLE;
|
|
|
|
|
/* No sign extension, cycles is positive */
|
|
|
|
|
a2 <= {{(CONSTS_WHOLE - CYCLE_COUNT_WID){1'b0}}, cycles, {(CONSTS_FRAC){1'b0}}};
|
|
|
|
|
mul_arm <= 1;
|
|
|
|
|
state <= WAIT_ON_CALCULATE_DT;
|
|
|
|
|
end else begin
|
|
|
|
|
finished <= 0;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
end
|
2022-11-13 18:03:55 -05:00
|
|
|
|
WAIT_ON_CALCULATE_DT:
|
|
|
|
|
if (mul_fin) begin
|
|
|
|
|
mul_arm <= 0;
|
|
|
|
|
|
|
|
|
|
`ifdef DEBUG_CONTROL_LOOP_MATH
|
|
|
|
|
dt_reg <= mul_out;
|
|
|
|
|
`endif
|
|
|
|
|
|
|
|
|
|
a1 <= mul_out; /* a1 = Δt */
|
|
|
|
|
a2 <= cl_I;
|
|
|
|
|
state <= CALCULATE_IDT;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
end
|
2022-11-13 18:03:55 -05:00
|
|
|
|
CALCULATE_IDT:
|
|
|
|
|
if (!mul_arm) begin
|
|
|
|
|
mul_arm <= 1;
|
|
|
|
|
end else if (mul_fin) begin
|
|
|
|
|
mul_arm <= 0;
|
|
|
|
|
add_sat <= (mul_out + cl_P);
|
|
|
|
|
|
|
|
|
|
`ifdef DEBUG_CONTROL_LOOP_MATH
|
|
|
|
|
idt_reg <= mul_out;
|
|
|
|
|
`endif
|
|
|
|
|
|
|
|
|
|
a2 <= {{(CONSTS_WHOLE-`E_WID){e_cur[`E_WID-1]}},e_cur, {(CONSTS_FRAC){1'b0}}};
|
|
|
|
|
state <= CALCULATE_EPIDT;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
end
|
2022-11-13 18:03:55 -05:00
|
|
|
|
CALCULATE_EPIDT:
|
|
|
|
|
if (!mul_arm) begin
|
|
|
|
|
a1 <= saturated_add;
|
|
|
|
|
mul_arm <= 1;
|
|
|
|
|
end else if (mul_fin) begin
|
|
|
|
|
mul_arm <= 0;
|
|
|
|
|
tmpstore <= {mul_out[`CONSTS_WID-1],mul_out};
|
|
|
|
|
|
|
|
|
|
`ifdef DEBUG_CONTROL_LOOP_MATH
|
|
|
|
|
epidt_reg <= mul_out;
|
|
|
|
|
`endif
|
|
|
|
|
|
|
|
|
|
a1 <= cl_P;
|
|
|
|
|
a2 <= {{(CONSTS_WHOLE-`E_WID){e_prev[`E_WID-1]}},e_prev, {(CONSTS_FRAC){1'b0}}};
|
|
|
|
|
state <= CALCULATE_EP;
|
|
|
|
|
end
|
|
|
|
|
CALCULATE_EP:
|
|
|
|
|
if (!mul_arm) begin
|
|
|
|
|
mul_arm <= 1;
|
|
|
|
|
end else if (mul_fin) begin
|
|
|
|
|
`ifdef DEBUG_CONTROL_LOOP_MATH
|
|
|
|
|
ep_reg <= mul_out;
|
|
|
|
|
`endif
|
|
|
|
|
|
|
|
|
|
mul_arm <= 0;
|
|
|
|
|
add_sat <= (tmpstore_view - mul_out);
|
|
|
|
|
state <= CALCULATE_A_PART_1;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
end
|
2022-11-13 18:03:55 -05:00
|
|
|
|
CALCULATE_A_PART_1: begin
|
|
|
|
|
tmpstore <= saturated_add + adjval_prev;
|
|
|
|
|
state <= CALCULATE_A_PART_2;
|
|
|
|
|
end
|
|
|
|
|
CALCULATE_A_PART_2: begin
|
|
|
|
|
add_sat <= tmpstore;
|
|
|
|
|
state <= WAIT_ON_DISARM;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
end
|
|
|
|
|
WAIT_ON_DISARM: begin
|
2022-11-13 18:03:55 -05:00
|
|
|
|
adj_val <= saturated_add;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
if (!arm) begin
|
|
|
|
|
state <= WAIT_ON_ARM;
|
2022-11-13 18:03:55 -05:00
|
|
|
|
finished <= 0;
|
|
|
|
|
end else begin
|
|
|
|
|
finished <= 1;
|
2022-10-28 17:31:23 -04:00
|
|
|
|
end
|
|
|
|
|
end
|
2022-11-13 18:03:55 -05:00
|
|
|
|
endcase
|
2022-10-28 17:31:23 -04:00
|
|
|
|
end
|
|
|
|
|
|
2022-11-13 18:03:55 -05:00
|
|
|
|
`ifdef VERILATOR
|
|
|
|
|
initial begin
|
|
|
|
|
$dumpfile("control_loop_math.fst");
|
|
|
|
|
$dumpvars;
|
|
|
|
|
end
|
|
|
|
|
`endif
|
|
|
|
|
|
2022-10-28 17:31:23 -04:00
|
|
|
|
endmodule
|
2022-11-13 18:03:55 -05:00
|
|
|
|
`undefineall
|