797 lines
28 KiB
Verilog
797 lines
28 KiB
Verilog
/******************************************************************************
|
|
* Copyright *
|
|
* Scintillating Fibre Beam Profile Monitor Software by Michal Dziewiecki, *
|
|
* Blake Leverington and Liqing Qin is licensed under CC BY 4.0 *
|
|
* https://creativecommons.org/licenses/by/4.0/ *
|
|
* funded by the Deutsche Forschungsgemeinschaft *
|
|
* (DFG, German Research Foundation) Projektnummer 419255448 *
|
|
* Project Leader: B.Leverington *
|
|
*******************************************************************************
|
|
* Create Date - 2019 *
|
|
* Author: M.Dziewiecki *
|
|
* Module - sensor_interface.v *
|
|
* Edited by L.Qin on Oct 15. 2024 *
|
|
******************************************************************************/
|
|
|
|
|
|
`timescale 1 ps / 1 ps
|
|
module sensor_interface (
|
|
//Clock/reset
|
|
input wire clk_clk, // clk.clk
|
|
input wire rst_reset, // rst.reset
|
|
//Avalon MM slave
|
|
input wire [1:0] csr_address, // avalon_slave.address
|
|
input wire csr_read, // .read
|
|
output wire [31:0] csr_readdata, // .readdata
|
|
input wire csr_write, // .write
|
|
input wire [31:0] csr_writedata, // .writedata
|
|
input wire [3:0] csr_byteenable, // .byteenable
|
|
//Avalon ST transmitter
|
|
output wire [31:0] data_out_data, // avalon_streaming_source.data
|
|
output wire data_out_endofpacket, // .endofpacket
|
|
output wire data_out_startofpacket, // .startofpacket
|
|
input wire data_out_ready, // .ready
|
|
output wire data_out_valid, // .valid
|
|
output wire [1:0] data_out_empty, // .empty
|
|
//Sensor interface
|
|
input wire in_trg, // sensor.in_trg // This is the frame timer
|
|
output wire out_adc_clk, // .out_dac_clk
|
|
output wire out_adc_cnv, // .out_dac_cnv
|
|
input wire [4:0] in_adc_data, // .in_dac_data
|
|
output wire out_sensor_rst, // .out_sensor_rst
|
|
output wire out_sensor_clk, // .out_sensor_clk
|
|
output wire out_sensor_gain, // .out_sensor_gain
|
|
//Serial synchro interface
|
|
input wire serial_rx, //receive data
|
|
output wire serial_tx, //send data
|
|
input wire [7:0] ext_input, //SMA etc.
|
|
//Debug information
|
|
output wire [7:0] status_out, //status bits - same as in reg0_read[15:8]
|
|
|
|
output reg int_rst, //the reset for each new run
|
|
output reg [7:0] cluster_threshold, //from registers[3][7:0]
|
|
output reg [7:0] cluster_size, //from registers[3][15:8]
|
|
output reg [7:0] in_algo_threshold //from registers[3][23:16]
|
|
);
|
|
|
|
|
|
//Very important globals
|
|
reg[31:0] transmit_buffer[162:0]; //Data to send: 3 words header plus sensor data
|
|
wire readout_ready; //pulsing this signal causes copying data from frame buffer to transmit buffer and initiating transmission
|
|
wire start_transmission; //pulsing this signal causes the Avalon ST link to start transmission
|
|
reg[15:0] local_sync_ctr; //local synchronization counter, it increments by one at each frame
|
|
wire[8:0] global_sync_ctr; //global synchronization counter, it's simply the received value from serial RX.
|
|
wire global_sync_error; //RX error in the global synchronization counter
|
|
|
|
//Common generate variables
|
|
genvar i; //for generate loops
|
|
genvar j;
|
|
integer xi; //for normal loops
|
|
integer xj;
|
|
|
|
// *********************** CSR stuff ************************
|
|
/*
|
|
There are 4 32-bit R/W-able registers. They are used for setting up the module and checking its state
|
|
Reg0 is a bit different: when read, bits 15-8 (the status byte) are taken directly from device internals, not from register contents.
|
|
The bus can be accessed 32-, 16- or 8-bit-wise.
|
|
|
|
Register definition
|
|
//reg0
|
|
#define SENSOR_REG_COMMAND 0 //8 bits
|
|
#define SENSOR_REG_STATUS 1 //8 bits
|
|
#define SENSOR_REG_SENSORCLK 2 //8 bits, 6 used divider for producing sensor clock ('4' MHz)
|
|
#define SESNOR_REG_ADCCNV 3 //8 bits, 6 used time of conversion pulse in ADC clocks, should be > 500 ns
|
|
//reg1
|
|
#define SENSOR_REG_DELAY 4 //16 bits, 12 used reset signal delay in 50 MHz clocks
|
|
#define SENSOR_REG_SHUTTER 6 //16 bits, 12 used sensor reset ('shutter') time in sensor clocks
|
|
//reg2
|
|
#define SENSOR_REG_SERSPEED 8 //8 bits synchro baudrate, 50 for 1 Mbps
|
|
#define SENSOR_REG_HEADER_ANYDATA 9 //8 bits any data transmitted with SMA state (8 bits SMA + 8 bits anydata)
|
|
#define SENSOR_REG_HEADER_CMD 10 //16 bits command field of the command header transmitted in packet
|
|
//reg3 //configure reg3 17.July.2024 Qin,L
|
|
#define SENSOR_REG_CLUSTER_THRESHOLD 12 //8 bits the threshold for cluster locate, range 0~255
|
|
#define SENSOR_REG_CLUSTER_SIZE 13 //8 bits the size for clluster locate, range 0~255
|
|
#define SENSOR_REG_IN_ALGO_THRESHOLD 14 //8 bits the threshold inside Linear Regression, range 0~255
|
|
#define SENSOR_REG_RESERVED 15 //8 bits not used
|
|
|
|
Command bitmasks
|
|
#define SENSOR_CSR_EN_BITMASK 0x01 //enable operation
|
|
#define SENSOR_CSR_GAIN_BITMASK 0x02 //gain selection
|
|
#define SENSOR_CSR_ADCK_BITMASK 0x04 //ADC clock divider on/off
|
|
#define SENSOR_CSR_RESET_BITMASK 0x08 //Reset all logic //registers[0][3]
|
|
|
|
Status bitmasks
|
|
#define SENSOR_STATUS_SEND 0x01 //Sending over Avalon-ST
|
|
#define SENSOR_STATUS_TRG_WAITING 0x02 //The trigger came and is being delayed now
|
|
#define SENSOR_STATUS_RESET_ACTIVE 0x04 //The RESET signal for the sensor is active now
|
|
#define SENSOR_STATUS_ADC_ACTIVE 0x08 //The ADC is converting data (signal high over all 64 channels) or just finished and waits for reset high
|
|
#define SESNOR_STATUS_ADC_FINISHED 0x10 //The ADC waits for reset high
|
|
#define SESNOR_STATUS_TX_ACTIVE 0x20 //Sync port is sending
|
|
#define SESNOR_STATUS_RX_ACTIVE 0x40 //Sync port is receiving
|
|
|
|
*/
|
|
|
|
reg [31:0] registers[3:0]; //main register space
|
|
wire [31:0] reg0_read; //reg[0] is read indirectly to include status byt(bits 15-8)
|
|
|
|
//read logic
|
|
assign reg0_read[7:0] = registers[0][7:0]; // Readback of control bits
|
|
assign reg0_read[31:16] = registers[0][31:16]; // Readback of payload size
|
|
assign reg0_read[15] = 0; // Unused bits of the status byte
|
|
assign csr_readdata = (csr_address==0) ? reg0_read : registers[csr_address];
|
|
|
|
//write logic
|
|
generate
|
|
for (i = 0; i < 4; i = i+1)
|
|
begin : write_registers //Quartus wants labels for 'for' blocks
|
|
for (j = 0; j < 4; j = j+1)
|
|
begin : write_bytes
|
|
always @(posedge clk_clk or posedge rst_reset)
|
|
begin
|
|
if (rst_reset)
|
|
registers[i][8*j+7:8*j] <= 8'd0;
|
|
else
|
|
if (csr_byteenable[j] && (csr_address == i) && (csr_write))
|
|
registers[i][8*j+7:8*j] <= csr_writedata[8*j+7:8*j];
|
|
end
|
|
end
|
|
end
|
|
endgenerate
|
|
|
|
// *********************** Internal reset logic ************************
|
|
/*
|
|
The internal reset signal derives from the reset input and reset bit (reg0.3).
|
|
It drives all subcomponents but registers (which use simpy the reset input).
|
|
This signal is registered to be sure that no glitches from register bank pass.
|
|
*/
|
|
|
|
//reg int_rst;
|
|
|
|
always @(posedge clk_clk)
|
|
begin
|
|
int_rst <= rst_reset | registers[0][3];
|
|
end
|
|
|
|
// *********************** Avalon transmitter ************************
|
|
/*
|
|
The transmitter is used to transmit collected sensor data together with sync frame.
|
|
It can be later packed into UDP by UDP generator and sent over Ethernet. Or whatever.
|
|
The transmitter has 8-bit symbol, 4 symbols per beat. It's backpressurizable and includes packet signals.
|
|
The Empty signal is dummy (always zero), as the data is always aligned to 32-bit size.
|
|
The transmission starts when the 'start_transmission' signal gets pulsed
|
|
*/
|
|
|
|
assign data_out_empty = 2'b00;
|
|
|
|
reg [1:0] data_out_state; //State of the state machine
|
|
localparam DATA_OUT_STATE_IDLE = 0; //waiting for high
|
|
localparam DATA_OUT_STATE_SEND = 1; //sending data
|
|
localparam DATA_OUT_STATE_LOCK = 2; //waiting for trigger low
|
|
|
|
//localparam DATA_OUT_STATE_PEND = 3; //the pending when sending data
|
|
|
|
localparam WORDS_TO_SEND = 163;
|
|
|
|
//registers to drive output pins of ST interfaces
|
|
// reg reg_valid;
|
|
// reg [31:0] reg_data;
|
|
// reg reg_startofpacket;
|
|
// reg reg_endofpacket;
|
|
|
|
//Helper stuff
|
|
reg [15:0] tx_ctr; //counter of sent data words
|
|
|
|
//The state machine
|
|
always @(posedge clk_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
data_out_state <= DATA_OUT_STATE_IDLE;
|
|
cluster_threshold <= registers[3][7:0];
|
|
cluster_size <= registers[3][15:8];
|
|
in_algo_threshold <= registers[3][23:16];
|
|
end
|
|
else
|
|
case(data_out_state)
|
|
DATA_OUT_STATE_IDLE:
|
|
begin
|
|
if (start_transmission)
|
|
begin
|
|
data_out_state <= DATA_OUT_STATE_SEND;
|
|
tx_ctr <= 0;
|
|
cluster_threshold <= registers[3][7:0];
|
|
cluster_size <= registers[3][15:8];
|
|
in_algo_threshold <= registers[3][23:16];
|
|
end
|
|
end
|
|
DATA_OUT_STATE_SEND:
|
|
begin
|
|
if (data_out_ready)
|
|
begin
|
|
tx_ctr <= tx_ctr + 1'b1;
|
|
if (tx_ctr == (WORDS_TO_SEND-1))
|
|
data_out_state <= DATA_OUT_STATE_LOCK;
|
|
end
|
|
end
|
|
DATA_OUT_STATE_LOCK:
|
|
begin
|
|
if (~start_transmission)
|
|
data_out_state <= DATA_OUT_STATE_IDLE;
|
|
end
|
|
|
|
default:
|
|
data_out_state <= DATA_OUT_STATE_IDLE;
|
|
endcase
|
|
end
|
|
|
|
assign data_out_valid = (data_out_state == DATA_OUT_STATE_SEND)? 1'b1: 1'b0;
|
|
assign data_out_data = (data_out_state == DATA_OUT_STATE_SEND)? transmit_buffer[tx_ctr]: 32'b0;
|
|
assign data_out_startofpacket = (data_out_state == DATA_OUT_STATE_SEND && tx_ctr == 0) ? 1'b1: 1'b0;
|
|
assign data_out_endofpacket = (data_out_state == DATA_OUT_STATE_SEND && tx_ctr == (WORDS_TO_SEND-1)) ? 1'b1: 1'b0;
|
|
|
|
assign reg0_read[8] = ~(data_out_state == DATA_OUT_STATE_IDLE); //to status register
|
|
|
|
// *********************** Detector gain logic ************************
|
|
/*
|
|
The gain signal is simply derived from reg0.
|
|
*/
|
|
|
|
assign out_sensor_gain = registers[0][1];
|
|
|
|
// *********************** Trigger delay logic ************************
|
|
/*
|
|
Individual trigger delay settings are needed to synchronize multiple boards.
|
|
A 12-bit downcounter allows for delaying incoming trigger's rising edge by up to 4095 50MHz clock cycles, i.e. 81.9 us.
|
|
Note: A further delay of one sensor clock will be added in the sensor clock generation logic.
|
|
*/
|
|
|
|
reg [11:0] trg_delay_ctr;
|
|
wire int_trg;
|
|
reg [1:0] trg_state;
|
|
localparam TRG_STATE_IDLE = 0; //waiting for external trigger
|
|
localparam TRG_STATE_DELAY = 1; //downcounting
|
|
localparam TRG_STATE_ACTIVE = 2; //internal trigger active
|
|
localparam TRG_STATE_LOCK = 3; //waiting for external trigger inactive
|
|
|
|
//localparam TRG_DURATION = 4; //duration of the trigger signal
|
|
|
|
assign int_trg = (trg_state == TRG_STATE_ACTIVE) ? 1'b1 : 1'b0;
|
|
|
|
always @(posedge clk_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
trg_state <= TRG_STATE_LOCK;
|
|
end
|
|
else
|
|
begin
|
|
case(trg_state)
|
|
TRG_STATE_IDLE:
|
|
begin
|
|
if (in_trg && registers[0][0]) //react on trigger only if enabled
|
|
begin
|
|
trg_delay_ctr <= registers[1][11:0];
|
|
trg_state <= TRG_STATE_DELAY;
|
|
end
|
|
end
|
|
TRG_STATE_DELAY:
|
|
begin
|
|
trg_delay_ctr <= trg_delay_ctr - 1'b1;
|
|
if (trg_delay_ctr == 0)
|
|
begin
|
|
//trg_delay_ctr <= TRG_DURATION-1;
|
|
trg_state <= TRG_STATE_ACTIVE;
|
|
end
|
|
end
|
|
TRG_STATE_ACTIVE:
|
|
begin
|
|
//trg_delay_ctr <= trg_delay_ctr - 1;
|
|
//if (trg_delay_ctr == 0)
|
|
//begin
|
|
trg_state <= TRG_STATE_LOCK; //only one cycle of active int_trg
|
|
//end
|
|
end
|
|
TRG_STATE_LOCK:
|
|
begin
|
|
if (~in_trg)
|
|
trg_state <= TRG_STATE_IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
assign reg0_read[9] = ~(trg_state == TRG_STATE_IDLE); //to status register
|
|
|
|
// *********************** Sensor clock logic ************************
|
|
/*
|
|
The sensor needs a clock of max. 4 MHz, which is generated by this module.
|
|
The actual clock speed is configurable by a register with a formula: Fs = Fi/2/(div+1),
|
|
where Fs is the sensor clock, Fi is input clock (50 MHz nom.) and div is a divider taken from the register.
|
|
A 5-bit downcounter allows for frequencies from 0.39 MHz (div=63) to 12.5 MHz (div=1).
|
|
The nominal setting is 6, giving 3.5714285 MHz.
|
|
|
|
Important: The sensor clock is synchronized to the rising edge of the (delayed) trigger input,
|
|
so that there is a positive slope at exactly one sensor clock period after the trigger.
|
|
This positive slope is then used to synchronize integration start signal (sensor reset or shutter).
|
|
The synchronization is done so that one of clock states (0 or 1) gets extended, but never shortened.
|
|
|
|
Note: 4 MHz is also the top speed accepted by the ADC module.
|
|
*/
|
|
|
|
reg [5:0] sensor_clk_ctr;
|
|
reg [1:0] sensor_clk_state;
|
|
|
|
localparam SENSOR_CLK_STATE_LOW = 2'd0;
|
|
localparam SENSOR_CLK_STATE_HIGH = 2'd1;
|
|
localparam SENSOR_CLK_STATE_LOWAIT = 2'd2;
|
|
localparam SENSOR_CLK_STATE_HIWAIT = 2'd3;
|
|
|
|
assign out_sensor_clk = sensor_clk_state[0];
|
|
|
|
always @(posedge clk_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
sensor_clk_state <= SENSOR_CLK_STATE_LOW;
|
|
sensor_clk_ctr <= registers[0][21:16];
|
|
end
|
|
else
|
|
begin
|
|
case(sensor_clk_state)
|
|
SENSOR_CLK_STATE_LOW:
|
|
begin
|
|
sensor_clk_ctr <= sensor_clk_ctr-1'b1;
|
|
if (int_trg)
|
|
begin
|
|
sensor_clk_ctr <= registers[0][21:16];
|
|
sensor_clk_state <= SENSOR_CLK_STATE_LOWAIT;
|
|
end
|
|
else if (sensor_clk_ctr == 0)
|
|
begin
|
|
sensor_clk_ctr <= registers[0][21:16];
|
|
sensor_clk_state <= SENSOR_CLK_STATE_HIGH;
|
|
end
|
|
end
|
|
SENSOR_CLK_STATE_HIGH:
|
|
begin
|
|
sensor_clk_ctr <= sensor_clk_ctr-1'b1;
|
|
if (int_trg)
|
|
begin
|
|
sensor_clk_ctr <= registers[0][21:16];
|
|
sensor_clk_state <= SENSOR_CLK_STATE_HIWAIT;
|
|
end
|
|
else if (sensor_clk_ctr == 0)
|
|
begin
|
|
sensor_clk_ctr <= registers[0][21:16];
|
|
sensor_clk_state <= SENSOR_CLK_STATE_LOW;
|
|
end
|
|
end
|
|
SENSOR_CLK_STATE_LOWAIT:
|
|
begin
|
|
sensor_clk_ctr <= sensor_clk_ctr-1'b1;
|
|
if (sensor_clk_ctr == 0)
|
|
begin
|
|
sensor_clk_ctr <= registers[0][21:16];
|
|
sensor_clk_state <= SENSOR_CLK_STATE_LOW;
|
|
end
|
|
end
|
|
SENSOR_CLK_STATE_HIWAIT:
|
|
begin
|
|
sensor_clk_ctr <= sensor_clk_ctr-1'b1;
|
|
if (sensor_clk_ctr == 0)
|
|
begin
|
|
sensor_clk_ctr <= registers[0][21:16];
|
|
sensor_clk_state <= SENSOR_CLK_STATE_LOW;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
// *********************** Sensor reset (shutter) logic ************************
|
|
/*
|
|
This block generates a 'reset' (shutter) signal for the sensor.
|
|
It defines the integration time and is programmable by means of a register in sensor clock units.
|
|
The leading (positive) slope of this signal is synchronized with the first positive slope of the sensor clock after the trigger.
|
|
An internal 12-bit downcounter allows for a max. integration time of ca 1.15 ms. It can be further extended by slowing down the sensor clock.
|
|
It's user's responsibility to check if the integration period fits into the trigger period. If this requirement is not fulfilled, some triggers will be skipped.
|
|
Sorry for this ugly code, but since it works...
|
|
|
|
Important remark from sensor's specification: Rise of a RESET pulse must be set outside the video output period.
|
|
This must by guaranteed by user by means of proper timing. First video comes 18 cycles after negative slope of sensor reset and is 2 cycles long.
|
|
Then 2 cycles pause (here we can come with the positive reset slope), then next video and so on, every 4 cycles.
|
|
The minimum duration of reset low is 21 clocks, and 20 clocks for reset high.
|
|
Our logic requires that new reset low comes only after all channels are read.
|
|
*/
|
|
|
|
reg [11:0] sensor_rst_ctr;
|
|
reg sensor_rst_state;
|
|
|
|
assign out_sensor_rst = sensor_rst_state;
|
|
|
|
always @(posedge int_trg or posedge out_sensor_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
sensor_rst_state <= 0;
|
|
sensor_rst_ctr <= 0;
|
|
end
|
|
else if (int_trg)
|
|
begin
|
|
sensor_rst_ctr <= registers[1][27:16];
|
|
end
|
|
else
|
|
begin
|
|
if (sensor_rst_ctr == 0)
|
|
begin
|
|
sensor_rst_state <= 0;
|
|
end
|
|
else
|
|
begin
|
|
sensor_rst_ctr <= sensor_rst_ctr - 1'b1;
|
|
sensor_rst_state <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
assign reg0_read[10] = sensor_rst_state; //to status register
|
|
|
|
// *********************** ADC trigger logic ************************
|
|
/*
|
|
This piece produces triggering signals for ADC framework and cares about counting ADC samples.
|
|
It doesn't rely on incoming Trig signals, just generates them internally by counting clocks.
|
|
|
|
*/
|
|
|
|
reg [5:0] adc_current_channel;
|
|
reg [2:0] adc_trg_state;
|
|
reg [4:0] adc_trg_downctr;
|
|
wire int_adc_trg;
|
|
wire adc_trg_frame_ready;
|
|
|
|
localparam ADC_TRG_STATE_IDLE = 0; //nothing happens, RESET is high or initial state
|
|
localparam ADC_TRG_STATE_WAIT = 1; //waiting for the first video
|
|
localparam ADC_TRG_STATE_VIDEO1 = 2; //capturing video
|
|
localparam ADC_TRG_STATE_VIDEO2 = 3; //second cycle
|
|
localparam ADC_TRG_STATE_VIDEO3 = 4; //third cycle
|
|
localparam ADC_TRG_STATE_VIDEO4 = 5; //fourth cycle
|
|
localparam ADC_TRG_STATE_FINISH = 6; //finished readout, waiting for RESET high to enter idle
|
|
localparam ADC_TRG_STATE_INIT = 7; //used only for reset procedure
|
|
|
|
localparam ADC_TRG_INITIALWAIT = 5'd17; //initial wait for the first video
|
|
localparam ADC_TRG_VIDEOPERIOD = 4; //video output period
|
|
|
|
always @(posedge out_sensor_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
adc_current_channel <= 63;
|
|
adc_trg_downctr <= 0;
|
|
adc_trg_state <= ADC_TRG_STATE_INIT; //will go to IDLE when adc_trg_reset is high
|
|
end
|
|
else
|
|
case(adc_trg_state)
|
|
ADC_TRG_STATE_IDLE:
|
|
begin
|
|
if (~out_sensor_rst)
|
|
begin
|
|
adc_trg_downctr <= ADC_TRG_INITIALWAIT;
|
|
adc_current_channel <= 63;
|
|
adc_trg_state <= ADC_TRG_STATE_WAIT;
|
|
end
|
|
end
|
|
ADC_TRG_STATE_WAIT:
|
|
begin
|
|
adc_trg_downctr <= adc_trg_downctr - 1'b1;
|
|
if (adc_trg_downctr == 0)
|
|
begin
|
|
adc_trg_state <= ADC_TRG_STATE_VIDEO1;
|
|
end
|
|
end
|
|
ADC_TRG_STATE_VIDEO1:
|
|
begin
|
|
adc_current_channel <= adc_current_channel + 1'b1;
|
|
adc_trg_state = ADC_TRG_STATE_VIDEO2;
|
|
end
|
|
ADC_TRG_STATE_VIDEO2:
|
|
begin
|
|
adc_trg_state = ADC_TRG_STATE_VIDEO3;
|
|
end
|
|
ADC_TRG_STATE_VIDEO3:
|
|
begin
|
|
adc_trg_state = ADC_TRG_STATE_VIDEO4;
|
|
end
|
|
ADC_TRG_STATE_VIDEO4:
|
|
begin
|
|
if (adc_current_channel == 63)
|
|
adc_trg_state <= ADC_TRG_STATE_FINISH;
|
|
else
|
|
adc_trg_state = ADC_TRG_STATE_VIDEO1;
|
|
end
|
|
ADC_TRG_STATE_FINISH, ADC_TRG_STATE_INIT:
|
|
begin
|
|
if (out_sensor_rst)
|
|
adc_trg_state <= ADC_TRG_STATE_IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
//Producing internal trigger for ADC framework (see below)
|
|
assign int_adc_trg = (adc_trg_state == ADC_TRG_STATE_VIDEO1) ? 1'b1 : 1'b0;
|
|
|
|
assign adc_trg_frame_ready = (adc_trg_state == ADC_TRG_STATE_FINISH) ? 1'b1 : 1'b0; //trigger data collection machine when the frame is ready
|
|
|
|
assign reg0_read[11] = ~(adc_trg_state == ADC_TRG_STATE_IDLE); //to status register
|
|
assign reg0_read[12] = (adc_trg_state == ADC_TRG_STATE_FINISH); //to status register
|
|
|
|
// *********************** ADC logic ************************
|
|
/*
|
|
SPI interface for ADCs. Triggers conversion and reads out the data on each incoming int_adc_trg.
|
|
The ADC framework and SPI runs with full clock frequency or with half of it, depending on register setting.
|
|
The ADC works in the 'CS Mode, 3-Wire Without Busy Indicator Serial Interface'.
|
|
The CNV signal length is defined by register and expressed in ADC clocks.
|
|
It's user's responsibility to ensure that the CNV pulse is longer than 500 ns (required by ADC spec).
|
|
The ADC clock can be selected to be either full or half system clock.
|
|
*/
|
|
|
|
//Clock divider and selection
|
|
reg adc_halfclock;
|
|
wire int_adc_clk;
|
|
always @(posedge clk_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
adc_halfclock <= 0;
|
|
else
|
|
adc_halfclock <= ~adc_halfclock;
|
|
end
|
|
assign int_adc_clk = registers[0][2] ? adc_halfclock : clk_clk;
|
|
|
|
//The main state machine
|
|
reg [2:0] adc_state;
|
|
reg [5:0] adc_downctr;
|
|
|
|
localparam ADC_STATE_IDLE = 0; //waiting for conversion
|
|
localparam ADC_STATE_CNV = 1; //conversion on-going
|
|
localparam ADC_STATE_STARTREAD = 2; //prepare to read, conversion finished
|
|
localparam ADC_STATE_READ = 3; //reading data
|
|
localparam ADC_STATE_FINISH = 4; //copy data to global buffer
|
|
localparam ADC_STATE_FINISHED = 5; //wait until trigger low, then go to idle
|
|
|
|
always @(posedge int_adc_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
adc_downctr <= 0;
|
|
adc_state <= ADC_STATE_IDLE;
|
|
end
|
|
else
|
|
begin
|
|
case(adc_state)
|
|
ADC_STATE_IDLE:
|
|
begin
|
|
adc_downctr <= registers[0][29:24];
|
|
if (int_adc_trg)
|
|
adc_state <= ADC_STATE_CNV;
|
|
end
|
|
ADC_STATE_CNV:
|
|
begin
|
|
adc_downctr <= adc_downctr - 1'b1;
|
|
if (adc_downctr == 0)
|
|
adc_state <= ADC_STATE_STARTREAD;
|
|
end
|
|
ADC_STATE_STARTREAD:
|
|
begin
|
|
adc_downctr <= 15;
|
|
adc_state <= ADC_STATE_READ;
|
|
end
|
|
ADC_STATE_READ:
|
|
begin
|
|
adc_downctr <= adc_downctr - 1'b1;
|
|
if (adc_downctr == 0)
|
|
adc_state <= ADC_STATE_FINISH;
|
|
end
|
|
ADC_STATE_FINISH:
|
|
begin
|
|
adc_state <= ADC_STATE_FINISHED;
|
|
end
|
|
ADC_STATE_FINISHED:
|
|
begin
|
|
if (!int_adc_trg)
|
|
adc_state <= ADC_STATE_IDLE;
|
|
end
|
|
default
|
|
begin
|
|
adc_state <= ADC_STATE_IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
//Reading data - this happens on the negative slope of clock!
|
|
reg [15:0] adc_buffer[4:0]; //A shift register for SPI acquisition
|
|
reg [15:0] frame_buffer[319:0]; //The 'big buffer' for whole frame
|
|
|
|
always @(negedge int_adc_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
for (xi = 0; xi < 5; xi = xi+1)
|
|
adc_buffer[xi] <= 0;
|
|
end
|
|
else
|
|
begin
|
|
case(adc_state)
|
|
ADC_STATE_STARTREAD:
|
|
begin
|
|
for (xi = 0; xi < 5; xi = xi+1)
|
|
adc_buffer[xi] <= 0;
|
|
end
|
|
ADC_STATE_READ:
|
|
begin
|
|
for (xi = 0; xi < 5; xi = xi+1)
|
|
begin
|
|
adc_buffer[xi][15:1] <= adc_buffer[xi][14:0];
|
|
adc_buffer[xi][0] <= in_adc_data[xi];
|
|
end
|
|
end
|
|
ADC_STATE_FINISH:
|
|
begin
|
|
for (xi = 0; xi < 5; xi = xi+1)
|
|
frame_buffer[64*xi + adc_current_channel] <= adc_buffer[xi];
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
|
|
assign out_adc_cnv = (adc_state == ADC_STATE_CNV) ? 1'b1 : 1'b0;
|
|
assign out_adc_clk = (adc_state == ADC_STATE_READ) ? int_adc_clk : 1'b1;
|
|
|
|
|
|
// *********************** Putting data together and letting transmit it ************************
|
|
/*
|
|
The data is sent together with a 3-word header (6 16-bit numbers),
|
|
which is compatible with v.1 head structures:
|
|
typedef struct
|
|
{
|
|
unsigned short marker; //must be 0x5555
|
|
unsigned short command;
|
|
unsigned short length;
|
|
} command_header;
|
|
typedef struct
|
|
{
|
|
//unsigned short channel_id;
|
|
unsigned short local_ctr;
|
|
unsigned short global_ctr;
|
|
unsigned short sma_state;
|
|
} sync_frame;
|
|
|
|
*/
|
|
|
|
reg [2:0] merger_state;
|
|
|
|
localparam MERGER_STATE_IDLE = 0; //waiting for ADC
|
|
localparam MERGER_STATE_COLLECT = 1; //collect frame information
|
|
localparam MERGER_STATE_SEND = 2; //run Avalon sender
|
|
localparam MERGER_STATE_FINISH = 3; //roll to idle
|
|
|
|
always @(posedge clk_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
merger_state <= ADC_STATE_IDLE;
|
|
for (xi= 0; xi < 163; xi = xi+1)
|
|
transmit_buffer[xi] <= 0;
|
|
end
|
|
else
|
|
begin
|
|
case(merger_state)
|
|
MERGER_STATE_IDLE:
|
|
begin
|
|
if (adc_trg_frame_ready)
|
|
merger_state <= MERGER_STATE_COLLECT;
|
|
end
|
|
MERGER_STATE_COLLECT:
|
|
begin
|
|
transmit_buffer[0][31:16] <= 16'h5555; //command_header.marker = 0x5555
|
|
transmit_buffer[0][15:0] <= registers[2][31:16]; //command_header.command = reg2.31-16
|
|
transmit_buffer[1][31:16] <= 16'd323; //16'd652; //command_header.length = 320 samples + 6 sync bytes = 323 * 16bit //16'h143
|
|
transmit_buffer[1][15:0] <= local_sync_ctr[15:0]; //sync_frame.local_ctr
|
|
transmit_buffer[2][24:16] <= global_sync_ctr; //sync_frame.global_ctr
|
|
transmit_buffer[2][25] <= global_sync_error;
|
|
transmit_buffer[2][31:26] <= 0;
|
|
transmit_buffer[2][7:0] <= ext_input; //sync_frame.sma_state[7:0] = ext_input
|
|
transmit_buffer[2][15:8] <= registers[2][15:8]; //sync_frame.sma_state[15:8] = reg2.15-8
|
|
for (xi = 0; xi < 160; xi = xi+1)
|
|
begin
|
|
//transmit_buffer[xi+3][15:0] <= frame_buffer[2*xi][15:0]; //ADC data
|
|
//transmit_buffer[xi+3][31:16] <= frame_buffer[2*xi+1][15:0];
|
|
transmit_buffer[xi+3][31:16] <= frame_buffer[2*xi][15:0]; //ADC data //CHANGED 26.11.2019
|
|
transmit_buffer[xi+3][15:0] <= frame_buffer[2*xi+1][15:0];
|
|
end
|
|
merger_state <= MERGER_STATE_SEND;
|
|
|
|
end
|
|
MERGER_STATE_SEND:
|
|
begin
|
|
merger_state <= MERGER_STATE_FINISH;
|
|
end
|
|
MERGER_STATE_FINISH:
|
|
begin
|
|
if (~adc_trg_frame_ready)
|
|
merger_state <= MERGER_STATE_IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
//Generate Avalon trigger signal
|
|
assign start_transmission = (merger_state == MERGER_STATE_SEND) ? 1'b1 : 1'b0;
|
|
|
|
// *********************** Synchro counter update ************************
|
|
/*
|
|
Just increment sunchronization counter at every trigger.
|
|
The counter is reset by int_reset.
|
|
*/
|
|
|
|
always @(posedge clk_clk or posedge int_rst)
|
|
begin
|
|
if (int_rst)
|
|
begin
|
|
local_sync_ctr <= 0;
|
|
end
|
|
else
|
|
begin
|
|
if (int_trg)
|
|
local_sync_ctr <= local_sync_ctr + 1'b1;
|
|
end
|
|
end
|
|
|
|
|
|
|
|
// *********************** Synchronization serial ports ************************
|
|
/*
|
|
There is one TX and one RX module.
|
|
The TX is getting triggered by int_trg (same as for the main readout framework).
|
|
It sends 9 LSBs of the local synchro counter with programmed baudrate.
|
|
The default baudrate is 1 Mbps, which needs a setting of 50 for fclk=50MHz.
|
|
The RX puts freshly received data into global_sync_ctr. It does not need any triggering.
|
|
*/
|
|
|
|
wire tx_idle;
|
|
wire rx_idle;
|
|
assign reg0_read[13] = ~tx_idle;
|
|
assign reg0_read[14] = ~rx_idle;
|
|
|
|
serial_tx synchro_tx (
|
|
.clk (clk_clk), // full-speed clock
|
|
.rst (int_rst), // reset
|
|
.data (local_sync_ctr[8:0]), // data to send
|
|
.clk_divisor (registers[2][7:0]), // defines baudrate
|
|
.trigger (start_transmission), // pulse to start transmission - just AFTER collecting data from sensors
|
|
.tx (serial_tx), // tx serial connection
|
|
.idle (tx_idle) // 1 if sender in idle state
|
|
);
|
|
|
|
serial_rx synchro_rx(
|
|
.clk (clk_clk), // full-speed clock
|
|
.rst (int_rst), // reset
|
|
.clk_divisor (registers[2][7:0]), // defines baudrate
|
|
.rx (serial_rx), // rx serial connection
|
|
.newdata (), // pulses for new data received
|
|
.data (global_sync_ctr), // data received
|
|
.error (global_sync_error), // bad start or stop bit
|
|
.idle (rx_idle) // 1 if receiver in idle state
|
|
);
|
|
|
|
// *********************** Status output ************************
|
|
/*
|
|
The status of the module (== reg0_read[15:8]) is available at the output status bus.
|
|
It can be then routed to a scope or whatever for debugging
|
|
*/
|
|
|
|
assign status_out[7:0] = reg0_read[15:8];
|
|
|
|
|
|
endmodule
|