upsilon/firmware/rtl/raster/raster.v

308 lines
7.7 KiB
Verilog

module raster #(
parameter SAMPLEWID = 9,
parameter DAC_DATA_WID = 20,
parameter DAC_WID = 24,
parameter DAC_WAIT_BETWEEN_CMD = 10,
parameter TIMER_WID = 4,
parameter STEPWID = 16,
parameter ADCNUM = 9,
parameter MAX_ADC_DATA_WID = 24
) (
input clk,
input arm,
output reg finished,
output reg running,
/* Amount of samples in one line (forward) */
input [SAMPLEWID-1:0] max_samples_in,
/* Amount of lines in the output. */
input [SAMPLEWID-1:0] max_lines_in,
/* Wait time after each step. */
input [TIMER_WID-1:0] settle_time_in,
/* Each step goes (x,y) -> (x + dx, y + dy) forward for each line of
* the output. */
input signed [DAC_DATA_WID-1:0] dx_in,
input signed [DAC_DATA_WID-1:0] dy_in,
/* X and Y DAC piezos */
output x_arm,
output [DAC_WID-1:0] x_to_dac,
/* verilator lint_off UNUSED */
input [DAC_WID-1:0] x_from_dac,
input x_finished,
output y_arm,
output [DAC_WID-1:0] y_to_dac,
/* verilator lint_off UNUSED */
input [DAC_WID-1:0] y_from_dac,
input y_finished,
/* Connections to all possible ADCs. These are connected to SPI masters
* and they will automatically extend ADC value lengths to their highest
* values. */
output reg [ADCNUM-1:0] adc_arm,
input [MAX_ADC_DATA_WID-1:0] adc_data [ADCNUM-1:0],
input [ADCNUM-1:0] adc_finished,
/* Bitmap for which ADCs are used. */
input [ADCNUM-1:0] adc_used_in,
/* RAM DMA. This is generally not directly connected to the
* DMA IP. A shim is used in order to write multiple words
* to memory. */
output reg [MAX_ADC_DATA_WID-1:0] data,
output reg mem_commit,
input mem_finished
);
/* During a scan, some of the ADCs will be scanned, but some will not.
* The data are packed in such a way so that the most significant
* word will contain the highest enabled ADC number, and the least
* significant word will contain the lowest enabled ADC number (and so
* on in between).
*
* There's not a good way to precalculate this so instead the check
* is done at each "send" stage.
*/
/* State machine:
┏━━━━ WAIT ON ARM
↑ ↓ (arm -> 1)
┃ REQUEST DAC VALUES
┃ ↓ (when x and y values are requested)
┃ OBTAIN DAC VALUES
┃ ↓ (when x and y values are measured)
┃ ┏━LOOP FORWARD WITHOUT MEASUREMENT
┃ ↑ ↓ (when enough steps are taken)
┃ ┃ GET ADC VALUES
┃ ┃ ↓ (when all ADC values are obtained)
┃ ┃ SEND THROUGH FIFO
┃ ┃ ↓ (when finished)
┃ ┏━┫ ┃
┃ ↑ ┗━━━←━┫
┃ ┃ ┃ (when at the end of a line)
┃ ┃ ┃
┃ ┃ ┏━LOOP BACKWARD WITHOUT MEASUREMENT
┃ ┃ ↑ ↓ (when enough steps are taken)
┃ ┃ ┃ GET ADC VALUES, BACKWARDS MEASUREMENT
┃ ┃ ┃ ↓ (when all ADC values are obtained)
┃ ┃ ┃ SEND THROUGH FIFO, BACKWARDS MEASUREMENT
┃ ┃ ┃ ↓ (when finished)
┃ ┃ ┃ ┃
┃ ┃ ┗━━━←━┫
┃ ┃ ↓
┃ ┗━━━━━━━┫
┃ ↓ (when the image is finished)
┃ ┃
┃ WAIT FOR ARM DEASSERT
┃ ↓ (when arm = 0)
┗━━━━━━━━━┛
*/
localparam WAIT_ON_ARM = 0;
localparam GET_DAC_VALUES = 1;
localparam REQUEST_DAC_VALUES = 2;
localparam MEASURE = 3;
localparam SCAN_ADC_VALUES = 4;
localparam SEND_VALUE = 5;
localparam ADVANCE_DAC_WRITE = 6;
localparam WAIT_ADVANCE = 7;
localparam ON_ADC_FINISHED = 8;
localparam NEXT_LINE = 9;
localparam WAIT_ON_ARM_DEASSERT = 10;
localparam STATE_WID = 4;
/********** Loop State ***********/
reg [STATE_WID-1:0] state = WAIT_ON_ARM;
reg [SAMPLEWID-1:0] sample = 0;
reg [SAMPLEWID-1:0] line = 0;
reg [TIMER_WID-1:0] counter = 0;
reg signed [DAC_DATA_WID-1:0] x_val = 0;
reg signed [DAC_DATA_WID-1:0] y_val = 0;
/* Buffer to store all measured ADC values. This
* is shifted until it is all zeros to determine
* which ADC values should be read off.
*/
reg [ADCNUM-1:0] adc_used_tmp = 0;
/* Buffer to store ADC data. The buffers are permuted in order
* for the design to read off the proper values into RAM.
*/
reg [MAX_ADC_DATA_WID-1:0] adc_data_tmp [ADCNUM-1:0];
/********** Loop Parameters *************/
reg [ADCNUM-1:0] adc_used = 0;
reg is_reverse = 0;
reg signed [DAC_DATA_WID-1:0] dx = 0;
reg signed [DAC_DATA_WID-1:0] dy = 0;
reg [TIMER_WID-1:0] settle_time = 0;
reg [SAMPLEWID-1:0] max_samples = 0;
reg [SAMPLEWID-1:0] max_lines = 0;
reg [STEPWID-1:0] steps_per_sample = 0;
/* Reading ADC data.
* If this doesn't work, a gigantic vector with large bit shifts
* can also work.
*/
genvar ii;
generate for (ii = 0; ii < ADCNUM - 1; ii = ii + 1) begin
always @ (posedge clk) begin
if (state == SCAN_ADC_VALUES) begin
adc_data_tmp[ii] <= adc_data_tmp[ii+1];
end
end
end endgenerate
always @ (posedge clk) begin
case (state)
WAIT_ON_ARM: begin if (arm) begin
adc_used <= adc_used_in;
dx <= dx_in;
dy <= dy_in;
max_samples <= max_samples_in;
max_lines <= max_lines_in;
settle_time <= settle_time_in;
is_reverse <= 0;
sample <= 0;
line <= 0;
x_to_dac <= {4'b1001, 20'b0};
y_to_dac <= {4'b1001, 20'b0};
x_arm <= 1;
y_arm <= 1;
adc_arm <= 0;
end else begin
running <= 0;
end end
REQUEST_DAC_VALUES: begin
if (x_finished && y_finished) begin
x_to_dac <= 0;
y_to_dac <= 0;
x_arm <= 0;
y_arm <= 0;
state <= GET_DAC_VALUES;
counter <= 0;
end
end
GET_DAC_VALUES: if (counter < DAC_WAIT_BETWEEN_CMD) begin
counter <= counter + 1;
if (!arm) state <= WAIT_ON_ARM;
end else if (!x_arm || !y_arm) begin
x_arm <= 1;
y_arm <= 1;
end else if (x_finished && y_finished) begin
x_val <= x_from_dac[DAC_DATA_WID-1:0];
y_val <= y_from_dac[DAC_DATA_WID-1:0];
x_arm <= 0;
y_arm <= 0;
counter <= 0;
state <= WAIT_ADVANCE;
end
WAIT_ADVANCE: begin
if (counter < settle_time) begin
if (!arm) state <= WAIT_ON_ARM;
counter <= counter + 1;
end else begin
adc_arm <= adc_used;
adc_used_tmp <= adc_used;
state <= MEASURE;
end
end
MEASURE: begin
if (adc_finished == adc_arm) begin
adc_arm <= 0;
state <= SCAN_ADC_VALUES;
counter <= 0;
end
end
SCAN_ADC_VALUES: begin
if (adc_used_tmp == 0) begin
state <= ON_ADC_FINISHED;
if (sample == max_samples) begin
dx <= ~dx + 1;
dy <= ~dy + 1;
is_reverse <= !is_reverse;
sample <= 0;
if (is_reverse) begin
state <= NEXT_LINE;
end else begin
state <= ADVANCE_DAC_WRITE;
end
end else begin
state <= ADVANCE_DAC_WRITE;
end
end else begin
adc_used_tmp <= adc_used_tmp << 1;
if (adc_used_tmp[ADCNUM-1]) begin
state <= SEND_VALUE;
data <= adc_data_tmp[ADCNUM-1];
mem_commit <= 1;
end
end
end
SEND_VALUE: if (mem_finished) begin
if (!arm) state <= WAIT_ON_ARM;
else state <= SCAN_ADC_VALUES;
end
ADVANCE_DAC_WRITE: begin
if (!x_arm || !y_arm) begin
x_val <= x_val + dx;
y_val <= y_val + dy;
x_to_dac <= {4'b0001, x_val + dx};
y_to_dac <= {4'b0001, y_val + dy};
x_arm <= 1;
y_arm <= 1;
sample <= sample + 1;
end else if (x_finished && y_finished) begin
counter <= 0;
state <= WAIT_ADVANCE;
x_arm <= 0;
y_arm <= 0;
end
end
NEXT_LINE: begin
if (!x_arm && !y_arm) begin
if (line == max_lines) begin
state <= WAIT_ON_ARM_DEASSERT;
finished <= 1;
running <= 0;
end else begin
/* rotation of (dx,dy) by 90° -> (dy, -dx) */
x_val <= x_val + dy;
x_to_dac <= {4'b0001, x_val + dx_vert};
x_arm <= 1;
y_val <= y_val - dx;
y_to_dac <= {4'b0001, y_val + dy_vert};
y_arm <= 1;
line <= line + 1;
end
end else if (x_finished && y_finished) begin
counter <= 0;
state <= WAIT_ADVANCE;
x_arm <= 0;
y_arm <= 0;
end
end
WAIT_ON_ARM_DEASSERT: begin
if (!arm) begin
state <= WAIT_ON_ARM;
finished <= 0;
end
end
endcase
end
endmodule