0773001708
RTL fixes discovered via new end-to-end testbench: - plfm_chirp_controller: TX/RX mixer enables now mutually exclusive by FSM state (Fix #4), preventing simultaneous TX+RX activation - usb_data_interface: stream control reset default 3'b001 (range-only), added doppler/cfar data_pending sticky flags, write FSM triggers on range_valid only — eliminates startup deadlock (Fix #5) - radar_receiver_final: STM32 toggle signals wired through for mode-00 pass-through, dynamic frame detection via host_chirps_per_elev - radar_system_top: STM32 toggle signal wiring to receiver instance - chirp_memory_loader_param: explicit readmemh range for short chirp Test infrastructure: - New tb_system_e2e.v: 46 checks across 12 groups (reset, TX, safety, RX, USB R/W, CDC, beam scanning, reset recovery, stream control, latency budgets, watchdog) - tb_usb_data_interface: Tests 21/22/56 updated for data_pending architecture (preload flags, verify consumption instead of state) - tb_chirp_controller: mixer tests T7.1/T7.2 updated for Fix #4 - run_regression.sh: PASS/FAIL regex fixed to match only [PASS]/[FAIL] markers, added E2E test entry - Updated rx_final_doppler_out.csv golden data
357 lines
12 KiB
Verilog
357 lines
12 KiB
Verilog
`timescale 1ns / 1ps
|
|
|
|
module plfm_chirp_controller_enhanced (
|
|
input wire clk_120m,
|
|
input wire clk_100m,
|
|
input wire reset_n,
|
|
input wire new_chirp,
|
|
input wire new_elevation,
|
|
input wire new_azimuth,
|
|
input wire mixers_enable,
|
|
output reg [7:0] chirp_data,
|
|
output reg chirp_valid,
|
|
output wire new_chirp_frame,
|
|
output reg chirp_done,
|
|
output reg rf_switch_ctrl,
|
|
output wire rx_mixer_en,
|
|
output wire tx_mixer_en,
|
|
output wire adar_tx_load_1,
|
|
output wire adar_rx_load_1,
|
|
output wire adar_tx_load_2,
|
|
output wire adar_rx_load_2,
|
|
output wire adar_tx_load_3,
|
|
output wire adar_rx_load_3,
|
|
output wire adar_tx_load_4,
|
|
output wire adar_rx_load_4,
|
|
output reg adar_tr_1,
|
|
output reg adar_tr_2,
|
|
output reg adar_tr_3,
|
|
output reg adar_tr_4,
|
|
output reg [5:0] chirp_counter,
|
|
output reg [5:0] elevation_counter,
|
|
output reg [5:0] azimuth_counter
|
|
);
|
|
|
|
// Chirp parameters
|
|
parameter F_START = 30000000; // 30 MHz (starting frequency)
|
|
parameter F_END = 10000000; // 10 MHz (ending frequency)
|
|
parameter FS = 120000000; // 120 MHz
|
|
|
|
// Timing parameters
|
|
parameter T1_SAMPLES = 3600; // 30us at 120MHz
|
|
parameter T1_RADAR_LISTENING = 16440; //137us at 120MHz
|
|
parameter T2_SAMPLES = 60; // 0.5us at 120MHz
|
|
parameter T2_RADAR_LISTENING = 20940; //174.5us at 120MHz
|
|
parameter GUARD_SAMPLES = 21048; // 175.4us at 120MHz
|
|
|
|
// Chirp and beam parameters
|
|
parameter CHIRP_MAX = 32;
|
|
parameter ELEVATION_MAX = 31;
|
|
parameter AZIMUTH_MAX = 50;
|
|
|
|
// State parameters
|
|
parameter IDLE = 3'b000;
|
|
parameter LONG_CHIRP = 3'b001;
|
|
parameter LONG_LISTEN = 3'b010;
|
|
parameter GUARD_TIME = 3'b011;
|
|
parameter SHORT_CHIRP = 3'b100;
|
|
parameter SHORT_LISTEN = 3'b101;
|
|
parameter DONE = 3'b110;
|
|
|
|
reg [2:0] current_state;
|
|
reg [2:0] next_state;
|
|
|
|
// Control registers
|
|
reg [15:0] sample_counter;
|
|
|
|
// Edge detection for input signals
|
|
wire chirp__toggling, elevation__toggling, azimuth__toggling;
|
|
|
|
// LUTs for chirp waveforms
|
|
(* ram_style = "block" *) reg [7:0] long_chirp_lut [0:3599]; // T1_SAMPLES-1
|
|
reg [7:0] short_chirp_lut [0:59]; // T2_SAMPLES-1
|
|
|
|
// Registered BRAM read output (sync-only for BRAM inference)
|
|
reg [7:0] long_chirp_rd_data;
|
|
|
|
// Edge detection
|
|
assign chirp__toggling = new_chirp;
|
|
assign elevation__toggling = new_elevation;
|
|
assign azimuth__toggling = new_azimuth;
|
|
assign new_chirp_frame = (current_state == IDLE && next_state == LONG_CHIRP);
|
|
|
|
// Mixer TX/RX sequencing — mutually exclusive based on chirp FSM state.
|
|
// TX mixer active during chirp transmission, RX mixer during listen.
|
|
// Both require mixers_enable (STM32 master enable) to be high.
|
|
assign tx_mixer_en = mixers_enable && (current_state == LONG_CHIRP ||
|
|
current_state == SHORT_CHIRP);
|
|
assign rx_mixer_en = mixers_enable && (current_state == LONG_LISTEN ||
|
|
current_state == SHORT_LISTEN);
|
|
|
|
// ADTR1000 pull to ground tx and rx load pins if not used
|
|
assign adar_tx_load_1 = 1'b0;
|
|
assign adar_rx_load_1 = 1'b0;
|
|
assign adar_tx_load_2 = 1'b0;
|
|
assign adar_rx_load_2 = 1'b0;
|
|
assign adar_tx_load_3 = 1'b0;
|
|
assign adar_rx_load_3 = 1'b0;
|
|
assign adar_tx_load_4 = 1'b0;
|
|
assign adar_rx_load_4 = 1'b0;
|
|
|
|
|
|
|
|
|
|
// LUT Initialization
|
|
// Long PLFM chirp LUT loaded from .mem file for BRAM inference
|
|
initial begin
|
|
$readmemh("long_chirp_lut.mem", long_chirp_lut);
|
|
end
|
|
|
|
// Synchronous-only BRAM read (no async reset) for BRAM inference
|
|
always @(posedge clk_120m) begin
|
|
long_chirp_rd_data <= long_chirp_lut[sample_counter];
|
|
end
|
|
|
|
// Short PLFM chirp LUT initialization (too small for BRAM, keep inline)
|
|
initial begin
|
|
// Complete Short PLFM chirp LUT (0.5us, 30MHz to 10MHz)
|
|
short_chirp_lut[ 0] = 8'd255; short_chirp_lut[ 1] = 8'd237; short_chirp_lut[ 2] = 8'd187; short_chirp_lut[ 3] = 8'd118; short_chirp_lut[ 4] = 8'd 49; short_chirp_lut[ 5] = 8'd 6; short_chirp_lut[ 6] = 8'd 7; short_chirp_lut[ 7] = 8'd 54;
|
|
short_chirp_lut[ 8] = 8'd132; short_chirp_lut[ 9] = 8'd210; short_chirp_lut[10] = 8'd253; short_chirp_lut[11] = 8'd237; short_chirp_lut[12] = 8'd167; short_chirp_lut[13] = 8'd 75; short_chirp_lut[14] = 8'd 10; short_chirp_lut[15] = 8'd 10;
|
|
short_chirp_lut[16] = 8'd 80; short_chirp_lut[17] = 8'd180; short_chirp_lut[18] = 8'd248; short_chirp_lut[19] = 8'd237; short_chirp_lut[20] = 8'd150; short_chirp_lut[21] = 8'd 45; short_chirp_lut[22] = 8'd 1; short_chirp_lut[23] = 8'd 54;
|
|
short_chirp_lut[24] = 8'd167; short_chirp_lut[25] = 8'd249; short_chirp_lut[26] = 8'd228; short_chirp_lut[27] = 8'd118; short_chirp_lut[28] = 8'd 15; short_chirp_lut[29] = 8'd 18; short_chirp_lut[30] = 8'd127; short_chirp_lut[31] = 8'd238;
|
|
short_chirp_lut[32] = 8'd235; short_chirp_lut[33] = 8'd118; short_chirp_lut[34] = 8'd 10; short_chirp_lut[35] = 8'd 34; short_chirp_lut[36] = 8'd167; short_chirp_lut[37] = 8'd254; short_chirp_lut[38] = 8'd187; short_chirp_lut[39] = 8'd 45;
|
|
short_chirp_lut[40] = 8'd 8; short_chirp_lut[41] = 8'd129; short_chirp_lut[42] = 8'd248; short_chirp_lut[43] = 8'd201; short_chirp_lut[44] = 8'd 49; short_chirp_lut[45] = 8'd 10; short_chirp_lut[46] = 8'd145; short_chirp_lut[47] = 8'd254;
|
|
short_chirp_lut[48] = 8'd167; short_chirp_lut[49] = 8'd 17; short_chirp_lut[50] = 8'd 46; short_chirp_lut[51] = 8'd210; short_chirp_lut[52] = 8'd235; short_chirp_lut[53] = 8'd 75; short_chirp_lut[54] = 8'd 7; short_chirp_lut[55] = 8'd155;
|
|
short_chirp_lut[56] = 8'd253; short_chirp_lut[57] = 8'd118; short_chirp_lut[58] = 8'd 1; short_chirp_lut[59] = 8'd129;
|
|
end
|
|
|
|
// chirp_counter is driven solely by the clk_120m FSM always block (line ~683).
|
|
// Removed redundant clk_100m driver that caused multi-driven register
|
|
// (synthesis failure, simulation race condition).
|
|
// The FSM internally sequences through CHIRP_MAX chirps per beam position,
|
|
// so external new_chirp edge counting is unnecessary here.
|
|
|
|
// Elevation counter
|
|
|
|
always @(posedge clk_100m or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
elevation_counter <= 6'b1;
|
|
end else begin
|
|
if (elevation__toggling) begin
|
|
if (elevation_counter == ELEVATION_MAX) begin
|
|
elevation_counter <= 6'b1;
|
|
end else begin
|
|
elevation_counter <= elevation_counter + 6'b1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Azimuth counter
|
|
|
|
always @(posedge clk_100m or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
azimuth_counter <= 6'd1;
|
|
end else begin
|
|
if (azimuth__toggling) begin
|
|
if (azimuth_counter == AZIMUTH_MAX) begin
|
|
azimuth_counter <= 6'd1;
|
|
end else begin
|
|
azimuth_counter <= azimuth_counter + 6'd1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
// State register
|
|
always @(posedge clk_120m or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
current_state <= IDLE;
|
|
end else begin
|
|
current_state <= next_state;
|
|
end
|
|
end
|
|
|
|
// Next state logic
|
|
always @(*) begin
|
|
case (current_state)
|
|
IDLE: begin
|
|
if (chirp__toggling && mixers_enable)
|
|
next_state = LONG_CHIRP;
|
|
else
|
|
next_state = IDLE;
|
|
end
|
|
|
|
LONG_CHIRP: begin
|
|
if (sample_counter == T1_SAMPLES-1)
|
|
next_state = LONG_LISTEN;
|
|
else
|
|
next_state = LONG_CHIRP;
|
|
end
|
|
|
|
LONG_LISTEN: begin
|
|
if (sample_counter == T1_RADAR_LISTENING-1) begin
|
|
if (chirp_counter == (CHIRP_MAX/2)-1)
|
|
next_state = GUARD_TIME;
|
|
else
|
|
next_state = LONG_CHIRP;
|
|
end else begin
|
|
next_state = LONG_LISTEN;
|
|
end
|
|
end
|
|
|
|
GUARD_TIME: begin
|
|
if (sample_counter == GUARD_SAMPLES-1)
|
|
next_state = SHORT_CHIRP;
|
|
else
|
|
next_state = GUARD_TIME;
|
|
end
|
|
|
|
SHORT_CHIRP: begin
|
|
if (sample_counter == T2_SAMPLES-1)
|
|
next_state = SHORT_LISTEN;
|
|
else
|
|
next_state = SHORT_CHIRP;
|
|
end
|
|
|
|
SHORT_LISTEN: begin
|
|
if (sample_counter == T2_RADAR_LISTENING-1) begin
|
|
if (chirp_counter == CHIRP_MAX-1)
|
|
next_state = DONE;
|
|
else
|
|
next_state = SHORT_CHIRP;
|
|
end else begin
|
|
next_state = SHORT_LISTEN;
|
|
end
|
|
end
|
|
|
|
DONE: begin
|
|
next_state = IDLE;
|
|
end
|
|
|
|
default: begin
|
|
next_state = IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
always @(posedge clk_120m or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
sample_counter <= 0;
|
|
chirp_counter <= 0;
|
|
chirp_valid <= 0;
|
|
chirp_done <= 0;
|
|
chirp_data <= 8'd128;
|
|
rf_switch_ctrl <= 1'b0;
|
|
adar_tr_1 <= 1'b0;
|
|
adar_tr_2 <= 1'b0;
|
|
adar_tr_3 <= 1'b0;
|
|
adar_tr_4 <= 1'b0;
|
|
end else if (mixers_enable) begin
|
|
// Default outputs
|
|
chirp_valid <= 0;
|
|
chirp_done <= 0;
|
|
rf_switch_ctrl <= 0;
|
|
{adar_tr_1, adar_tr_2, adar_tr_3, adar_tr_4} <= 4'b0000;
|
|
|
|
// Sample counter increment logic
|
|
if (current_state == LONG_CHIRP || current_state == LONG_LISTEN ||
|
|
current_state == GUARD_TIME || current_state == SHORT_CHIRP ||
|
|
current_state == SHORT_LISTEN) begin
|
|
if (sample_counter == get_max_counter(current_state) - 1) begin
|
|
sample_counter <= 0;
|
|
// Increment chirp counter at end of listen states
|
|
if (current_state == LONG_LISTEN || current_state == SHORT_LISTEN) begin
|
|
chirp_counter <= chirp_counter + 1;
|
|
end
|
|
end else begin
|
|
sample_counter <= sample_counter + 1;
|
|
end
|
|
end else begin
|
|
sample_counter <= 0;
|
|
end
|
|
|
|
// State-specific outputs
|
|
case (current_state)
|
|
IDLE: begin
|
|
chirp_data <= 8'd128;
|
|
end
|
|
|
|
LONG_CHIRP: begin
|
|
rf_switch_ctrl <= 1'b1;
|
|
{adar_tr_1, adar_tr_2, adar_tr_3, adar_tr_4} <= 4'b1111;
|
|
|
|
// CRITICAL FIX: Generate valid signal
|
|
if (sample_counter < T1_SAMPLES) begin
|
|
chirp_data <= long_chirp_rd_data;
|
|
chirp_valid <= 1'b1; // Valid during entire chirp
|
|
end else begin
|
|
chirp_data <= 8'd128;
|
|
end
|
|
end
|
|
|
|
LONG_LISTEN: begin
|
|
chirp_data <= 8'd128;
|
|
rf_switch_ctrl <= 1'b0;
|
|
end
|
|
|
|
GUARD_TIME: begin
|
|
chirp_data <= 8'd128;
|
|
rf_switch_ctrl <= 1'b0;
|
|
end
|
|
|
|
SHORT_CHIRP: begin
|
|
rf_switch_ctrl <= 1'b1;
|
|
{adar_tr_1, adar_tr_2, adar_tr_3, adar_tr_4} <= 4'b1111;
|
|
|
|
// CRITICAL FIX: Generate valid signal for short chirp
|
|
if (sample_counter < T2_SAMPLES) begin
|
|
chirp_data <= short_chirp_lut[sample_counter];
|
|
chirp_valid <= 1'b1; // Valid during entire chirp
|
|
end else begin
|
|
chirp_data <= 8'd128;
|
|
end
|
|
end
|
|
|
|
SHORT_LISTEN: begin
|
|
chirp_data <= 8'd128;
|
|
rf_switch_ctrl <= 1'b0;
|
|
end
|
|
|
|
DONE: begin
|
|
chirp_done <= 1'b1;
|
|
chirp_data <= 8'd128;
|
|
end
|
|
|
|
default: begin
|
|
chirp_data <= 8'd128;
|
|
end
|
|
endcase
|
|
end else begin
|
|
// Mixers disabled
|
|
chirp_data <= 8'd128;
|
|
chirp_valid <= 0;
|
|
chirp_done <= 0;
|
|
rf_switch_ctrl <= 0;
|
|
{adar_tr_1, adar_tr_2, adar_tr_3, adar_tr_4} <= 4'b0000;
|
|
sample_counter <= 0;
|
|
end
|
|
end
|
|
|
|
// Helper function to get max counter for each state
|
|
function [15:0] get_max_counter;
|
|
input [2:0] state;
|
|
begin
|
|
case (state)
|
|
LONG_CHIRP: get_max_counter = T1_SAMPLES;
|
|
LONG_LISTEN: get_max_counter = T1_RADAR_LISTENING;
|
|
GUARD_TIME: get_max_counter = GUARD_SAMPLES;
|
|
SHORT_CHIRP: get_max_counter = T2_SAMPLES;
|
|
SHORT_LISTEN: get_max_counter = T2_RADAR_LISTENING;
|
|
default: get_max_counter = 0;
|
|
endcase
|
|
end
|
|
endfunction
|
|
|
|
endmodule |