dev/controller #5

Merged
baulin.fa merged 13 commits from dev/controller into master 2026-04-15 18:58:23 +03:00
Showing only changes of commit 003750d972 - Show all commits

View File

@ -0,0 +1,472 @@
module control #(
parameter int unsigned DAC_DATA_WIDTH = 12
) (
input logic eth_clk_in,
input logic dac_clk_in,
input logic adc_clk_in,
input logic rst_n,
// AXI stream slave, eth_clk_in domain
input logic [7:0] s_axis_tdata,
input logic s_axis_tvalid,
output logic s_axis_tready,
input logic s_axis_tlast,
// adc_clk_in domain
input logic finish,
// dac_clk_in domain outputs
output logic [31:0] dac_pulse_width,
output logic [31:0] dac_pulse_period,
output logic [DAC_DATA_WIDTH-1:0] dac_pulse_height,
output logic [15:0] dac_pulse_num,
// adc_clk_in domain outputs
output logic [31:0] adc_pulse_period,
output logic [15:0] adc_pulse_num,
// pulse outputs
output logic dac_start,
output logic adc_start,
output logic dac_rst,
output logic adc_rst
);
// static checks
initial begin
if (DAC_DATA_WIDTH > 16) begin
$error("DAC_DATA_WIDTH must be <= 16");
end
if (DAC_DATA_WIDTH == 0) begin
$error("DAC_DATA_WIDTH must be > 0");
end
end
// command constants
localparam logic [7:0] CMD_SOFT_RESET = 8'h0F;
localparam logic [7:0] CMD_START = 8'hF0;
localparam logic [7:0] CMD_SET_DATA = 8'h88;
// reset synchronizers: async assert, sync deassert in each domain
logic eth_rst_ff1, eth_rst_ff2;
logic dac_rst_ff1, dac_rst_ff2;
logic adc_rst_ff1, adc_rst_ff2;
logic eth_rst;
logic dac_rst_int;
logic adc_rst_int;
always_ff @(posedge eth_clk_in or negedge rst_n) begin
if (!rst_n) begin
eth_rst_ff1 <= 1'b1;
eth_rst_ff2 <= 1'b1;
end else begin
eth_rst_ff1 <= 1'b0;
eth_rst_ff2 <= eth_rst_ff1;
end
end
always_ff @(posedge dac_clk_in or negedge rst_n) begin
if (!rst_n) begin
dac_rst_ff1 <= 1'b1;
dac_rst_ff2 <= 1'b1;
end else begin
dac_rst_ff1 <= 1'b0;
dac_rst_ff2 <= dac_rst_ff1;
end
end
always_ff @(posedge adc_clk_in or negedge rst_n) begin
if (!rst_n) begin
adc_rst_ff1 <= 1'b1;
adc_rst_ff2 <= 1'b1;
end else begin
adc_rst_ff1 <= 1'b0;
adc_rst_ff2 <= adc_rst_ff1;
end
end
assign eth_rst = eth_rst_ff2;
assign dac_rst_int = dac_rst_ff2;
assign adc_rst_int = adc_rst_ff2;
// axi stream is always accepted. If packet is not needed, it is discarded.
assign s_axis_tready = 1'b1;
wire axis_hs = s_axis_tvalid & s_axis_tready;
// -------------------------------------------------------------------------
// Shared 96-bit config bus in ETH domain
//
// Byte order for SET_DATA payload, little-endian:
// payload byte 0 -> cfg_bus_eth[7:0]
// payload byte 1 -> cfg_bus_eth[15:8]
// ...etc...
// payload byte 11 -> cfg_bus_eth[95:88]
//
// Field layout inside cfg_bus_eth:
// [31:0] pulse_width
// [63:32] pulse_period
// [79:64] pulse_num
// [95:80] pulse_height_raw[15:0]
// -------------------------------------------------------------------------
logic [95:0] cfg_bus_eth;
logic [95:0] cfg_shift_eth;
// ETH-domain parser and control
typedef enum logic [2:0] {
ST_IDLE = 3'd0,
ST_RECV_CFG = 3'd1,
ST_WAIT_CFG_ACK = 3'd2,
ST_DISCARD = 3'd3
} eth_state_t;
eth_state_t eth_state;
logic [3:0] cfg_byte_cnt;
// Busy flag: set by START command, cleared by finish event from ADC domain
logic busy_flag_eth;
// Pending ACKs for config delivery
logic cfg_wait_dac_ack;
logic cfg_wait_adc_ack;
// Event toggles ETH -> DAC/ADC
logic start_toggle_eth;
logic rst_toggle_eth;
// Config request toggles ETH -> DAC/ADC
logic cfg_req_toggle_dac_eth;
logic cfg_req_toggle_adc_eth;
// ACK toggles DAC/ADC -> ETH
logic cfg_ack_toggle_dac;
logic cfg_ack_toggle_adc;
(* ASYNC_REG = "TRUE" *) logic cfg_ack_toggle_dac_meta, cfg_ack_toggle_dac_sync, cfg_ack_toggle_dac_sync_d;
(* ASYNC_REG = "TRUE" *) logic cfg_ack_toggle_adc_meta, cfg_ack_toggle_adc_sync, cfg_ack_toggle_adc_sync_d;
wire cfg_ack_pulse_dac_eth = cfg_ack_toggle_dac_sync ^ cfg_ack_toggle_dac_sync_d;
wire cfg_ack_pulse_adc_eth = cfg_ack_toggle_adc_sync ^ cfg_ack_toggle_adc_sync_d;
always_ff @(posedge eth_clk_in or posedge eth_rst) begin
if (eth_rst) begin
cfg_ack_toggle_dac_meta <= 1'b0;
cfg_ack_toggle_dac_sync <= 1'b0;
cfg_ack_toggle_dac_sync_d <= 1'b0;
cfg_ack_toggle_adc_meta <= 1'b0;
cfg_ack_toggle_adc_sync <= 1'b0;
cfg_ack_toggle_adc_sync_d <= 1'b0;
end else begin
cfg_ack_toggle_dac_meta <= cfg_ack_toggle_dac;
cfg_ack_toggle_dac_sync <= cfg_ack_toggle_dac_meta;
cfg_ack_toggle_dac_sync_d <= cfg_ack_toggle_dac_sync;
cfg_ack_toggle_adc_meta <= cfg_ack_toggle_adc;
cfg_ack_toggle_adc_sync <= cfg_ack_toggle_adc_meta;
cfg_ack_toggle_adc_sync_d <= cfg_ack_toggle_adc_sync;
end
end
// finish event: ADC -> ETH via toggle CDC
logic finish_toggle_adc;
logic finish_meta_eth, finish_sync_eth, finish_sync_eth_d;
wire finish_pulse_eth = finish_sync_eth ^ finish_sync_eth_d;
always_ff @(posedge adc_clk_in or posedge adc_rst_int) begin
if (adc_rst_int) begin
finish_toggle_adc <= 1'b0;
end else if (finish) begin
finish_toggle_adc <= ~finish_toggle_adc;
end
end
always_ff @(posedge eth_clk_in or posedge eth_rst) begin
if (eth_rst) begin
finish_meta_eth <= 1'b0;
finish_sync_eth <= 1'b0;
finish_sync_eth_d <= 1'b0;
end else begin
finish_meta_eth <= finish_toggle_adc;
finish_sync_eth <= finish_meta_eth;
finish_sync_eth_d <= finish_sync_eth;
end
end
// ETH FSM
always_ff @(posedge eth_clk_in or posedge eth_rst) begin
if (eth_rst) begin
eth_state <= ST_IDLE;
cfg_byte_cnt <= '0;
cfg_shift_eth <= '0;
cfg_bus_eth <= '0;
busy_flag_eth <= 1'b0;
start_toggle_eth <= 1'b0;
rst_toggle_eth <= 1'b0;
cfg_req_toggle_dac_eth <= 1'b0;
cfg_req_toggle_adc_eth <= 1'b0;
cfg_wait_dac_ack <= 1'b0;
cfg_wait_adc_ack <= 1'b0;
end else begin
// finish always clears busy
if (finish_pulse_eth) begin
busy_flag_eth <= 1'b0;
end
// config acks
if (cfg_ack_pulse_dac_eth) begin
cfg_wait_dac_ack <= 1'b0;
end
if (cfg_ack_pulse_adc_eth) begin
cfg_wait_adc_ack <= 1'b0;
end
case (eth_state)
ST_IDLE: begin
cfg_byte_cnt <= '0;
cfg_shift_eth <= cfg_shift_eth;
if (axis_hs) begin
// if busy, drop the whole packet
if (busy_flag_eth) begin
if (!s_axis_tlast) begin
eth_state <= ST_DISCARD;
end
end else begin
unique case (s_axis_tdata)
CMD_SOFT_RESET: begin
// command packet must be exactly 1 byte
if (s_axis_tlast) begin
rst_toggle_eth <= ~rst_toggle_eth;
end else begin
eth_state <= ST_DISCARD;
end
end
CMD_START: begin
// command packet must be exactly 1 byte
if (s_axis_tlast) begin
start_toggle_eth <= ~start_toggle_eth;
busy_flag_eth <= 1'b1;
end else begin
eth_state <= ST_DISCARD;
end
end
CMD_SET_DATA: begin
// expect exactly 12 bytes after command
if (s_axis_tlast) begin
// no payload, invalid packet
eth_state <= ST_IDLE;
end else begin
cfg_byte_cnt <= 4'd0;
cfg_shift_eth <= '0;
eth_state <= ST_RECV_CFG;
end
end
default: begin
// unknown command: discard packet remainder if any
if (!s_axis_tlast) begin
eth_state <= ST_DISCARD;
end
end
endcase
end
end
end
ST_RECV_CFG: begin
if (axis_hs) begin
// little endian packing
cfg_shift_eth[cfg_byte_cnt*8 +: 8] <= s_axis_tdata;
if (cfg_byte_cnt == 4'd11) begin
// this must be the final payload byte
if (s_axis_tlast) begin
cfg_bus_eth <= {s_axis_tdata, cfg_shift_eth[87:0]};
cfg_req_toggle_dac_eth <= ~cfg_req_toggle_dac_eth;
cfg_req_toggle_adc_eth <= ~cfg_req_toggle_adc_eth;
cfg_wait_dac_ack <= 1'b1;
cfg_wait_adc_ack <= 1'b1;
eth_state <= ST_WAIT_CFG_ACK;
end else begin
// too many bytes in packet
eth_state <= ST_DISCARD;
end
end else begin
// early tlast means packet too short!!
if (s_axis_tlast) begin
eth_state <= ST_IDLE;
end else begin
cfg_byte_cnt <= cfg_byte_cnt + 4'd1;
end
end
end
end
ST_WAIT_CFG_ACK: begin
// any incoming packet while waiting ack is discarded
if (cfg_ack_pulse_dac_eth || cfg_ack_pulse_adc_eth) begin
if ((~cfg_wait_dac_ack || cfg_ack_pulse_dac_eth) &&
(~cfg_wait_adc_ack || cfg_ack_pulse_adc_eth)) begin
eth_state <= ST_IDLE;
end
end
if (axis_hs && !s_axis_tlast) begin
eth_state <= ST_DISCARD;
end
end
ST_DISCARD: begin
if (axis_hs && s_axis_tlast) begin
eth_state <= ST_IDLE;
end
end
default: begin
eth_state <= ST_IDLE;
end
endcase
end
end
// ETH -> DAC: start/reset event sync
(* ASYNC_REG = "TRUE" *) logic start_meta_dac, start_sync_dac;
logic start_sync_dac_d;
(* ASYNC_REG = "TRUE" *) logic rst_meta_dac, rst_sync_dac;
logic rst_sync_dac_d;
wire dac_start_pulse = start_sync_dac ^ start_sync_dac_d;
wire dac_rst_pulse = rst_sync_dac ^ rst_sync_dac_d;
always_ff @(posedge dac_clk_in or posedge dac_rst_int) begin
if (dac_rst_int) begin
start_meta_dac <= 1'b0;
start_sync_dac <= 1'b0;
start_sync_dac_d <= 1'b0;
rst_meta_dac <= 1'b0;
rst_sync_dac <= 1'b0;
rst_sync_dac_d <= 1'b0;
dac_start <= 1'b0;
dac_rst <= 1'b0;
end else begin
start_meta_dac <= start_toggle_eth;
start_sync_dac <= start_meta_dac;
start_sync_dac_d <= start_sync_dac;
rst_meta_dac <= rst_toggle_eth;
rst_sync_dac <= rst_meta_dac;
rst_sync_dac_d <= rst_sync_dac;
dac_start <= dac_start_pulse;
dac_rst <= dac_rst_pulse;
end
end
// ETH -> ADC: start/reset event sync
(* ASYNC_REG = "TRUE" *) logic start_meta_adc, start_sync_adc;
logic start_sync_adc_d;
(* ASYNC_REG = "TRUE" *) logic rst_meta_adc, rst_sync_adc;
logic rst_sync_adc_d;
wire adc_start_pulse = start_sync_adc ^ start_sync_adc_d;
wire adc_rst_pulse = rst_sync_adc ^ rst_sync_adc_d;
always_ff @(posedge adc_clk_in or posedge adc_rst_int) begin
if (adc_rst_int) begin
start_meta_adc <= 1'b0;
start_sync_adc <= 1'b0;
start_sync_adc_d <= 1'b0;
rst_meta_adc <= 1'b0;
rst_sync_adc <= 1'b0;
rst_sync_adc_d <= 1'b0;
adc_start <= 1'b0;
adc_rst <= 1'b0;
end else begin
start_meta_adc <= start_toggle_eth;
start_sync_adc <= start_meta_adc;
start_sync_adc_d <= start_sync_adc;
rst_meta_adc <= rst_toggle_eth;
rst_sync_adc <= rst_meta_adc;
rst_sync_adc_d <= rst_sync_adc;
adc_start <= adc_start_pulse;
adc_rst <= adc_rst_pulse;
end
end
// ETH -> DAC config CDC
// cfg_bus_eth is kept stable in ETH domain until DAC and ADC both ACK.
(* ASYNC_REG = "TRUE" *) logic cfg_req_meta_dac, cfg_req_sync_dac;
logic cfg_req_sync_dac_d;
wire cfg_req_pulse_dac = cfg_req_sync_dac ^ cfg_req_sync_dac_d;
always_ff @(posedge dac_clk_in or posedge dac_rst_int) begin
if (dac_rst_int) begin
cfg_req_meta_dac <= 1'b0;
cfg_req_sync_dac <= 1'b0;
cfg_req_sync_dac_d<= 1'b0;
cfg_ack_toggle_dac<= 1'b0;
dac_pulse_width <= '0;
dac_pulse_period <= '0;
dac_pulse_num <= '0;
dac_pulse_height <= '0;
end else begin
cfg_req_meta_dac <= cfg_req_toggle_dac_eth;
cfg_req_sync_dac <= cfg_req_meta_dac;
cfg_req_sync_dac_d <= cfg_req_sync_dac;
if (cfg_req_pulse_dac) begin
dac_pulse_width <= cfg_bus_eth[31:0];
dac_pulse_period <= cfg_bus_eth[63:32];
dac_pulse_num <= cfg_bus_eth[79:64];
dac_pulse_height <= cfg_bus_eth[80 +: DAC_DATA_WIDTH];
cfg_ack_toggle_dac <= ~cfg_ack_toggle_dac;
end
end
end
// ETH -> ADC config CDC
logic cfg_req_meta_adc, cfg_req_sync_adc, cfg_req_sync_adc_d;
wire cfg_req_pulse_adc = cfg_req_sync_adc ^ cfg_req_sync_adc_d;
always_ff @(posedge adc_clk_in or posedge adc_rst_int) begin
if (adc_rst_int) begin
cfg_req_meta_adc <= 1'b0;
cfg_req_sync_adc <= 1'b0;
cfg_req_sync_adc_d <= 1'b0;
cfg_ack_toggle_adc <= 1'b0;
adc_pulse_period <= '0;
adc_pulse_num <= '0;
end else begin
cfg_req_meta_adc <= cfg_req_toggle_adc_eth;
cfg_req_sync_adc <= cfg_req_meta_adc;
cfg_req_sync_adc_d <= cfg_req_sync_adc;
if (cfg_req_pulse_adc) begin
adc_pulse_period <= cfg_bus_eth[63:32];
adc_pulse_num <= cfg_bus_eth[79:64];
cfg_ack_toggle_adc <= ~cfg_ack_toggle_adc;
end
end
end
endmodule