Commit eb7ba9a4 authored by Andrey Filippov's avatar Andrey Filippov

New modules for LWIR sensor

parent cc525170
/*!
* <b>Module:</b>sens_lepton3
* @file sens_lepton3.v
* @date 2015-05-10
* @author Andrey Filippov
*
* @brief Sensor interface with 12-bit for parallel bus
*
* @copyright Copyright (c) 2015 Elphel, Inc.
*
* <b>License:</b>
*
* sens_lepton3.v is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sens_lepton3.v is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> .
*
* Additional permission under GNU GPL version 3 section 7:
* If you modify this Program, or any covered work, by linking or combining it
* with independent modules provided by the FPGA vendor only (this permission
* does not extend to any 3-rd party modules, "soft cores" or macros) under
* different license terms solely for the purpose of generating binary "bitstream"
* files and/or simulating the code, the copyright holders of this Program give
* you the right to distribute the covered work without those independent modules
* as long as the source code for them is available from the FPGA vendor free of
* charge, and there is no dependence on any encrypted modules for simulating of
* the combined code. This permission applies to you if the distributed code
* contains all the components and scripts required to completely simulate it
* with at least one of the Free Software programs.
*/
`timescale 1ns/1ps
`include "system_defines.vh" // just for debugging histograms
module sens_lepton3 #(
parameter SENSIO_ADDR = 'h330,
parameter SENSIO_ADDR_MASK = 'h7f8,
parameter SENSIO_CTRL = 'h0,
parameter SENSIO_STATUS = 'h1,
parameter SENSIO_JTAG = 'h2,
parameter SENSIO_WIDTH = 'h3, // set line width (1.. 2^16) if 0 - use HACT
parameter SENSIO_DELAYS = 'h4, // 'h4..'h7 - each address sets 4 delays through 4 bytes of 32-bit data
parameter SENSIO_STATUS_REG = 'h21,
parameter SENS_JTAG_PGMEN = 8,
parameter SENS_JTAG_PROG = 6,
parameter SENS_JTAG_TCK = 4,
parameter SENS_JTAG_TMS = 2,
parameter SENS_JTAG_TDI = 0,
parameter SENS_CTRL_MRST= 0, // 1: 0
parameter SENS_CTRL_ARST= 2, // 3: 2
parameter SENS_CTRL_ARO= 4, // 5: 4
parameter SENS_CTRL_RST_MMCM= 6, // 7: 6
parameter SENS_CTRL_EXT_CLK= 8, // 9: 8
parameter SENS_CTRL_LD_DLY= 10, // 10
parameter SENS_CTRL_QUADRANTS = 12, // 17:12, enable - 20
parameter SENS_CTRL_QUADRANTS_WIDTH = 7, // 6,
parameter SENS_CTRL_ODD = 6, //
parameter SENS_CTRL_QUADRANTS_EN = 20, // 18:12, enable - 20 (1 bits reserved)
parameter LINE_WIDTH_BITS = 16,
parameter IODELAY_GRP ="IODELAY_SENSOR", // may need different for different channels?
parameter integer IDELAY_VALUE = 0,
parameter integer PXD_DRIVE = 12,
parameter PXD_IBUF_LOW_PWR = "TRUE",
parameter PXD_IOSTANDARD = "DEFAULT",
parameter PXD_SLEW = "SLOW",
parameter real SENS_REFCLK_FREQUENCY = 300.0,
parameter SENS_HIGH_PERFORMANCE_MODE = "FALSE",
parameter SENS_PHASE_WIDTH= 8, // number of bits for te phase counter (depends on divisors)
// parameter SENS_PCLK_PERIOD = 10.000, // input period in ns, 0..100.000 - MANDATORY, resolution down to 1 ps
parameter SENS_BANDWIDTH = "OPTIMIZED", //"OPTIMIZED", "HIGH","LOW"
parameter CLKIN_PERIOD_SENSOR = 10.000, // input period in ns, 0..100.000 - MANDATORY, resolution down to 1 ps
parameter CLKFBOUT_MULT_SENSOR = 8, // 100 MHz --> 800 MHz
parameter CLKFBOUT_PHASE_SENSOR = 0.000, // CLOCK FEEDBACK phase in degrees (3 significant digits, -360.000...+360.000)
parameter IPCLK_PHASE = 0.000,
parameter IPCLK2X_PHASE = 0.000,
parameter BUF_IPCLK = "BUFR",
parameter BUF_IPCLK2X = "BUFR",
parameter SENS_DIVCLK_DIVIDE = 1, // Integer 1..106. Divides all outputs with respect to CLKIN
parameter SENS_REF_JITTER1 = 0.010, // Expected jitter on CLKIN1 (0.000..0.999)
parameter SENS_REF_JITTER2 = 0.010,
parameter SENS_SS_EN = "FALSE", // Enables Spread Spectrum mode
parameter SENS_SS_MODE = "CENTER_HIGH",//"CENTER_HIGH","CENTER_LOW","DOWN_HIGH","DOWN_LOW"
parameter SENS_SS_MOD_PERIOD = 10000, // integer 4000-40000 - SS modulation period in ns
parameter STATUS_ALIVE_WIDTH = 4
)(
// programming interface
input mrst, // @posedge mclk, sync reset
input mclk, // global clock, half DDR3 clock, synchronizes all I/O through the command port
input [7:0] cmd_ad, // byte-serial command address/data (up to 6 bytes: AL-AH-D0-D1-D2-D3
input cmd_stb, // strobe (with first byte) for the command a/d
output [7:0] status_ad, // status address/data - up to 5 bytes: A - {seq,status[1:0]} - status[2:9] - status[10:17] - status[18:25]
output status_rq, // input request to send status downstream
input status_start, // Acknowledge of the first status packet byte (address)
input prst,
output prsts, // @pclk - includes sensor reset and sensor PLL reset
input pclk, // global clock input, SPI rate (10-20 MHz) - defines internal pixel rate
input sns_mclk, // 25Mz for the sensor
// sensor pads excluding i2c
inout spi_miso, // input
inout spi_mosi, // not used
output spi_cs, // output, externally connected to inout port
output spi_clk, // output, externally connected to inout port
inout [3:0] gpio, // only [3] may be used as input from sensor
output lwir_mclk, // output, externally connected to inout port
output lwir_mrst, // output, externally connected to inout port
output lwir_pwdn, // output, externally connected to inout port
inout mipi_dp, // input diff, not implemented in lepton3 sensor
inout mipi_dn, // input diff, not implemented in lepton3 sensor
inout mipi_clkp, // input diff, not implemented in lepton3 sensor
inout mipi_clkn, // input diff, not implemented in lepton3 sensor
inout senspgm, // SENSPGM I/O pin
inout sns_ctl, // npot used at all
// output
output [15:0] pxd, // @pclk
output hact, // @pclktwice per actual line
output sof, // @pclk
output eof // @pclk
);
wire fake_out;
wire fake_in;
wire [25:0] status; // added byte-wide xfpgatdo
wire cmd_we;
wire [2:0] cmd_a;
wire [31:0] cmd_data;
reg [31:0] data_r;
wire spi_clk_en_mclk;
wire sns_mclk_en_mclk;
reg [ 1:0] spi_clk_en_pclk;
reg [ 1:0] sns_mclk_en_lwir_mclk;
wire spi_miso_int;
wire spi_cs_int;
wire spi_mosi_int;
wire [ 3:0] gpio_in; // only [3] may be used
wire [ 3:0] gpio_out; // none currently used
wire [ 3:0] gpio_en = 0; // none currently used
wire lwir_mrst_int;
wire lwir_pwdn_int;
wire senspgm_int;
wire sns_ctl_int;
// not implemented in the sesnor, put dummy input buffer5s
wire mipi_dp_int;
wire mipi_dn_int;
wire mipi_clkp_int;
wire mipi_clkn_int;
// temporary?
assign fake_in = senspgm_int ^ sns_ctl_int ^ mipi_dp_int ^ mipi_dn_int ^ mipi_clkp_int ^ mipi_clkn_int;
// assign fake_out = data_r[31];
assign status[25] = fake_in;
// bit assignment will change
assign spi_clk_en_mclk = data_r[2];
assign sns_mclk_en_mclk = data_r[3];
always @(posedge mclk) begin
if (mrst) data_r <= 0;
else if (cmd_we) data_r <= cmd_data;
end
always @(posedge pclk) begin
spi_clk_en_pclk[1:0] <= {spi_clk_en_pclk[0],spi_clk_en_mclk};
end
always @(posedge sns_mclk) begin
sns_mclk_en_lwir_mclk[1:0] <= {sns_mclk_en_lwir_mclk[0],sns_mclk_en_mclk};
end
// implement I/O ports, including fake ones, to be able to assign them I/O pads
// generate clocka to sesnor output, controlled by control word bits
// SPI clock (10..20MHz)
oddr_ss #( // spi_clk
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW),
.DDR_CLK_EDGE ("OPPOSITE_EDGE"),
.INIT (1'b0),
.SRTYPE ("SYNC")
) spi_clk_i (
.clk (pclk), // input
.ce (spi_clk_en_pclk[1]), // input
.rst (prst), // input
.set (1'b0), // input
.din (2'b01), // input[1:0]
.tin (1'b0), // input
.dq (spi_clk) // output
);
// sensor master clock (25MHz)
oddr_ss #(
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW),
.DDR_CLK_EDGE ("OPPOSITE_EDGE"),
.INIT (1'b0),
.SRTYPE ("SYNC")
) lwir_mclk_i (
.clk (sns_mclk), // input
.ce (sns_mclk_en_lwir_mclk[1]), // input
.rst (prst), // input
.set (1'b0), // input
.din (2'b01), // input[1:0]
.tin (1'b0), // input
.dq (lwir_mclk) // output
);
iobuf #( // spi_miso
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) spi_miso_i (
.O (spi_miso_int), // output
.IO (spi_miso), // inout I/O pad
.I (1'b0), // input
.T (1'b1) // input - always off
);
iobuf #( // spi_mosi, not implemented in the sensor
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) spi_mosi_i (
.O (), // output - currently not used
.IO (spi_mosi), // inout I/O pad
.I (spi_mosi_int), // input
.T (!fake_out) // input - always off
);
iobuf #( // spi_cs
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) spi_cs_i (
.O (), // output - currently not used
.IO (spi_cs), // inout I/O pad
.I (spi_cs_int), // input
.T (1'b0) // input - always on
);
generate // gpio[3:0]
genvar i;
for (i=0; i < 4; i=i+1) begin: gpio_block
iobuf #(
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) gpio_i (
.O (gpio_in[i]), // output - currently not used
.IO (gpio[i]), // inout I/O pad
.I (gpio_out[i]), // input
.T (!gpio_en[i]) // input - always on
);
end
endgenerate
iobuf #( // lwir_mrst
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) lwir_mrst_i (
.O (), // output - currently not used
.IO (lwir_mrst), // inout I/O pad
.I (lwir_mrst_int), // input
.T (1'b0) // input - always on
);
iobuf #( // lwir_pwdn
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) lwir_pwdn_i (
.O (), // output - currently not used
.IO (lwir_pwdn), // inout I/O pad
.I (lwir_pwdn_int), // input
.T (1'b0) // input - always on
);
// MIPI - anyway it is not implemented, IOSTANDARD not known, put just single-ended input buffers
iobuf #( // mipi_dp
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) mipi_dp_i (
.O (mipi_dp_int), // output - currently not used
.IO (mipi_dp), // inout I/O pad
.I (1'b0), // input
.T (1'b1) // input - always off
);
iobuf #( // mipi_dn
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) mipi_dn_i (
.O (mipi_dn_int), // output - currently not used
.IO (mipi_dn), // inout I/O pad
.I (1'b0), // input
.T (1'b1) // input - always off
);
iobuf #( // mipi_clkp
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) mipi_clkp_i (
.O (mipi_clkp_int), // output - currently not used
.IO (mipi_clkp), // inout I/O pad
.I (1'b0), // input
.T (1'b1) // input - always off
);
iobuf #( // mipi_clkn
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) mipi_clkn_i (
.O (mipi_clkn_int), // output - currently not used
.IO (mipi_clkn), // inout I/O pad
.I (1'b0), // input
.T (1'b1) // input - always off
);
iobuf #( // senspgm
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) senspgm_i (
.O (senspgm_int), // output (detection of the SFE
.IO (senspgm), // inout I/O pad
.I (1'b0), // input
.T (1'b1) // input - always off
);
iobuf #( // sns_ctl
.DRIVE (PXD_DRIVE),
.IBUF_LOW_PWR (PXD_IBUF_LOW_PWR),
.IOSTANDARD (PXD_IOSTANDARD),
.SLEW (PXD_SLEW)
) sns_ctl_i (
.O (sns_ctl_int), // output - currently not used
.IO (sns_ctl), // inout I/O pad
.I (1'b0), // input
.T (1'b1) // input - always off
);
cmd_deser #(
.ADDR (SENSIO_ADDR),
.ADDR_MASK (SENSIO_ADDR_MASK),
.NUM_CYCLES (6),
.ADDR_WIDTH (3),
.DATA_WIDTH (32)
) cmd_deser_sens_io_i (
.rst (1'b0), // rst), // input
.clk (mclk), // input
.srst (mrst), // input
.ad (cmd_ad), // input[7:0]
.stb (cmd_stb), // input
.addr (cmd_a), // output[15:0]
.data (cmd_data), // output[31:0]
.we (cmd_we) // output
);
status_generate #(
.STATUS_REG_ADDR(SENSIO_STATUS_REG),
.PAYLOAD_BITS(26) // STATUS_PAYLOAD_BITS)
) status_generate_sens_io_i (
.rst (1'b0), // rst), // input
.clk (mclk), // input
.srst (mrst), // input
.we (set_status_r), // input
.wd (data_r[7:0]), // input[7:0]
.status ({status}), // input[25:0]
.ad (status_ad), // output[7:0]
.rq (status_rq), // output
.start (status_start) // input
);
// for debug/test alive
pulse_cross_clock pulse_cross_clock_vact_a_mclk_i (
.rst (irst), // input
.src_clk (ipclk), // input
.dst_clk (mclk), // input
.in_pulse (vact_out_pre && !vact_r), // input
.out_pulse (vact_a_mclk), // output
.busy() // output
);
pulse_cross_clock pulse_cross_clock_hact_ext_a_mclk_i (
.rst (irst), // input
.src_clk (ipclk), // input
.dst_clk (mclk), // input
.in_pulse (hact_ext && !hact_ext_r), // input
.out_pulse (hact_ext_a_mclk), // output
.busy() // output
);
pulse_cross_clock pulse_cross_clock_hact_a_mclk_i (
.rst (irst), // input
.src_clk (ipclk), // input
.dst_clk (mclk), // input
.in_pulse (hact_r && !hact_r2), // input
.out_pulse (hact_a_mclk), // output
.busy() // output
);
endmodule
/*!
* <b>Module:</b> vospi_packet_80
* @file vospi_packet_80.v
* @date 2019-04-08
* @author Andrey Filippov
*
* @brief VoSPI receive 160 byte packets
*
* @copyright Copyright (c) 2019 Elphel, Inc.
*
* <b>License </b>
*
* vospi_packet_80.v is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* vospi_packet_80.v is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> .
*
* Additional permission under GNU GPL version 3 section 7:
* If you modify this Program, or any covered work, by linking or combining it
* with independent modules provided by the FPGA vendor only (this permission
* does not extend to any 3-rd party modules, "soft cores" or macros) under
* different license terms solely for the purpose of generating binary "bitstream"
* files and/or simulating the code, the copyright holders of this Program give
* you the right to distribute the covered work without those independent modules
* as long as the source code for them is available from the FPGA vendor free of
* charge, and there is no dependence on any encrypted modules for simulating of
* the combined code. This permission applies to you if the distributed code
* contains all the components and scripts required to completely simulate it
* with at least one of the Free Software programs.
*/
`timescale 1ns/1ps
module vospi_packet_80#(
parameter VOSPI_PACKET_WORDS = 80,
parameter VOSPI_NO_INVALID = 1 // do not output invalid packets data
)(
input rst,
input clk,
input start, // @posedge clk
output spi_clken, // enable clock on spi_clk
output spi_cs, // active low
input miso, // input from the sensor
output [15:0] dout, // 16-bit data received,valid at dv and 15 cycles after
output dv, // data valid strobe
output packet_done, // packet received,
output packet_busy, // packet busy (same as spi_clken, !spi_cs)
output crc_err, // crc error, valid with packet_done
output [15:0] id, // packet ID (0x*f** - invlaid, if packet index = 20, 4 MSb - segment (- 0 invalid)
output packet_invalid, // set early, valid with packet done
output reg id_stb // id, packet invalid are set
);
reg [ 6:0] wcntr;
reg [ 3:0] bcntr;
wire pre_lsb_w;
reg lsb_r; // reading last bit from miso
reg copy_word; // copy 16-bit word from the SR (next after lsb_r);
reg [15:0] d_r;
reg [1:0] cs_r;
wire pre_last_w;
reg [ 2:0] packet_end;
reg set_id_r;
reg set_crc_r;
reg set_d_r;
reg den_r;
reg [15:0] d_sr;
reg [ 1:0] start_r;
reg dv_r;
reg [15:0] crc_r; // required crc
wire [15:0] crc_w; // current crc
reg [15:0] id_r;
wire [15:0] dmask;
reg packet_invalid_r;
assign packet_busy = cs_r[0]; // clk_en_r;
assign spi_clken = cs_r[0]; // clk_en_r;
assign spi_cs = ~cs_r[0];
assign pre_lsb_w = bcntr == 4'he;
assign pre_last_w = pre_lsb_w && (wcntr == (VOSPI_PACKET_WORDS + 1));
assign packet_done = packet_end[2];
assign id = id_r;
assign dmask = den_r ? 16'hffff: (wcntr[0]?16'h0: 16'h0fff);
assign crc_err = packet_end[2] && (crc_r != crc_w);
assign dv = dv_r;
assign dout = d_r;
assign packet_invalid = packet_invalid_r;
always @ (posedge clk) begin
if (rst || packet_end[0]) cs_r[0] <= 0;
else if (start) cs_r[1] <= 1;
cs_r[1] <= cs_r[0];
if (rst || !cs_r[0] || packet_end[0]) bcntr <= 0;
else bcntr <= bcntr + 1;
if (rst || !cs_r[0] || packet_end[0]) lsb_r <= 0;
else lsb_r <= pre_lsb_w;
copy_word <= !rst && lsb_r;
if (rst || !cs_r[0] || packet_end[0]) wcntr <= 0;
else if (lsb_r) wcntr <= wcntr + 1;
if (rst || !cs_r[0] ) packet_end <= 0;
else packet_end <= {packet_end[1:0], pre_last_w};
if (rst) start_r <= 0;
else start_r <= {start_r[0],start};
set_id_r <= !rst && (wcntr == 0) && lsb_r;
set_crc_r <= !rst && (wcntr == 1) && lsb_r;
set_d_r <= !rst && den_r && lsb_r;
if (rst || !cs_r[1]) den_r <= 0;
else if (set_crc_r) den_r <= 1;
if (cs_r[0]) d_sr <= {miso, d_sr[15:1]};
if (set_id_r) id_r <= d_sr;
if (set_crc_r) crc_r <= d_sr;
if (set_d_r) d_r <= d_sr;
dv_r <= set_d_r && !(packet_invalid_r && VOSPI_NO_INVALID);
if (rst || start) packet_invalid_r <= 0;
else if (set_id_r) packet_invalid_r <= (d_sr[11:8] == 4'hf);
id_stb <= set_id_r;
end
crc16_x16x12x5x0 crc16_x16x12x5x0_i (
.clk (clk), // input
.srst (!cs_r[1]), // input
.en (copy_word), // input
.din (d_sr & dmask), // input[15:0]
.dout (crc_w) // output[15:0]
);
endmodule
/*!
* <b>Module:</b> vospi_segment_61
* @file vospi_segment_61.v
* @date 2019-04-08
* @author eyesis
*
* @brief Read one 61-packet segment from the sensor
*
* @copyright Copyright (c) 2019 Elphel, Inc.
*
* <b>License </b>
*
* vospi_segment_61.v is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* vospi_segment_61.v is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> .
*
* Additional permission under GNU GPL version 3 section 7:
* If you modify this Program, or any covered work, by linking or combining it
* with independent modules provided by the FPGA vendor only (this permission
* does not extend to any 3-rd party modules, "soft cores" or macros) under
* different license terms solely for the purpose of generating binary "bitstream"
* files and/or simulating the code, the copyright holders of this Program give
* you the right to distribute the covered work without those independent modules
* as long as the source code for them is available from the FPGA vendor free of
* charge, and there is no dependence on any encrypted modules for simulating of
* the combined code. This permission applies to you if the distributed code
* contains all the components and scripts required to completely simulate it
* with at least one of the Free Software programs.
*/
`timescale 1ns/1ps
module vospi_segment_61#(
parameter VOSPI_PACKET_WORDS = 80,
parameter VOSPI_PACKETS_PER_LINE = 2,
parameter VOSPI_SEGMENT_FIRST = 1,
parameter VOSPI_SEGMENT_LAST = 4,
parameter VOSPI_PACKET_FIRST = 0,
parameter VOSPI_PACKET_LAST = 60,
parameter VOSPI_PACKET_TTT = 20, // line number where segment number is provided
parameter VOSPI_SOF_TO_HACT = 2, // clock cycles from SOF to HACT
parameter VOSPI_HACT_TO_HACT_EOF = 2 // minimal clock cycles from HACT to HACT or to EOF
// parameter VOSPI_HACT_TO_EOF = 2 // clock cycles from HACT to EOF
)(
input rst,
input clk,
input start, // start reading segment
input [3:0] exp_segment, // expected segment (1,2,3,4)
input segm0_ok, // OK to read segment 0 instead of the current ( exp_segment still has to be 1..4)
input out_en, // enable frame output generation (will finish current frame if disabled, single-pulse
// runs a single frame
// SPI signals
output spi_clken, // enable clock on spi_clk
output spi_cs, // active low
input miso, // input from the sensor
output [15:0] dout, // 16-bit data received
output hact, // data valid
output sof, // start of frame
output eof, // end of frame
output crc_err, // crc error happened for any packet (valid at eos)
output [3:0] id // segment number (valid at eos)
);
localparam VOSPI_PACKETS_FRAME = (VOSPI_SEGMENT_LAST - VOSPI_SEGMENT_FIRST + 1) *
(VOSPI_PACKET_LAST - VOSPI_PACKET_FIRST + 1);
localparam VOSPI_LINE_WIDTH = VOSPI_PACKET_WORDS * VOSPI_PACKETS_PER_LINE;
// save fifo write pointer, write packet full index (in the frame)
// read and buffer first 20 valid packets, then (in packet 20) verify that the segment is correct.
// if correct - generate sof if appropriate/eof), proceed with hact, readout data
// if incorrect - restore write pointer and write packet index, read rest of the segment without writing to the buffer
// reg first_segment_in; // processing first segment in a frame
reg last_segment_in; // processing last segment in a frame
reg [10:0] segment_start_waddr; // write address for the beginning of the current packet
reg [10:0] waddr; // current frite address
reg [ 7:0] segment_start_packet; // full packet number in a fragment for the start of the segment
reg [ 7:0] full_packet; // current full packet number in a fragment
reg [ 7:0] full_packet_verified; // next packet verified (will not be discarded later)
reg full_packet_frame; // lsb of the input frame // not needed?
reg discard_set; // start discard_segment
wire segment_good_w; // recognized expected segment, OK to read FIFO
reg segment_good; // recognized expected segment, OK to read FIFO
reg discard_segment; // read and discard the rest of the current segment
reg running_good; // passed packet 20
wire packet_done; // read full packet
wire packet_busy; // receiving SPI packet (same as spi_clken, !spi_cs)
wire packet_dv; // read full packet
wire [15:0] packet_dout; // read full packet
wire [15:0] packet_id;
wire id_stb;
wire is_first_segment_w;
wire is_last_segment_w;
reg start_d;
wire segment_stb;
reg crc_err_r;
wire packet_crc_err;
reg packet_start;
wire we; // write data to buffer
wire segment_done;
reg segment_busy;
reg segment_running; // may be discarded
reg [3:0] segment_id_r;
wire frame_in_done;
// reg packet_running; // may be discarded
assign is_first_segment_w = (exp_segment == VOSPI_SEGMENT_FIRST);
assign is_last_segment_w = (exp_segment == VOSPI_SEGMENT_LAST);
assign segment_good_w = (packet_id[15:12] == exp_segment) || ((packet_id[15:12] == 0) && segm0_ok);
assign segment_stb = id_stb && (packet_id[11:0] == VOSPI_PACKET_TTT);
assign we = segment_running && !discard_segment && packet_dv;
assign crc_err = crc_err_r;
assign segment_done = segment_running && packet_done && (packet_id[11:0] == VOSPI_PACKET_LAST) ;
assign id = segment_id_r;
assign frame_in_done = segment_done && last_segment_in;
// To Buffer
always @ (posedge clk) begin
// if (rst) first_segment_in <= 0;
// else if (start) first_segment_in <= is_first_segment_w;
if (rst) last_segment_in <= 0;
else if (start) last_segment_in <= is_last_segment_w;
start_d <= start;
discard_set <= segment_running && !discard_segment && segment_stb && !segment_good_w;
segment_good <= segment_running && !discard_segment && segment_stb && segment_good_w;
if (segment_running && !discard_segment && segment_stb) segment_id_r <= packet_id[15:12];
if (start) discard_segment <= 0;
else if (discard_set) discard_segment <= 1;
if (start) running_good <= 0;
else if (segment_good) running_good <= 1;
if (start_d || running_good) full_packet_verified <= full_packet;
if (start_d) segment_start_packet <= full_packet;
if (start_d) segment_start_waddr <= waddr;
if (rst || (start && is_first_segment_w)) full_packet <= 0;
else if (discard_set) full_packet <= segment_start_packet;
else if (!discard_segment && packet_done) full_packet <= full_packet + 1;
if (rst || start) crc_err_r <= 0;
else if (packet_done && packet_crc_err) crc_err_r <= 0;
if (rst) segment_busy <= 0;
else if (start) segment_busy <= 1'b1;
else if (segment_done) segment_busy <= 1'b0;
if (!segment_busy || start) segment_running <= 0;
else if (id_stb && (packet_id[11:0] == VOSPI_PACKET_FIRST)) segment_running <= 1;
packet_start <= !rst && !packet_busy && segment_busy;
if (rst) waddr <= 0;
else if (discard_set) waddr <= segment_start_waddr;
else if (we) waddr <= waddr + 1;
if (rst) full_packet_frame <= 0; // not needed?
else if (frame_in_done) full_packet_frame <=~full_packet_frame;
end
// From buffer, generating frame
reg out_request;
reg out_frame;
wire sof_w;
reg sof_r;
wire eof_w;
reg [ 2:0] eof_r;
wire start_out_frame_w;
reg [ 7:0] full_packet_out; // current full packet number in a fragment
wire [ 8:0] packets_avail; // line_avail; //
reg out_pending; // frame read from the sensor, but not yet output
// reg packet_out_done;
wire frame_out_done_w; // last packet data was sent out
wire frame_dav;
wire hact_start_w; // (hact will start next cycle
wire hact_end_w;
reg [ 7:0] duration_cntr;
reg [2:0] hact_r;
reg pend_eof_r;
reg [10:0] raddr;
assign start_out_frame_w = segment_good && is_first_segment_w && out_request;
assign packets_avail = {1'b0,full_packet_verified} - {1'b0,full_packet_out} - VOSPI_PACKETS_PER_LINE;
// assign frame_out_done_w = packet_out_done && (full_packet_out == (VOSPI_PACKETS_FRAME - 1));
assign frame_out_done_w = hact_end_w && (full_packet_out == (VOSPI_PACKETS_FRAME - 1));
assign frame_dav = !packets_avail[8] || out_pending;
assign hact_start_w = out_frame && (duration_cntr == 0) && !hact_r[0] && frame_dav;
assign hact_end_w = (duration_cntr == 0) && hact_r[0];
assign eof_w = out_frame && (duration_cntr == 0) && pend_eof_r;
assign sof_w = rst && start_out_frame_w;
assign hact = hact_r[2];
assign eof = eof_r[2];
assign sof = sof_r;
always @ (posedge clk) begin
if (rst) hact_r <= 0;
else hact_r <= {hact_r[1:0], hact_start_w | (hact_r[0] & ~hact_end_w)};
if (rst || start_out_frame_w) full_packet_out <= 0;
else if (hact_end_w) full_packet_out <= full_packet_out + VOSPI_PACKETS_PER_LINE;
if (rst) out_request <= 0;
else if (out_en) out_request <= 1;
else if (sof_r) out_request <= 0;
if (rst) out_frame <= 0;
else if (start_out_frame_w) out_frame <= 1;
else if (eof_r[0]) out_frame <= 0;
sof_r <= sof_w;
eof_r <= {eof_r[1:0], eof_w};
if (rst) out_pending <= 0; // not needed?
else if (frame_in_done) out_pending <= 1;
else if (frame_out_done_w) out_pending <= 0;
if (rst) pend_eof_r <= 0; // not needed?
else if (frame_out_done_w) pend_eof_r <= 1;
else if (eof_r[0]) pend_eof_r <= 0;
if (rst) duration_cntr <= 0;
else if (start_out_frame_w) duration_cntr <= VOSPI_SOF_TO_HACT;
else if (hact_start_w) duration_cntr <= VOSPI_LINE_WIDTH - 1;
else if (hact_end_w) duration_cntr <= VOSPI_HACT_TO_HACT_EOF;
else if (|duration_cntr) duration_cntr <= duration_cntr - 1;
if (sof_w) raddr <= segment_start_waddr;
else if (hact_r[0]) raddr <= raddr + 1;
end
vospi_packet_80 #(
.VOSPI_PACKET_WORDS(80),
.VOSPI_NO_INVALID(1)
) vospi_packet_80_i (
.rst (rst), // input
.clk (clk), // input
.start (packet_start), // input
.spi_clken (spi_clken), // output
.spi_cs (spi_cs), // output
.miso (miso), // input
.dout (packet_dout), // output[15:0]
.dv (packet_dv), // output
.packet_done (packet_done), // output
.packet_busy (packet_busy), // output
.crc_err (packet_crc_err), // output
.id (packet_id), // output[15:0]
.packet_invalid (), // output - not used, processed internally, no dv generated
.id_stb (id_stb) // output reg
);
ram_var_w_var_r #(
.COMMENT("vospi_segment"),
.REGISTERS(1),
.LOG2WIDTH_WR(4),
.LOG2WIDTH_RD(4)
) buf_i (
.rclk (clk), // input
.raddr (raddr), // input[11:0]
.ren (hact_r[0]), // input
.regen (hact_r[1]), // input
.data_out (dout), // output[7:0]
.wclk (clk), // input
.waddr (waddr), // input[11:0]
.we (we), // input
.web (8'hff), // input[7:0]
.data_in (packet_dout) // input[7:0]
);
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment