Commit 6b2c4c47 authored by Andrey Filippov's avatar Andrey Filippov

working on files realted to sensor channels, added frame sequences to...

working on files realted to sensor channels, added frame sequences to mcntrl_linear_rw.v and mcntrl_tiled_rw.v
parent cdf97c98
/*******************************************************************************
* Module: cmd_readback
* Date:2015-05-05
* Author: andrey
* Description: Store control register data and readback
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* cmd_readback.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.
*
* cmd_readback.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/> .
*******************************************************************************/
`timescale 1ns/1ps
module cmd_readback#(
parameter AXI_WR_ADDR_BITS= 14,
parameter AXI_RD_ADDR_BITS = 14,
parameter CONTROL_RBACK_DEPTH= 10, //
parameter CONTROL_ADDR = 'h2000, // AXI write address of control write registers
parameter CONTROL_ADDR_MASK = 'h3c00, // AXI write address of control registers
parameter CONTROL_RBACK_ADDR = 'h2000, // AXI write address of control write registers
parameter CONTROL_RBACK_ADDR_MASK = 'h3c00 // AXI write address of control registers
)(
input rst,
input mclk,
input axi_clk,
input [AXI_WR_ADDR_BITS-1:0] par_waddr, // parallel address
input [31:0] par_data, // parallel 32-bit data
input ad_stb, // low address output strobe (and parallel A/D)
input [AXI_RD_ADDR_BITS-1:0] axird_pre_araddr, // status read address, 1 cycle ahead of read data
input axird_start_burst, // start of read burst, valid pre_araddr, save externally to control ext. dev_ready multiplexer
input [CONTROL_RBACK_DEPTH-1:0] axird_raddr, // .raddr(read_in_progress?read_address[9:0]:10'h3ff), // read address
input axird_ren, // .ren(bram_reg_re_w) , // read port enable
// input axird_regen, //==axird_ren?? - remove? .regen(bram_reg_re_w), // output register enable
output [31:0] axird_rdata, // combinatorial multiplexed (add external register layer, modify axibram_read?) .data_out(rdata[31:0]), // data out
output axird_selected // axird_rdata contains cvalid data from this module, vcalid next after axird_start_burst
);
localparam integer DATA_2DEPTH = (1<<CONTROL_RBACK_DEPTH)-1;
reg [31:0] ram [0:DATA_2DEPTH];
reg [CONTROL_RBACK_DEPTH-1:0] waddr;
reg we;
reg [31: 0] wdata;
wire select_w;
reg select_r;
reg select_d;
wire rd;
wire regen;
reg [31:0] axi_rback_rdata;
reg [31:0] axi_rback_rdata_r;
reg axird_regen;
wire we_w;
assign we_w = ad_stb && (((par_waddr ^ CONTROL_ADDR) & CONTROL_ADDR_MASK)==0);
assign select_w = ((axird_pre_araddr ^ CONTROL_RBACK_ADDR) & CONTROL_RBACK_ADDR_MASK)==0;
assign rd = axird_ren && select_r;
assign regen = axird_regen && select_d;
assign axird_rdata=axi_rback_rdata_r;
assign axird_selected = select_r;
always @ (posedge rst or posedge axi_clk) begin
if (rst) axird_regen <= 0;
else axird_regen <= axird_ren;
if (rst) select_r <= 0;
else if (axird_start_burst) select_r <= select_w;
end
always @ (posedge axi_clk) begin
if (rd) axi_rback_rdata <= ram[axird_raddr];
if (regen) axi_rback_rdata_r <= axi_rback_rdata;
select_d <= select_r;
end
always @ (posedge rst or posedge mclk) begin
if (rst) we <= 0;
else we <= we_w;
end
always @ (posedge mclk) begin
if (we_w) wdata <= par_data;
if (we_w) waddr <= par_waddr[CONTROL_RBACK_DEPTH-1:0];
end
always @ (posedge mclk) begin
if (we) ram[waddr] <= wdata; // shifted data here
end
endmodule
......@@ -36,7 +36,7 @@ TODO: Maybe allow less rows with different sequence (no autoprecharge/no activat
number fo rows>1!
Known issues:
1: Most tile heights cause timing violation. Valid height mod 8 can be 0,6,7 (1,2,3,4,5 - invalid)
1: Most tile heights cause timing violation. Valid height mod 8 can be 0,6,7 (1,2,3,4,5 - invalid - wrong - that was for tile16 mode)
2: With option "keep_open" there should be no page boundary crossings, caller only checks the first line, and if window full width
is not multiple of CAS page, page crossings can appear on other than first line (fix caller to use largest common divider of page and
frame full width? Seems easy to fix
......
......@@ -36,7 +36,7 @@ TODO: Maybe allow less rows with different sequence (no autoprecharge/no activat
number fo rows>1!
Known issues:
1: Most tile heights cause timing violation. Valid height mod 8 can be 0,6,7 (1,2,3,4,5 - invalid)
1: Most tile heights cause timing violation. Valid height mod 8 can be 0,6,7 (1,2,3,4,5 - invalid- wrong - that was for tile16 mode)
2: With option "keep_open" there should be no page boundary crossings, caller only checks the first line, and if window full width
is not multiple of CAS page, page crossings can appear on other than first line (fix caller to use largest common divider of page and
frame full width? Seems easy to fix
......
......@@ -26,16 +26,19 @@ module mcntrl_linear_rw #(
parameter COLADDR_NUMBER= 10,
parameter NUM_XFER_BITS= 6, // number of bits to specify transfer length
parameter FRAME_WIDTH_BITS= 13, // Maximal frame width - 8-word (16 bytes) bursts
parameter FRAME_HEIGHT_BITS= 16, // Maximal frame height
parameter FRAME_HEIGHT_BITS= 16, // Maximal frame height
parameter LAST_FRAME_BITS= 16, // number of bits in frame counter (before rolls over)
parameter MCNTRL_SCANLINE_ADDR= 'h120,
parameter MCNTRL_SCANLINE_MASK= 'h3f0, // both channels 0 and 1
parameter MCNTRL_SCANLINE_MODE= 'h0, // set mode register: {extra_pages[1:0],write,enable,!reset}
parameter MCNTRL_SCANLINE_MODE= 'h0, // set mode register: {repet,single,rst_frame,na[2:0],extra_pages[1:0],write_mode,enable,!reset}
parameter MCNTRL_SCANLINE_STATUS_CNTRL= 'h1, // control status reporting
parameter MCNTRL_SCANLINE_STARTADDR= 'h2, // 22-bit frame start address (3 CA LSBs==0. BA==0)
parameter MCNTRL_SCANLINE_FRAME_FULL_WIDTH='h3, // Padded line length (8-row increment), in 8-bursts (16 bytes)
parameter MCNTRL_SCANLINE_WINDOW_WH= 'h4, // low word - 13-bit window width (0->'h4000), high word - 16-bit frame height (0->'h10000)
parameter MCNTRL_SCANLINE_WINDOW_X0Y0= 'h5, // low word - 13-bit window left, high word - 16-bit window top
parameter MCNTRL_SCANLINE_WINDOW_STARTXY= 'h6, // low word - 13-bit start X (relative to window), high word - 16-bit start y
parameter MCNTRL_SCANLINE_FRAME_SIZE= 'h3, // 22-bit frame start address increment (3 CA LSBs==0. BA==0)
parameter MCNTRL_SCANLINE_FRAME_LAST= 'h4, // 16-bit last frame number in the buffer
parameter MCNTRL_SCANLINE_FRAME_FULL_WIDTH='h5, // Padded line length (8-row increment), in 8-bursts (16 bytes)
parameter MCNTRL_SCANLINE_WINDOW_WH= 'h6, // low word - 13-bit window width (0->'h4000), high word - 16-bit frame height (0->'h10000)
parameter MCNTRL_SCANLINE_WINDOW_X0Y0= 'h7, // low word - 13-bit window left, high word - 16-bit window top
parameter MCNTRL_SCANLINE_WINDOW_STARTXY= 'h8, // low word - 13-bit start X (relative to window), high word - 16-bit start y
// Start XY can be used when read command to start from the middle
// TODO: Add number of blocks to R/W? (blocks can be different) - total length?
// Read back current address (for debugging)?
......@@ -65,6 +68,7 @@ module mcntrl_linear_rw #(
// optional I/O for channel synchronization
output [FRAME_HEIGHT_BITS-1:0] line_unfinished, // number of the current (ufinished ) line, REALATIVE TO FRAME, NOT WINDOW?.
input suspend, // suspend transfers (from external line number comparator)
output [LAST_FRAME_BITS-1:0] frame_number, // current frame number (for multi-frame ranges)
output xfer_want, // "want" data transfer
output xfer_need, // "need" - really need a transfer (only 1 page/ room for 1 page left in a buffer), want should still be set.
input xfer_grant, // sequencer programming access granted, deassert wait/need
......@@ -120,7 +124,7 @@ module mcntrl_linear_rw #(
reg [PAR_MOD_LATENCY-1:0] par_mod_r;
reg [PAR_MOD_LATENCY-1:0] recalc_r; // 1-hot CE for re-calculating registers
wire calc_valid; // calculated registers have valid values
wire chn_en; // enable requests by channle (continue ones in progress)
wire chn_en; // enable requests by channle (continue ones in progress), enable frame_start inputs
wire chn_rst; // resets command, including fifo;
reg chn_rst_d; // delayed by 1 cycle do detect turning off
// reg xfer_reset_page_r;
......@@ -132,6 +136,14 @@ module mcntrl_linear_rw #(
// wire cmd_wrmem; //=MCNTRL_SCANLINE_WRITE_MODE; // 0: read from memory, 1:write to memory
wire [1:0] cmd_extra_pages; // external module needs more than 1 page
wire repeat_frames; // mode bit
wire single_frame_w; // pulse
wire rst_frame_num_w;
reg single_frame_r; // pulse
reg [1:0] rst_frame_num_r; // reset frame number/next start adderss
reg frame_en; // enable next frame
reg busy_r;
reg want_r;
reg need_r;
......@@ -152,21 +164,27 @@ module mcntrl_linear_rw #(
wire set_mode_w;
wire set_status_w;
wire set_start_addr_w;
wire set_frame_size_w;
wire set_last_frame_w;
wire set_frame_width_w;
wire set_window_wh_w;
wire set_window_x0y0_w;
wire set_window_start_w;
wire lsw13_zero=!(|cmd_data[FRAME_WIDTH_BITS-1:0]); // LSW 13 (FRAME_WIDTH_BITS) low bits are all 0 - set carry bit
// wire msw13_zero=!(|cmd_data[FRAME_WIDTH_BITS+15:16]); // MSW 13 (FRAME_WIDTH_BITS) low bits are all 0 - set carry bit
wire msw_zero= !(|cmd_data[31:16]); // MSW all bits are 0 - set carry bit
// reg [4:0] mode_reg;//mode register: {extra_pages[1:0],write,enable,!reset}
reg [4:0] mode_reg;//mode register: {extra_pages[1:0],write,enable,!reset}
reg [10:0] mode_reg;//mode register: {repet,single,rst_frame,na[2:0],extra_pages[1:0],write_mode,enable,!reset}
reg [NUM_RC_BURST_BITS-1:0] start_range_addr; // (programmed) First frame in range start (in {row,col8} in burst8, bank ==0
reg [NUM_RC_BURST_BITS-1:0] frame_size; // (programmed) First frame in range start (in {row,col8} in burst8, bank ==0
reg [LAST_FRAME_BITS-1:0] last_frame_number;
reg [NUM_RC_BURST_BITS-1:0] start_addr; // (programmed) Frame start (in {row,col8} in burst8, bank ==0
// reg [FRAME_WIDTH_BITS:0] frame_width; // (programmed) 0- max
reg [NUM_RC_BURST_BITS-1:0] next_frame_start_addr;
reg [LAST_FRAME_BITS-1:0] frame_number_cntr;
reg is_last_frame;
reg [2:0] frame_start_r;
//FIXME!!!!!!!!
reg [FRAME_WIDTH_BITS:0] frame_full_width; // (programmed) increment combined row/col when moving to the next line
// frame_width rounded up to max transfer (half page) if frame_width> max transfer/2,
// otherwise (smaller widths) round up to the nearest power of 2
......@@ -177,25 +195,70 @@ module mcntrl_linear_rw #(
reg [FRAME_WIDTH_BITS-1:0] start_x; // (programmed) normally 0, copied to curr_x on frame_start
reg [FRAME_HEIGHT_BITS-1:0] start_y; // (programmed) normally 0, copied to curr_y on frame_start
reg xfer_done_d; // xfer_done delayed by 1 cycle;
// reg no_more_needed; // frame finished, no more requests is needed
assign frame_number = frame_number_cntr;
assign set_mode_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_MODE);
assign set_status_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_STATUS_CNTRL);
assign set_start_addr_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_STARTADDR);
assign set_frame_size_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_FRAME_SIZE);
assign set_last_frame_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_FRAME_LAST);
assign set_frame_width_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_FRAME_FULL_WIDTH);
assign set_window_wh_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_WINDOW_WH);
assign set_window_x0y0_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_WINDOW_X0Y0);
assign set_window_start_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_WINDOW_STARTXY);
assign single_frame_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_MODE) && cmd_data[9];
assign rst_frame_num_w = cmd_we && (cmd_a== MCNTRL_SCANLINE_MODE) && cmd_data[8];
// Set parameter registers
always @(posedge rst or posedge mclk) begin
if (rst) mode_reg <= 0;
else if (set_mode_w) mode_reg <= cmd_data[4:0]; // [4:0];
else if (set_mode_w) mode_reg <= cmd_data[10:0]; // 4:0]; // [4:0];
if (rst) single_frame_r <= 0;
else single_frame_r <= single_frame_w;
if (rst) start_addr <= 0;
else if (set_start_addr_w) start_addr <= cmd_data[NUM_RC_BURST_BITS-1:0];
if (rst) rst_frame_num_r <= 0;
else rst_frame_num_r <= {rst_frame_num_r[0],
rst_frame_num_w |
set_start_addr_w |
set_last_frame_w |
set_frame_size_w};
if (rst) start_range_addr <= 0;
else if (set_start_addr_w) start_range_addr <= cmd_data[NUM_RC_BURST_BITS-1:0];
if (rst) frame_size <= 0;
else if (set_start_addr_w) frame_size <= 1; // default number of frames - just one
else if (set_frame_size_w) frame_size <= cmd_data[NUM_RC_BURST_BITS-1:0];
if (rst) last_frame_number <= 0;
else if (set_last_frame_w) last_frame_number <= cmd_data[LAST_FRAME_BITS-1:0];
if (rst) frame_full_width <= 0;
else if (set_frame_width_w) frame_full_width <= {lsw13_zero,cmd_data[FRAME_WIDTH_BITS-1:0]};
if (rst) is_last_frame <= 0;
else is_last_frame <= frame_number_cntr == last_frame_number;
if (rst) frame_start_r <= 0;
else frame_start_r <= {frame_start_r[1:0], frame_start & frame_en};
if (rst) frame_en <= 0;
else if (single_frame_r || repeat_frames) frame_en <= 1;
else if (frame_start) frame_en <= 0;
if (rst) frame_number_cntr <= 0;
else if (rst_frame_num_r[0]) frame_number_cntr <= 0;
else if (frame_start_r[2]) frame_number_cntr <= is_last_frame?{LAST_FRAME_BITS{1'b0}}:(frame_number_cntr+1);
if (rst) next_frame_start_addr <= start_range_addr; // just to use rst
else if (rst_frame_num_r[1]) next_frame_start_addr <= start_range_addr;
else if (frame_start_r[2]) next_frame_start_addr <= is_last_frame? start_range_addr : (start_addr+frame_size);
if (rst) start_addr <= start_range_addr; // just to use rst
else if (frame_start_r[0]) start_addr <= next_frame_start_addr;
if (rst) begin
window_width <= 0;
window_height <= 0;
......@@ -221,14 +284,10 @@ module mcntrl_linear_rw #(
end
end
assign mul_rslt_w= frame_y8_r * frame_full_width_r; // 5 MSBs will be discarded
// assign xfer_num128= xfer_num128_m1_r[NUM_XFER_BITS-1:0];
assign xfer_num128= xfer_num128_r[NUM_XFER_BITS-1:0];
// assign xfer_start= xfer_start_r[0];
assign xfer_start_rd= xfer_start_rd_r;
assign xfer_start_wr= xfer_start_wr_r;
assign calc_valid= par_mod_r[PAR_MOD_LATENCY-1]; // MSB, longest 0
// assign xfer_page= xfer_page_r;
// assign xfer_reset_page = xfer_reset_page_r;
assign xfer_page_rst_wr= xfer_page_rst_r;
assign xfer_page_rst_rd= xfer_page_rst_neg;
......@@ -237,9 +296,8 @@ module mcntrl_linear_rw #(
assign frame_done= frame_done_r;
assign frame_finished= frame_finished_r;
assign pre_want= chn_en && busy_r && !want_r && !xfer_start_r[0] && calc_valid && !last_block && !suspend && !frame_start;
// assign pre_want= chn_en && busy_r && !want_r && !xfer_start_r[0] && calc_valid && !no_more_needed && !suspend;
//
assign pre_want= chn_en && busy_r && !want_r && !xfer_start_r[0] && calc_valid && !last_block && !suspend && !frame_start_r[0];
assign last_in_row_w=(row_left=={{(FRAME_WIDTH_BITS-NUM_XFER_BITS){1'b0}},xfer_num128_r});
assign last_row_w= next_y==window_height;
assign xfer_want= want_r;
......@@ -252,19 +310,19 @@ module mcntrl_linear_rw #(
assign chn_rst = ~mode_reg[0]; // resets command, including fifo;
assign cmd_wrmem = mode_reg[2];// 0: read from memory, 1:write to memory
assign cmd_extra_pages = mode_reg[4:3]; // external module needs more than 1 page
assign repeat_frames= mode_reg[10];
assign status_data= {frame_finished_r, busy_r}; // TODO: Add second bit?
assign pgm_param_w= cmd_we;
localparam [COLADDR_NUMBER-3-NUM_XFER_BITS-1:0] EXTRA_BITS=0;
assign remainder_in_xfer = {EXTRA_BITS, lim_by_xfer}-mem_page_left;
integer i;
// localparam EXTRA_BITS={ADDRESS_NUMBER-3-COLADDR_NUMBER-3{1'b0}};
// localparam EXTRA_BITS={COLADDR_NUMBER-3-NUM_XFER_BITS{1'b0}};
wire xfer_limited_by_mem_page;
reg xfer_limited_by_mem_page_r;
assign xfer_limited_by_mem_page= mem_page_left < {EXTRA_BITS,lim_by_xfer};
/// Recalcualting jusrt after starting request - preparing for the next one. Also happens after parameter change.
/// Recalcualting just after starting request - preparing for the next one. Also happens after parameter change.
/// Should dpepend only on the parameters updated separately (curr_x, curr_y)
always @(posedge mclk) begin // TODO: Match latencies (is it needed?) Reduce consumption by CE?
if (recalc_r[0]) begin // cycle 1
......@@ -320,41 +378,43 @@ wire start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;
// now have row start address, bank and row_left ;
// calculate number to read (min of row_left, maximal xfer and what is left in the DDR3 page
always @(posedge rst or posedge mclk) begin
if (rst) par_mod_r<=0;
else if (pgm_param_w || xfer_start_r[0] || chn_rst || frame_start) par_mod_r<=0;
else par_mod_r <= {par_mod_r[PAR_MOD_LATENCY-2:0], 1'b1};
if (rst) par_mod_r<=0;
else if (pgm_param_w ||
xfer_start_r[0] ||
chn_rst ||
frame_start_r[0]) par_mod_r<=0;
else par_mod_r <= {par_mod_r[PAR_MOD_LATENCY-2:0], 1'b1};
if (rst) chn_rst_d <= 0;
else chn_rst_d <= chn_rst;
if (rst) recalc_r<=0;
else if (chn_rst) recalc_r<=0;
// else recalc_r <= {recalc_r[PAR_MOD_LATENCY-2:0], (xfer_grant & ~chn_rst) | pgm_param_w | (chn_rst_d & ~chn_rst)};
else recalc_r <= {recalc_r[PAR_MOD_LATENCY-2:0],
((xfer_start_r[0] | frame_start) & ~chn_rst) | pgm_param_w | (chn_rst_d & ~chn_rst)};
((xfer_start_r[0] | frame_start_r[0]) & ~chn_rst) | pgm_param_w | (chn_rst_d & ~chn_rst)};
if (rst) busy_r <= 0;
else if (chn_rst) busy_r <= 0;
else if (frame_start) busy_r <= 1;
else if (frame_done_r) busy_r <= 0;
if (rst) busy_r <= 0;
else if (chn_rst) busy_r <= 0;
else if (frame_start_r[0]) busy_r <= 1;
else if (frame_done_r) busy_r <= 0;
if (rst) xfer_done_d <= 0;
else xfer_done_d <= xfer_done;
if (rst) continued_xfer <= 1'b0;
else if (chn_rst) continued_xfer <= 1'b0;
else if (frame_start) continued_xfer <= 1'b0;
else if (xfer_start_r[0]) continued_xfer <= xfer_limited_by_mem_page_r; // only set after actual start if it was partial, not after parameter change
if (rst) continued_xfer <= 1'b0;
else if (chn_rst) continued_xfer <= 1'b0;
else if (frame_start_r[0]) continued_xfer <= 1'b0;
else if (xfer_start_r[0]) continued_xfer <= xfer_limited_by_mem_page_r; // only set after actual start if it was partial, not after parameter change
// single cycle (sent out)
if (rst) frame_done_r <= 0;
else frame_done_r <= busy_r && last_block && xfer_done_d && (pending_xfers==0);
// turns and stays on (used in status)
if (rst) frame_finished_r <= 0;
else if (chn_rst || frame_start) frame_finished_r <= 0;
else if (frame_done_r) frame_finished_r <= 1;
if (rst) frame_finished_r <= 0;
else if (chn_rst || frame_start_r[0]) frame_finished_r <= 0;
else if (frame_done_r) frame_finished_r <= 1;
if (rst) xfer_start_r <= 0;
else xfer_start_r <= {xfer_start_r[1:0],xfer_grant && !chn_rst};
......@@ -374,50 +434,44 @@ wire start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;
else if (pre_want && (page_cntr>{1'b0,cmd_extra_pages})) want_r <= 1;
if (rst) page_cntr <= 0;
else if (frame_start) page_cntr <= cmd_wrmem?0:4; // What about last pages (like if only 1 page is needed)? Early frame end?
// else if ( xfer_start_r[0] && !next_page) page_cntr <= page_cntr - 1;
// else if (!xfer_start_r[0] && next_page) page_cntr <= page_cntr + 1;
else if (frame_start_r[0]) page_cntr <= cmd_wrmem?0:4; // What about last pages (like if only 1 page is needed)? Early frame end?
else if ( start_not_partial && !next_page) page_cntr <= page_cntr - 1;
else if (!start_not_partial && next_page) page_cntr <= page_cntr + 1;
// xfer_reset_page_r <= chn_rst; // || frame_start ; // TODO: Check if it is better to reset page on frame start?
if (rst) xfer_page_rst_r <= 1;
else xfer_page_rst_r <= chn_rst || (MCNTRL_SCANLINE_FRAME_PAGE_RESET ? (frame_start & cmd_wrmem):1'b0);
else xfer_page_rst_r <= chn_rst || (MCNTRL_SCANLINE_FRAME_PAGE_RESET ? (frame_start_r[0] & cmd_wrmem):1'b0);
if (rst) xfer_page_rst_pos <= 1;
else xfer_page_rst_pos <= chn_rst || (MCNTRL_SCANLINE_FRAME_PAGE_RESET ? (frame_start & ~cmd_wrmem):1'b0);
else xfer_page_rst_pos <= chn_rst || (MCNTRL_SCANLINE_FRAME_PAGE_RESET ? (frame_start_r[0] & ~cmd_wrmem):1'b0);
// increment x,y (two cycles)
if (rst) curr_x <= 0;
else if (chn_rst || frame_start) curr_x <= start_x;
else if (chn_rst || frame_start_r[0]) curr_x <= start_x;
else if (xfer_start_r[0]) curr_x <= last_in_row?0: curr_x + xfer_num128_r;
if (rst) curr_y <= 0;
else if (chn_rst || frame_start) curr_y <= start_y;
else if (chn_rst || frame_start_r[0]) curr_y <= start_y;
else if (xfer_start_r[0] && last_in_row) curr_y <= next_y[FRAME_HEIGHT_BITS-1:0];
if (rst) last_block <= 0;
else if (chn_rst || !busy_r) last_block <= 0;
// else if (last_row_w && last_in_row_w) last_block <= 1;
else if (xfer_start_r[0]) last_block <= last_row_w && last_in_row_w;
if (rst) pending_xfers <= 0;
else if (chn_rst || !busy_r) pending_xfers <= 0;
else if ( xfer_start_r[0] && !xfer_done) pending_xfers <= pending_xfers + 1;
else if (!xfer_start_r[0] && xfer_done) pending_xfers <= pending_xfers - 1;
// else if ( start_not_partial && !xfer_done) pending_xfers <= pending_xfers + 1;
// else if (!start_not_partial && xfer_done) pending_xfers <= pending_xfers - 1;
//line_unfinished_r cmd_wrmem
if (rst) line_unfinished_r[0] <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
else if (chn_rst || frame_start) line_unfinished_r[0] <= window_y0+start_y;
else if (xfer_start_r[2]) line_unfinished_r[0] <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
if (rst) line_unfinished_r[0] <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
else if (chn_rst || frame_start_r[0]) line_unfinished_r[0] <= window_y0+start_y;
else if (xfer_start_r[2]) line_unfinished_r[0] <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
if (rst) line_unfinished_r[1] <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
else if (chn_rst || frame_start) line_unfinished_r[1] <= window_y0+start_y;
if (rst) line_unfinished_r[1] <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
else if (chn_rst || frame_start_r[0]) line_unfinished_r[1] <= window_y0+start_y;
// in read mode advance line number ASAP
else if (xfer_start_r[2] && !cmd_wrmem) line_unfinished_r[1] <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
// in write mode advance line number only when it is guaranteed it will be the first to acyually access memory
......
......@@ -28,19 +28,22 @@ module mcntrl_tiled_rw#(
parameter FRAME_HEIGHT_BITS= 16, // Maximal frame height
parameter MAX_TILE_WIDTH= 6, // number of bits to specify maximal tile (width-1) (6 -> 64)
parameter MAX_TILE_HEIGHT= 6, // number of bits to specify maximal tile (height-1) (6 -> 64)
parameter LAST_FRAME_BITS= 16, // number of bits in frame counter (before rolls over)
parameter MCNTRL_TILED_ADDR= 'h120,
parameter MCNTRL_TILED_MASK= 'h3f0, // both channels 0 and 1
parameter MCNTRL_TILED_MODE= 'h0, // set mode register: {byte32,keep_open,extra_pages[1:0],write_mode,enable,!reset}
parameter MCNTRL_TILED_STATUS_CNTRL= 'h1, // control status reporting
parameter MCNTRL_TILED_STARTADDR= 'h2, // 22-bit frame start address (3 CA LSBs==0. BA==0)
parameter MCNTRL_TILED_FRAME_FULL_WIDTH='h3, // Padded line length (8-row increment), in 8-bursts (16 bytes)
parameter MCNTRL_TILED_WINDOW_WH= 'h4, // low word - 13-bit window width (0->'h4000), high word - 16-bit frame height (0->'h10000)
parameter MCNTRL_TILED_WINDOW_X0Y0= 'h5, // low word - 13-bit window left, high word - 16-bit window top
parameter MCNTRL_TILED_WINDOW_STARTXY= 'h6, // low word - 13-bit start X (relative to window), high word - 16-bit start y
parameter MCNTRL_TILED_FRAME_SIZE= 'h3, // 22-bit frame start address increment (3 CA LSBs==0. BA==0)
parameter MCNTRL_TILED_FRAME_LAST= 'h4, // 16-bit last frame number in the buffer
parameter MCNTRL_TILED_FRAME_FULL_WIDTH='h5, // Padded line length (8-row increment), in 8-bursts (16 bytes)
parameter MCNTRL_TILED_WINDOW_WH= 'h6, // low word - 13-bit window width (0->'h4000), high word - 16-bit frame height (0->'h10000)
parameter MCNTRL_TILED_WINDOW_X0Y0= 'h7, // low word - 13-bit window left, high word - 16-bit window top
parameter MCNTRL_TILED_WINDOW_STARTXY= 'h8, // low word - 13-bit start X (relative to window), high word - 16-bit start y
// Start XY can be used when read command to start from the middle
// TODO: Add number of blocks to R/W? (blocks can be different) - total length?
// Read back current address (for debugging)?
parameter MCNTRL_TILED_TILE_WHS= 'h7, // low byte - 6-bit tile width in 8-bursts, second byte - tile height (0 - > 64),
parameter MCNTRL_TILED_TILE_WHS= 'h9, // low byte - 6-bit tile width in 8-bursts, second byte - tile height (0 - > 64),
// 3-rd byte - vertical step (to control tile vertical overlap)
parameter MCNTRL_TILED_STATUS_REG_ADDR= 'h5,
parameter MCNTRL_TILED_PENDING_CNTR_BITS=2, // Number of bits to count pending trasfers, currently 2 is enough, but may increase
......@@ -68,6 +71,7 @@ module mcntrl_tiled_rw#(
// optional I/O for channel synchronization
output [FRAME_HEIGHT_BITS-1:0] line_unfinished, // number of the current (ufinished ) line, REALATIVE TO FRAME, NOT WINDOW?.
input suspend, // suspend transfers (from external line number comparator)
output [LAST_FRAME_BITS-1:0] frame_number, // current frame number (for multi-frame ranges)
output xfer_want, // "want" data transfer
output xfer_need, // "need" - really need a transfer (only 1 page/ room for 1 page left in a buffer), want should still be set.
input xfer_grant, // sequencer programming access granted, deassert wait/need
......@@ -128,7 +132,7 @@ module mcntrl_tiled_rw#(
reg [PAR_MOD_LATENCY-1:0] par_mod_r;
reg [PAR_MOD_LATENCY-1:0] recalc_r; // 1-hot CE for re-calculating registers
wire calc_valid; // calculated registers have valid values
wire chn_en; // enable requests by channle (continue ones in progress)
wire chn_en; // enable requests by channle (continue ones in progress), enable frame_start inputs
wire chn_rst; // resets command, including fifo;
reg chn_rst_d; // delayed by 1 cycle do detect turning off
reg xfer_page_rst_r=1;
......@@ -140,6 +144,14 @@ module mcntrl_tiled_rw#(
wire cmd_wrmem; //= MCNTRL_TILED_WRITE_MODE; // 0: read from memory, 1:write to memory (change to parameter?)
wire [1:0] cmd_extra_pages; // external module needs more than 1 page
wire byte32; // use 32-byte wide colums in each tile (0 - use 16-byte ones)
wire repeat_frames; // mode bit
wire single_frame_w; // pulse
wire rst_frame_num_w;
reg single_frame_r; // pulse
reg [1:0] rst_frame_num_r; // reset frame number/next start adderss
reg frame_en; // enable next frame
reg busy_r;
reg want_r;
reg need_r;
......@@ -161,6 +173,9 @@ module mcntrl_tiled_rw#(
wire set_mode_w;
wire set_status_w;
wire set_start_addr_w;
wire set_frame_size_w;
wire set_last_frame_w;
wire set_frame_width_w;
wire set_window_wh_w;
wire set_window_x0y0_w;
......@@ -174,8 +189,18 @@ module mcntrl_tiled_rw#(
wire tile_vstep_zero= !(|cmd_data[16+:MAX_TILE_HEIGHT]);
// reg [5:0] mode_reg;//mode register: {write_mode,keep_open,extra_pages[1:0],enable,!reset}
reg [6:0] mode_reg;//mode register: {byte32,keep_open,extra_pages[1:0],write_mode,enable,!reset}
reg [NUM_RC_BURST_BITS-1:0] start_addr; // (programmed) Frame start (in {row,col8} in burst8, bank ==0
// reg [6:0] mode_reg;//mode register: {byte32,keep_open,extra_pages[1:0],write_mode,enable,!reset}
reg [10:0] mode_reg;//mode register: {repet,single,rst_frame,na,byte32,keep_open,extra_pages[1:0],write_mode,enable,!reset}
reg [NUM_RC_BURST_BITS-1:0] start_range_addr; // (programmed) First frame in range start (in {row,col8} in burst8, bank ==0
reg [NUM_RC_BURST_BITS-1:0] frame_size; // (programmed) First frame in range start (in {row,col8} in burst8, bank ==0
reg [LAST_FRAME_BITS-1:0] last_frame_number;
reg [NUM_RC_BURST_BITS-1:0] start_addr; // (programmed) Frame start (in {row,col8} in burst8, bank ==0
reg [NUM_RC_BURST_BITS-1:0] next_frame_start_addr;
reg [LAST_FRAME_BITS-1:0] frame_number_cntr;
reg is_last_frame;
reg [2:0] frame_start_r;
// reg rst_frame_num_d;
reg [MAX_TILE_WIDTH:0] tile_cols; // full number of columns in a tile
// reg [MAX_TILE_HEIGHT:0] tile_rows; // full number of rows in a tile
reg [MAX_TILE_HEIGHT-1:0] tile_rows; // full number of rows in a tile
......@@ -196,27 +221,74 @@ module mcntrl_tiled_rw#(
reg [FRAME_HEIGHT_BITS-1:0] start_y; // (programmed) normally 0, copied to curr_y on frame_start
reg xfer_page_done_d; // next cycle after xfer_page_done
assign frame_number = frame_number_cntr;
assign set_mode_w = cmd_we && (cmd_a== MCNTRL_TILED_MODE);
assign set_status_w = cmd_we && (cmd_a== MCNTRL_TILED_STATUS_CNTRL);
assign set_start_addr_w = cmd_we && (cmd_a== MCNTRL_TILED_STARTADDR);
assign set_frame_size_w = cmd_we && (cmd_a== MCNTRL_TILED_FRAME_SIZE);
assign set_last_frame_w = cmd_we && (cmd_a== MCNTRL_TILED_FRAME_LAST);
assign set_frame_width_w = cmd_we && (cmd_a== MCNTRL_TILED_FRAME_FULL_WIDTH);
assign set_window_wh_w = cmd_we && (cmd_a== MCNTRL_TILED_WINDOW_WH);
assign set_window_x0y0_w = cmd_we && (cmd_a== MCNTRL_TILED_WINDOW_X0Y0);
assign set_window_start_w = cmd_we && (cmd_a== MCNTRL_TILED_WINDOW_STARTXY);
assign set_tile_whs_w = cmd_we && (cmd_a== MCNTRL_TILED_TILE_WHS);
assign single_frame_w = cmd_we && (cmd_a== MCNTRL_TILED_MODE) && cmd_data[9];
assign rst_frame_num_w = cmd_we && (cmd_a== MCNTRL_TILED_MODE) && cmd_data[8];
//
// Set parameter registers
always @(posedge rst or posedge mclk) begin
if (rst) mode_reg <= 0;
else if (set_mode_w) mode_reg <= cmd_data[6:0]; // [5:0];
else if (set_mode_w) mode_reg <= cmd_data[10:0]; // [5:0];
if (rst) single_frame_r <= 0;
else single_frame_r <= single_frame_w;
if (rst) start_addr <= 0;
else if (set_start_addr_w) start_addr <= cmd_data[NUM_RC_BURST_BITS-1:0];
if (rst) rst_frame_num_r <= 0;
else rst_frame_num_r <= {rst_frame_num_r[0],
rst_frame_num_w |
set_start_addr_w |
set_last_frame_w |
set_frame_size_w};
if (rst) start_range_addr <= 0;
else if (set_start_addr_w) start_range_addr <= cmd_data[NUM_RC_BURST_BITS-1:0];
if (rst) frame_size <= 0;
else if (set_start_addr_w) frame_size <= 1; // default number of frames - just one
else if (set_frame_size_w) frame_size <= cmd_data[NUM_RC_BURST_BITS-1:0];
if (rst) last_frame_number <= 0;
else if (set_last_frame_w) last_frame_number <= cmd_data[LAST_FRAME_BITS-1:0];
if (rst) frame_full_width <= 0;
else if (set_frame_width_w) frame_full_width <= {lsw13_zero,cmd_data[FRAME_WIDTH_BITS-1:0]};
if (rst) is_last_frame <= 0;
else is_last_frame <= frame_number_cntr == last_frame_number;
if (rst) frame_start_r <= 0;
else frame_start_r <= {frame_start_r[1:0], frame_start & frame_en};
if (rst) frame_en <= 0;
else if (single_frame_r || repeat_frames) frame_en <= 1;
else if (frame_start) frame_en <= 0;
if (rst) frame_number_cntr <= 0;
else if (rst_frame_num_r[0]) frame_number_cntr <= 0;
else if (frame_start_r[2]) frame_number_cntr <= is_last_frame?{LAST_FRAME_BITS{1'b0}}:(frame_number_cntr+1);
if (rst) next_frame_start_addr <= start_range_addr; // just to use rst
else if (rst_frame_num_r[1]) next_frame_start_addr <= start_range_addr;
else if (frame_start_r[2]) next_frame_start_addr <= is_last_frame? start_range_addr : (start_addr+frame_size);
if (rst) start_addr <= start_range_addr; // just to use rst
else if (frame_start_r[0]) start_addr <= next_frame_start_addr;
if (rst) begin
window_width <= 0;
window_height <= 0;
......@@ -238,7 +310,7 @@ module mcntrl_tiled_rw#(
if (rst) begin
window_x0 <= 0;
window_y0 <= 0;
window_y0 <= 0;
end else if (set_window_x0y0_w) begin
window_x0 <= cmd_data[FRAME_WIDTH_BITS-1:0];
window_y0 <=cmd_data[FRAME_HEIGHT_BITS+15:16];
......@@ -262,7 +334,7 @@ module mcntrl_tiled_rw#(
assign calc_valid= par_mod_r[PAR_MOD_LATENCY-1]; // MSB, longest 0
assign frame_done= frame_done_r;
assign frame_finished= frame_finished_r;
assign pre_want= chn_en && busy_r && !want_r && !xfer_start_r[0] && calc_valid && !last_block && !suspend && !frame_start;
assign pre_want= chn_en && busy_r && !want_r && !xfer_start_r[0] && calc_valid && !last_block && !suspend && !frame_start_r[0];
assign last_in_row_w=(row_left=={{(FRAME_WIDTH_BITS-MAX_TILE_WIDTH){1'b0}},num_cols_r}); // what if it crosses page? OK, num_cols_r & row_left know that
// assign last_row_w= next_y>=window_height; // (next_y==window_height) is faster, but will not forgive software errors
// tiles must completely fit window
......@@ -283,6 +355,10 @@ module mcntrl_tiled_rw#(
assign cmd_extra_pages = mode_reg[4:3]; // external module needs more than 1 page
assign keep_open= mode_reg[5]; // keep banks open (will be used only if number of rows <= 8
assign byte32= mode_reg[6]; // use 32-byte wide columns in each tile (false - 16-byte)
assign repeat_frames= mode_reg[10];
// reg [10:0] mode_reg;//mode register: {repet,single,rst_frame,na,byte32,keep_open,extra_pages[1:0],write_mode,enable,!reset}
assign status_data= {frame_finished_r, busy_r};
assign pgm_param_w= cmd_we;
assign rowcol_inc= frame_full_width;
......@@ -355,9 +431,12 @@ module mcntrl_tiled_rw#(
// calculate number to read (min of row_left, maximal xfer and what is left in the DDR3 page
wire start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;
always @(posedge rst or posedge mclk) begin
if (rst) par_mod_r<=0;
else if (pgm_param_w || xfer_start_r[0] || chn_rst || frame_start) par_mod_r<=0;
else par_mod_r <= {par_mod_r[PAR_MOD_LATENCY-2:0], 1'b1};
if (rst) par_mod_r<=0;
else if (pgm_param_w ||
xfer_start_r[0] ||
chn_rst ||
frame_start_r[0]) par_mod_r<=0;
else par_mod_r <= {par_mod_r[PAR_MOD_LATENCY-2:0], 1'b1};
if (rst) chn_rst_d <= 0;
else chn_rst_d <= chn_rst;
......@@ -366,12 +445,12 @@ wire start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;
else if (chn_rst) recalc_r<=0;
// else recalc_r <= {recalc_r[PAR_MOD_LATENCY-2:0], (xfer_grant & ~chn_rst) | pgm_param_w | (chn_rst_d & ~chn_rst)};
else recalc_r <= {recalc_r[PAR_MOD_LATENCY-2:0],
((xfer_start_r[0] | frame_start) & ~chn_rst) | pgm_param_w | (chn_rst_d & ~chn_rst)};
((xfer_start_r[0] | frame_start_r[0]) & ~chn_rst) | pgm_param_w | (chn_rst_d & ~chn_rst)};
if (rst) busy_r <= 0;
else if (chn_rst) busy_r <= 0;
else if (frame_start) busy_r <= 1;
else if (frame_done_r) busy_r <= 0;
if (rst) busy_r <= 0;
else if (chn_rst) busy_r <= 0;
else if (frame_start_r[0]) busy_r <= 1;
else if (frame_done_r) busy_r <= 0;
if (rst) xfer_page_done_d <= 0;
else xfer_page_done_d <= xfer_page_done;
......@@ -393,7 +472,7 @@ wire start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;
if (rst) continued_tile <= 1'b0;
else if (chn_rst) continued_tile <= 1'b0;
else if (frame_start) continued_tile <= 1'b0;
else if (frame_start_r[0]) continued_tile <= 1'b0;
else if (xfer_start_r[0]) continued_tile <= xfer_limited_by_mem_page_r; // only set after actual start if it was partial, not after parameter change
if (rst) need_r <= 0;
......@@ -405,25 +484,25 @@ wire start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;
else if (pre_want && (page_cntr>{1'b0,cmd_extra_pages})) want_r <= 1;
if (rst) page_cntr <= 0;
else if (frame_start) page_cntr <= cmd_wrmem?0:4;
else if (frame_start_r[0]) page_cntr <= cmd_wrmem?0:4;
// else if ( xfer_start_r[0] && !next_page) page_cntr <= page_cntr + 1;
// else if (!xfer_start_r[0] && next_page) page_cntr <= page_cntr - 1;
else if ( start_not_partial && !next_page) page_cntr <= page_cntr - 1;
else if (!start_not_partial && next_page) page_cntr <= page_cntr + 1;
if (rst) xfer_page_rst_r <= 1;
else xfer_page_rst_r <= chn_rst || (MCNTRL_TILED_FRAME_PAGE_RESET ? (frame_start & cmd_wrmem):1'b0);
else xfer_page_rst_r <= chn_rst || (MCNTRL_TILED_FRAME_PAGE_RESET ? (frame_start_r[0] & cmd_wrmem):1'b0);
if (rst) xfer_page_rst_pos <= 1;
else xfer_page_rst_pos <= chn_rst || (MCNTRL_TILED_FRAME_PAGE_RESET ? (frame_start & ~cmd_wrmem):1'b0);
else xfer_page_rst_pos <= chn_rst || (MCNTRL_TILED_FRAME_PAGE_RESET ? (frame_start_r[0] & ~cmd_wrmem):1'b0);
// increment x,y (two cycles)
if (rst) curr_x <= 0;
else if (chn_rst || frame_start) curr_x <= start_x;
else if (chn_rst || frame_start_r[0]) curr_x <= start_x;
else if (xfer_start_r[0]) curr_x <= last_in_row?0: curr_x + num_cols_r;
if (rst) curr_y <= 0;
else if (chn_rst || frame_start) curr_y <= start_y;
else if (chn_rst || frame_start_r[0]) curr_y <= start_y;
else if (xfer_start_r[0] && last_in_row) curr_y <= next_y[FRAME_HEIGHT_BITS-1:0];
if (rst) last_block <= 0;
......@@ -442,17 +521,17 @@ wire start_not_partial= xfer_start_r[0] && !xfer_limited_by_mem_page_r;
else frame_done_r <= busy_r && last_block && xfer_page_done_d && (pending_xfers==0);
// turns and stays on (used in status)
if (rst) frame_finished_r <= 0;
else if (chn_rst || frame_start) frame_finished_r <= 0;
else if (frame_done_r) frame_finished_r <= 1;
if (rst) frame_finished_r <= 0;
else if (chn_rst || frame_start_r[0]) frame_finished_r <= 0;
else if (frame_done_r) frame_finished_r <= 1;
//line_unfinished_r cmd_wrmem
if (rst) line_unfinished_r0 <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
else if (chn_rst || frame_start) line_unfinished_r0 <= window_y0+start_y;
else if (xfer_start_r[2]) line_unfinished_r0 <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
if (rst) line_unfinished_r0 <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
else if (chn_rst || frame_start_r[0]) line_unfinished_r0 <= window_y0+start_y;
else if (xfer_start_r[2]) line_unfinished_r0 <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
if (rst) line_unfinished_r1 <= 0; //{FRAME_HEIGHT_BITS{1'b0}};
else if (chn_rst || frame_start) line_unfinished_r1 <= window_y0+start_y;
else if (chn_rst || frame_start_r[0]) line_unfinished_r1 <= window_y0+start_y;
// in read mode advance line number ASAP
else if (xfer_start_r[2] && !cmd_wrmem) line_unfinished_r1 <= window_y0+next_y[FRAME_HEIGHT_BITS-1:0]; // latency 2 from xfer_start
// in write mode advance line number only when it is guaranteed it will be the first to actually access memory
......
/*******************************************************************************
* Module: sens_parallel12
* Date:2015-05-10
* Author: andrey
* Description: Sensor interface with 12-bit for parallel bus
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* sens_parallel12.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_parallel12.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/> .
*******************************************************************************/
`timescale 1ns/1ps
module sens_parallel12 (
input rst,
input pclk, // global clock input, pixel rate (96MHz for MT9P006)
input pclk2x, // maybe not needed here
// sensor pads excluding i2c
input vact,
input hact, //output in fillfactory mode
inout bpf, // output in fillfactory mode
inout [11:0] pxd, //actually only 2 LSBs are inouts
inout mrst,
output arst,
output aro,
// output
output [15:0] ipxd,
output vacts,
// programming interface
input mclk, // global clock, half DDR3 clock, synchronizes all I/O thorough 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)
);
endmodule
/*******************************************************************************
* Module: sensor_channel
* Date:2015-05-10
* Author: andrey
* Description: Top module for a sensor channel
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* sensor_channel.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.
*
* sensor_channel.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/> .
*******************************************************************************/
`timescale 1ns/1ps
module sensor_channel#(
parameter SENSI2C_ABS_ADDR = 'h300,
parameter SENSI2C_REL_ADDR = 'h310,
parameter SENSI2C_ADDR_MASK = 'h3f0, // both for SENSI2C_ABS_ADDR and SENSI2C_REL_ADDR
parameter SENSI2C_CTRL_ADDR = 'h320,
parameter SENSI2C_CTRL_MASK = 'h3fe,
parameter SENSI2C_CTRL = 'h0,
parameter SENSI2C_STATUS = 'h1,
parameter SENSI2C_STATUS_REG = 'h30,
parameter integer DRIVE = 12,
parameter IBUF_LOW_PWR = "TRUE",
parameter IOSTANDARD = "DEFAULT",
`ifdef XIL_TIMING
parameter LOC = " UNPLACED",
`endif
parameter SLEW = "SLOW"
) (
input rst,
input pclk, // global clock input, pixel rate (96MHz for MT9P006)
// I/O pads, pin names match circuit diagram
inout [7:0] sns_dp,
inout [7:0] sns_dn,
inout sns_clkp,
inout sns_scl,
inout sns_sda,
inout sns_ctl,
inout sns_pg,
// programming interface
input mclk, // global clock, half DDR3 clock, synchronizes all I/O thorough 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)
// (much) more will be added later
);
sensor_i2c_io #(
.SENSI2C_ABS_ADDR(SENSI2C_ABS_ADDR),
.SENSI2C_REL_ADDR(SENSI2C_REL_ADDR),
.SENSI2C_ADDR_MASK(SENSI2C_ADDR_MASK),
.SENSI2C_CTRL_ADDR(SENSI2C_CTRL_ADDR),
.SENSI2C_CTRL_MASK(SENSI2C_CTRL_MASK),
.SENSI2C_CTRL(SENSI2C_CTRL),
.SENSI2C_STATUS(SENSI2C_STATUS),
.SENSI2C_STATUS_REG(SENSI2C_STATUS_REG),
.SENSI2C_DRIVE(SENSI2C_DRIVE),
.SENSI2C_IBUF_LOW_PWR(SENSI2C_IBUF_LOW_PWR),
.SENSI2C_IOSTANDARD(SENSI2C_IOSTANDARD),
.SENSI2C_SLEW(SENSI2C_SLEW)
) sensor_i2c_io_i (
.rst(), // input
.mclk(), // input
.cmd_ad(), // input[7:0]
.cmd_stb(), // input
.status_ad(), // output[7:0]
.status_rq(), // output
.status_start(), // input
.frame_sync(), // input
.scl(), // inout
.sda() // inout
);
endmodule
/*******************************************************************************
* Module: sensor_i2c
* Date:2015-05-10
* Author: andrey
* Description: i2c write-only sequencer to control image sensor
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* sensor_i2c.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.
*
* sensor_i2c.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/> .
*******************************************************************************/
`timescale 1ns/1ps
module sensor_i2c#(
parameter SENSI2C_ABS_ADDR = 'h300,
parameter SENSI2C_REL_ADDR = 'h310,
parameter SENSI2C_ADDR_MASK = 'h3f0, // both for SENSI2C_ABS_ADDR and SENSI2C_REL_ADDR
parameter SENSI2C_CTRL_ADDR = 'h320,
parameter SENSI2C_CTRL_MASK = 'h3fe,
parameter SENSI2C_CTRL = 'h0,
parameter SENSI2C_STATUS = 'h1,
parameter SENSI2C_STATUS_REG = 'h30
)(
input rst,
input mclk, // global clock, half DDR3 clock, synchronizes all I/O thorough 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
// status will {frame_num[3:0],busy,sda,scl} - read outside of this module?
// Or still use status here but program it in other bits?
// increase address range over 5 bits?
// borrow 0x1e?
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 frame_sync, // increment/reset frame number
// input frame_0, // reset frame number to zero - can be done by soft reset before first enabled frame
// output busy, // busy (do not use software i2i)
input scl_in, // i2c SCL input
input sda_in, // i2c SDA input
output scl_out, // i2c SCL output
output sda_out, // i2c SDA output
output scl_en, // i2c SCL enable
output sda_en // i2c SDA enable
// output busy,
// output [3:0] frame_num
);
// TODO: Make sure that using more than 64 commands will just send them during next frame, not loose?
// 0x0..0xf write directly to the frame number [3:0] modulo 16, except if you write to the frame
// "just missed" - in that case data will go to the current frame.
// 0x10 - write i2c commands to be sent ASAP
// 0x11 - write i2c commands to be sent after the next frame starts
// ...
// 0x1e - write i2c commands to be sent after the next 14 frames start
// 0x1e - program status? Or
// 0x1f - control register:
// [14] - reset all FIFO (takes 16 clock pulses), also - stops i2c until run command
// [13:12] - 3 - run i2c, 2 - stop i2c (needed before software i2c), 1,0 - no change to run state
// [11] - if 1, use [10:9] to set command bytes to send after slave address (0..3)
// [10:9] - number of bytes to send, valid if [11] is set
// [8] - set duration of quarter i2c cycle in system clock cycles - nominal value 100 (0x64)
// [7:0] - duration of quater i2c cycle (applied if [8] is set)
wire we_abs;
wire we_rel;
wire we_cmd;
wire wen;
wire [31:0] di;
wire [3:0] wa;
// wire busy; // busy (do not use software i2i)
// reg [4:0] wen_d; // [0] - not just fifo, but any PIO writes, [1] and next - filtered for FIFO only
// reg [3:0] wen_d; // [0] - not just fifo, but any PIO writes, [1] and next - filtered for FIFO only
// reg [3:0] wad;
reg [31:0] di_r; // 32 bit command takes 6 cycles, so di_r can hold data for up to this long
// reg [15:0] di_1;
// reg [15:0] di_2;
// reg [15:0] di_3;
reg [3:0] wpage0; // FIFO page where ASAP writes go
reg [3:0] wpage_prev; // unused page, currently being cleared
reg [3:0] page_r; // FIFO page where current i2c commands are taken from
reg [3:0] wpage_wr; // FIFO page where current write goes (reading from write address)
reg [1:0] wpage0_inc; // increment wpage0 (after frame sync or during reset)
reg reset_cmd;
reg dly_cmd;
reg bytes_cmd;
reg run_cmd;
reg reset_on; // reset FIFO in progress
reg [1:0] i2c_bytes;
reg [7:0] i2c_dly;
reg i2c_enrun; // enable i2c
reg we_fifo_wp; // enable writing to fifo write pointer memory
reg req_clr; // request for clearing fifo_wp (delay frame sync if previous is not yet sent out), also used for clearing all
// wire is_ctl= (wad[3:0]==4'hf);
// wire is_abs= (wad[3]==0);
wire pre_wpage0_inc; // ready to increment
wire [3:0] frame_num=wpage0[3:0];
//fifo write pointers (dual port distributed RAM)
reg [5:0] fifo_wr_pointers [0:15]; // dual ported read?
wire [5:0] fifo_wr_pointers_outw; // pointer dual-ported RAM - write port out, valid next after command
wire [5:0] fifo_wr_pointers_outr; // pointer dual-ported RAM - read port out
reg [5:0] fifo_wr_pointers_outw_r;
reg [5:0] fifo_wr_pointers_outr_r;
// command i2c fifo (RAMB16_S9_S18)
reg [9:0] i2c_cmd_wa; // wite address for the current pair of 16-bit data words - changed to a single 32-bit word
// {page[3:0],word[5:0],MSW[0]}
reg i2c_cmd_we; // write enable to blockRAM
reg [1:0] page_r_inc; // increment page_r[2:0]; - signal and delayed version
reg [5:0] rpointer; // FIFO read pointer for current page
reg i2c_start; // initiate i2c register write sequence
reg i2c_run; // i2c sequence is in progress
reg i2c_done; // i2c sequence is over
reg [1:0] bytes_left; // bytes left to send after this one
reg [1:0] byte_number; // byte number to send next (3-2-1-0)
reg [1:0] byte_sending; // byte number currently sending (3-2-1-0)
reg [5:0] i2c_state; // 0x2b..0x28 - sending start, 0x27..0x24 - stop, 0x23..0x4 - data, 0x03..0x00 - ACKN
reg [7:0] dly_cntr; // bit delay down counter
reg scl_hard;
reg sda_hard;
reg sda_en_hard;
// reg wen_i2c_soft; // write software-contrlolles SDA, SCL state
reg scl_en_soft; // software i2c control signals (used when i2c controller is disabled)
reg scl_soft;
reg sda_en_soft;
reg sda_soft;
wire [7:0] i2c_data;
reg [8:0] i2c_sr;
reg i2c_dly_pre_over;
wire i2c_dly_pre2_over;
reg i2c_dly_over;
wire i2c_startseq_last=(i2c_state[5:0]==6'h28);
wire i2c_stopseq_last= (i2c_state[5:0]==6'h24);
wire i2c_dataseq_last= (i2c_state[5:0]==6'h00);
wire i2c_bit_last = (i2c_state[1:0]==2'h0);
wire i2c_is_ackn = (i2c_state[5:2]==4'h0);
wire i2c_is_start = i2c_state[5] && i2c_state[3];
wire i2c_is_stop = i2c_state[5] && i2c_state[2];
wire i2c_is_data = !i2c_state[5] || (!i2c_state[3] && !i2c_state[2]); // including ackn
// reg i2c_startseq_done; // last cycle of start sequence
reg i2c_dataseq_done; // last cycle of each byte sequence
// reg i2c_dataseq_all_done; // last cycle of the last byte sequence
reg [2:0] i2c_byte_start;
reg i2c_sr_shift;
reg i2c_stop_start;
reg sda_0;
reg scl_0;
reg busy;
reg [3:0] busy_cntr;
assign i2c_dly_pre2_over=(dly_cntr[7:0]==8'h2);
wire set_ctrl_w;
wire set_status_w;
reg [1:0] wen_r;
reg [1:0] wen_fifo;
assign set_ctrl_w = we_cmd && (wa == SENSI2C_CTRL );// ==0
assign set_status_w = we_cmd && (wa == SENSI2C_STATUS );// ==0
assign scl_out=i2c_run? scl_hard: scl_soft ;
assign sda_out=i2c_run? sda_hard: sda_soft ;
assign scl_en=i2c_run? 1'b1: scl_en_soft ;
assign sda_en=i2c_run? sda_en_hard: sda_en_soft ;
assign pre_wpage0_inc = (!wen && !(|wen_r) && !wpage0_inc) && (req_clr || reset_on) ;
assign fifo_wr_pointers_outw = fifo_wr_pointers[wpage_wr[3:0]]; // valid next after command
assign fifo_wr_pointers_outr = fifo_wr_pointers[page_r[3:0]];
// wire we_abs;
// wire we_rel;
// wire we_cmd;
// wire [15:0] di;
// wire [3:0] wa;
assign wen=set_ctrl_w || we_rel || we_abs; //remove set_ctrl_w?
cmd_deser #(
.ADDR (SENSI2C_ABS_ADDR),
.ADDR_MASK (SENSI2C_ADDR_MASK),
.NUM_CYCLES (6),
.ADDR_WIDTH (4),
.DATA_WIDTH (32),
.ADDR1 (SENSI2C_REL_ADDR),
.ADDR_MASK1 (SENSI2C_ADDR_MASK),
.ADDR2 (SENSI2C_CTRL_ADDR),
.ADDR_MASK2 (SENSI2C_CTRL_MASK)
) cmd_deser_sens_i2c_i (
.rst (rst), // input
.clk (mclk), // input
.ad (cmd_ad), // input[7:0]
.stb (cmd_stb), // input
.addr (wa), // output[15:0]
.data (di), // output[31:0]
.we ({we_cmd,we_rel,we_abs}) // output
);
status_generate #(
.STATUS_REG_ADDR(SENSI2C_STATUS_REG),
.PAYLOAD_BITS(7) // STATUS_PAYLOAD_BITS)
) status_generate_i (
.rst (rst), // input
.clk (mclk), // input
.we (set_status_w), // input
.wd (di[7:0]), // input[7:0]
.status ({busy, frame_num, sda_in, scl_in}), // input[25:0]
.ad (status_ad), // output[7:0]
.rq (status_rq), // output
.start (status_start) // input
);
always @ (posedge mclk) begin
if (wen) di_r <= di; // 32 bit command takes 6 cycles, so di_r can hold data for up to this long
wen_r <= {wen_r[0],wen}; // is it needed?
wen_fifo <= {wen_fifo[0],we_rel || we_abs};
// signals related to writing to i2c FIFO
// delayed versions of address, data write strobe
// if (wen) wad [ 3:0] <= wa[ 3:0];
// if (wen || wen_d[0]) di_1[15:0] <= di[15:0];
// di_2[15:0] <= di_1[15:0];
// di_3[15:0] <= di_2[15:0];
// wen_d[4:0] <= {wen_d[3:1],wen_d[0] && !is_ctl,wen};
// wen_d[3:0] <= {wen_d[2:1],wen_d[0] && !is_ctl,wen};
// software i2c signals
// wen_i2c_soft <= wen_d[0] && is_ctl;
// decoded commands, valid next cycle after we_*
reset_cmd <= set_ctrl_w && di[14];
run_cmd <= set_ctrl_w && di[13];
bytes_cmd <= set_ctrl_w && di[11];
dly_cmd <= set_ctrl_w && di[ 8];
// direct i2c control, valid 1 cycle after we_*
if (i2c_run) scl_en_soft <= 1'b0;
else if (set_ctrl_w & |di[1:0]) scl_en_soft <= (di[1:0]!=2'h3);
if (i2c_run) scl_soft <= 1'b0;
else if (set_ctrl_w & |di[1:0]) scl_soft <= (di[1:0]==2'h2);
if (i2c_run) sda_en_soft <= 1'b0;
else if (set_ctrl_w & |di[3:2]) sda_en_soft <= (di[3:2]!=2'h3);
if (i2c_run) sda_soft <= 1'b0;
else if (set_ctrl_w & |di[3:2]) sda_soft <= (di[3:2]==2'h2);
// setting i2c control parameters, valid 2 cycles after we_*
if (bytes_cmd) i2c_bytes[1:0] <= di_r[10:9];
if (dly_cmd) i2c_dly[7:0] <= di_r[ 7:0];
if (reset_cmd) i2c_enrun <= 1'b0;
else if (run_cmd) i2c_enrun <= di_r[12];
// write pointer memory
wpage0_inc <= {wpage0_inc[0],pre_wpage0_inc};
// reset pointers in all 16 pages:
reset_on <= reset_cmd || (reset_on && !(wpage0_inc && ( wpage0[3:0] == 4'hf)));
// request to clear pointer(s)? for one page - during reset or delayed frame sync (if previous was not finished)
req_clr <= frame_sync || (req_clr && !wpage0_inc);
if (reset_cmd) wpage0 <= 0;
// else if (frame_0) wpage0 <= 0;
else if (wpage0_inc) wpage0<=wpage0+1;
if (reset_cmd) wpage_prev<=4'hf;
else if (wpage0_inc) wpage_prev<=wpage0;
if (we_abs) wpage_wr <= ((wa==wpage_prev)? wpage0[3:0] : wa);
else if (we_rel) wpage_wr <= wpage0+wa;
else if (wpage0_inc) wpage_wr <= wpage_prev; // only for erasing?
// we_fifo_wp <= wen || wpage0_inc; // during commands and during reset?
/// we_fifo_wp <= wen_fifo[0] || wpage0_inc; // during commands and during reset?
we_fifo_wp <= wen_fifo[0] || we_rel || we_abs; // ??
// reg [1:0] wen_r;
// reg [1:0] wen_fifo;
if (wen_fifo[0]) fifo_wr_pointers_outw_r[5:0] <= fifo_wr_pointers_outw[5:0];
// write to dual-port pointer memory
if (we_fifo_wp) fifo_wr_pointers[wpage_wr] <= wpage0_inc[1]? 6'h0:(fifo_wr_pointers_outw_r[5:0]+1);
fifo_wr_pointers_outr_r[5:0] <= fifo_wr_pointers_outr[5:0]; // just register distri
// command i2c fifo (RAMB16_S9_S18)
if (wen_fifo[0]) i2c_cmd_wa <= {wpage_wr[3:0],fifo_wr_pointers_outw[5:0]};
// if (wen_d[1]) i2c_cmd_wa[10:1] <= {wpage_wr[3:0],fifo_wr_pointers_outw[5:0]};
// i2c_cmd_wa[0] <= !wen_d[1]; // 0 for the first in a pair, 1 - for the second
// i2c_cmd_we <= !reset_cmd && (wen_d[1] || (i2c_cmd_we && !wen_d[3])); //reset_cmd added to keep simulator happy
i2c_cmd_we <= !reset_cmd && wen_fifo[0];
// signals related to reading from i2c FIFO
if (reset_on) page_r<=0;
else if (page_r_inc[0]) page_r<=page_r+1;
if (reset_cmd || page_r_inc[0]) rpointer[5:0] <= 6'h0;
else if (i2c_done) rpointer[5:0] <= rpointer[5:0] + 1;
i2c_run <= !reset_cmd && (i2c_start || (i2c_run && !i2c_done));
i2c_start <= i2c_enrun && !i2c_run && !i2c_start && (rpointer[5:0]!= fifo_wr_pointers_outr_r[5:0]) && !(|page_r_inc);
page_r_inc[1:0] <= {page_r_inc[0],
!i2c_run && // not i2c in progress
!page_r_inc[0] && // was not incrementing in previous cycle
(rpointer == fifo_wr_pointers_outr_r) && // nothing left for this page
(page_r != wpage0)}; // not already the write-open current page
//i2c sequence generation
if (!i2c_run) bytes_left[1:0] <= i2c_bytes[1:0];
else if (i2c_dataseq_done) bytes_left[1:0] <= bytes_left[1:0] -1;
if (!i2c_run) byte_sending[1:0] <= 2'h3;
else if (i2c_dataseq_done) byte_sending[1:0] <= byte_sending[1:0] + 1;
if (!i2c_run) byte_number[1:0] <= 2'h3;
else if (i2c_byte_start[2])byte_number[1:0] <= byte_number[1:0] - 1;
if (!i2c_run || i2c_dly_over) dly_cntr[7:0] <= i2c_dly[7:0];
else dly_cntr[7:0] <= dly_cntr[7:0] - 1;
i2c_dly_pre_over <= i2c_dly_pre2_over; // period = 3..258
i2c_dly_over <=i2c_dly_pre_over;
i2c_dataseq_done <= i2c_dataseq_last && i2c_dly_pre_over;
i2c_byte_start[2:0] <= {i2c_byte_start[1:0],
(i2c_startseq_last || (i2c_dataseq_last && (bytes_left[1:0] != 2'h0))) && i2c_dly_pre2_over };
i2c_sr_shift <= i2c_bit_last && !(i2c_dataseq_last) && i2c_dly_pre_over;
i2c_stop_start <= i2c_dataseq_last && (bytes_left[1:0] == 2'h0) && i2c_dly_pre_over ;
i2c_done <= i2c_stopseq_last && i2c_dly_pre_over;
if (i2c_byte_start[2]) i2c_sr[8:0] <= {i2c_data[7:0], 1'b1};
else if (i2c_sr_shift) i2c_sr[8:0] <= {i2c_sr[7:0], 1'b1};
if (!i2c_run) i2c_state[5:0] <= 6'h2a; // start of start seq
else if (i2c_stop_start) i2c_state[5:0] <= 6'h26; // start of stop seq
else if (i2c_byte_start[2]) i2c_state[5:0] <= 6'h23; // start of data seq
else if (i2c_dly_over) i2c_state[5:0] <= i2c_state[5:0] - 1;
// now creating output signals
scl_0 <= (i2c_is_start && (i2c_state[1:0]!=2'h0)) ||
(i2c_is_stop && !i2c_state[1]) ||
(i2c_is_data && (i2c_state[1] ^i2c_state[0])) ||
!i2c_run;
sda_0 <= (i2c_is_start && i2c_state[1]) ||
(i2c_is_stop && (i2c_state[1:0]==2'h0)) ||
(i2c_is_data && i2c_sr[8]) ||
!i2c_run;
sda_hard <= sda_0;
scl_hard <= scl_0;
sda_en_hard <= i2c_run && (!sda_0 || (!i2c_is_ackn && !sda_hard));
if (wen) busy_cntr <= 4'hf;
else if (|busy_cntr) busy_cntr <= busy_cntr-1;
busy <= (i2c_enrun && ((rpointer[5:0]!= fifo_wr_pointers_outr_r[5:0]) || (page_r!=wpage0))) ||
(|busy_cntr) ||
i2c_run ||
reset_on;
end
ram_var_w_var_r #(
.REGISTERS(1), // try to delay i2c_byte_start by one more cycle
.LOG2WIDTH_WR(5),
.LOG2WIDTH_RD(3)
) i_fifo (
.rclk(mclk), // input
.raddr({page_r[3:0], rpointer[5:0], byte_number[1:0]}), // input[11:0]
.ren(i2c_byte_start[0]), // input
.regen(i2c_byte_start[1]), // input
.data_out(i2c_data[7:0]), // output[7:0]
.wclk(mclk), // input
.waddr(i2c_cmd_wa), // input[9:0]
.we(i2c_cmd_we), // input
.web(8'hff), // input[7:0]
.data_in(di_r) // input[31:0]
);
/*
RAMB16_S9_S18 i_fifo (
.DOA(i2c_data[7:0]), // Port A 8-bit Data Output
.DOPA(), // Port A 1-bit Parity Output
.ADDRA({page_r[2:0],
rpointer[5:0],
byte_number[1:0]}), // Port A 11-bit Address Input
.CLKA(mclk), // Port A Clock
.DIA(8'h0), // Port A 8-bit Data Input
.DIPA(1'b0), // Port A 1-bit parity Input
.ENA(i2c_byte_start[0]), // Port A RAM Enable Input
.SSRA(1'b0), // Port A Synchronous Set/Reset Input
.WEA(1'b0), // Port A Write Enable Input
.DOB(), // Port B 16-bit Data Output
.DOPB(), // Port B 2-bit Parity Output
.ADDRB(i2c_cmd_wa[9:0]), // Port B 10-bit Address Input
.CLKB(mclk), // Port B Clock
.DIB(di_3[15:0]), // Port B 16-bit Data Input
.DIPB(2'b0), // Port-B 2-bit parity Input
.ENB(i2c_cmd_we), // PortB RAM Enable Input
.SSRB(1'b0), // Port B Synchronous Set/Reset Input
.WEB(1'b1) // Port B Write Enable Input
);
*/
endmodule
/*******************************************************************************
* Module: sensor_i2c_io
* Date:2015-05-15
* Author: andrey
* Description: sensor_i2c with I/O pad elements
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* sensor_i2c_io.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.
*
* sensor_i2c_io.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/> .
*******************************************************************************/
`timescale 1ns/1ps
module sensor_i2c_io#(
parameter SENSI2C_ABS_ADDR = 'h300,
parameter SENSI2C_REL_ADDR = 'h310,
parameter SENSI2C_ADDR_MASK = 'h3f0, // both for SENSI2C_ABS_ADDR and SENSI2C_REL_ADDR
parameter SENSI2C_CTRL_ADDR = 'h320,
parameter SENSI2C_CTRL_MASK = 'h3fe,
parameter SENSI2C_CTRL = 'h0,
parameter SENSI2C_STATUS = 'h1,
parameter SENSI2C_STATUS_REG = 'h30,
parameter integer SENSI2C_DRIVE = 12,
parameter SENSI2C_IBUF_LOW_PWR = "TRUE",
parameter SENSI2C_IOSTANDARD = "DEFAULT",
parameter SENSI2C_SLEW = "SLOW"
)(
input rst,
input mclk, // global clock, half DDR3 clock, synchronizes all I/O thorough 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 frame_sync, // increment/reset frame number
inout scl,
inout sda
);
wire scl_in;
wire sda_in;
wire scl_out;
wire sda_out;
wire scl_en;
wire sda_en;
sensor_i2c #(
.SENSI2C_ABS_ADDR(SENSI2C_ABS_ADDR),
.SENSI2C_REL_ADDR(SENSI2C_REL_ADDR),
.SENSI2C_ADDR_MASK(SENSI2C_ADDR_MASK),
.SENSI2C_CTRL_ADDR(SENSI2C_CTRL_ADDR),
.SENSI2C_CTRL_MASK(SENSI2C_CTRL_MASK),
.SENSI2C_CTRL(SENSI2C_CTRL),
.SENSI2C_STATUS(SENSI2C_STATUS),
.SENSI2C_STATUS_REG(SENSI2C_STATUS_REG)
) sensor_i2c_i (
.rst(rst), // input
.mclk(mclk), // input
.cmd_ad(cmd_ad), // input[7:0]
.cmd_stb(cmd_stb), // input
.status_ad(status_ad), // output[7:0]
.status_rq(status_rq), // output
.status_start(status_start), // input
.frame_sync(frame_sync), // input
.scl_in(scl_in), // input
.sda_in(sda_in), // input
.scl_out(scl_out), // output
.sda_out(sda_out), // output
.scl_en(scl_en), // output
.sda_en(sda_en) // output
);
iobuf #(
.DRIVE (SENSI2C_DRIVE),
.IBUF_LOW_PWR (SENSI2C_IBUF_LOW_PWR),
.IOSTANDARD (SENSI2C_IOSTANDARD),
.SLEW (SENSI2C_SLEW)
) iobuf_scl_i (
.O (scl_in), // output
.IO (scl), // inout
.I (scl_out), // input
.T (!scl_en) // input
);
iobuf #(
.DRIVE (SENSI2C_DRIVE),
.IBUF_LOW_PWR (SENSI2C_IBUF_LOW_PWR),
.IOSTANDARD (SENSI2C_IOSTANDARD),
.SLEW (SENSI2C_SLEW)
) iobuf_sda_i (
.O (sda_in), // output
.IO (sda), // inout
.I (sda_out), // input
.T (!sda_en) // input
);
endmodule
......@@ -25,23 +25,33 @@ module cmd_deser#(
parameter ADDR_MASK='hffff,
parameter NUM_CYCLES=6,
parameter ADDR_WIDTH=16,
parameter DATA_WIDTH=32
parameter DATA_WIDTH=32,
parameter ADDR1=0, // optional second address
parameter ADDR_MASK1=0, // optional second mask
parameter ADDR2=0, // optional third address
parameter ADDR_MASK2=0 // optional third mask
)(
input rst,
input clk,
input [7:0] ad,
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output we
input rst,
input clk,
input [7:0] ad,
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output [(ADDR_MASK2!=0)?2:((ADDR_MASK1!=0)?1:0):0] we
);
localparam WE_WIDTH=(ADDR_MASK2!=0)?3:((ADDR_MASK1!=0)?2:1);
generate
if (NUM_CYCLES==1)
cmd_deser_single # (
.ADDR(ADDR),
.ADDR_MASK(ADDR_MASK),
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
.ADDR (ADDR),
.ADDR_MASK (ADDR_MASK),
.ADDR_WIDTH (ADDR_WIDTH),
.DATA_WIDTH (DATA_WIDTH),
.ADDR1 (ADDR1),
.ADDR_MASK1 (ADDR_MASK1),
.ADDR2 (ADDR2),
.ADDR_MASK2 (ADDR_MASK2),
.WE_WIDTH (WE_WIDTH)
) i_cmd_deser_single (
.rst(rst),
.clk(clk),
......@@ -56,7 +66,12 @@ module cmd_deser#(
.ADDR(ADDR),
.ADDR_MASK(ADDR_MASK),
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
.DATA_WIDTH(DATA_WIDTH),
.ADDR1 (ADDR1),
.ADDR_MASK1 (ADDR_MASK1),
.ADDR2 (ADDR2),
.ADDR_MASK2 (ADDR_MASK2),
.WE_WIDTH (WE_WIDTH)
) i_cmd_deser_dual (
.rst(rst),
.clk(clk),
......@@ -72,7 +87,12 @@ module cmd_deser#(
.ADDR_MASK(ADDR_MASK),
.NUM_CYCLES(NUM_CYCLES),
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
.DATA_WIDTH(DATA_WIDTH),
.ADDR1 (ADDR1),
.ADDR_MASK1 (ADDR_MASK1),
.ADDR2 (ADDR2),
.ADDR_MASK2 (ADDR_MASK2),
.WE_WIDTH (WE_WIDTH)
) i_cmd_deser_multi (
.rst(rst),
.clk(clk),
......@@ -91,7 +111,12 @@ module cmd_deser_single#(
parameter ADDR=0,
parameter ADDR_MASK='hffff,
parameter ADDR_WIDTH=8, // <=8
parameter DATA_WIDTH=1 // will 0 work?
parameter DATA_WIDTH=1, // will 0 work?
parameter ADDR1=0,
parameter ADDR_MASK1=0,
parameter ADDR2=0,
parameter ADDR_MASK2=0,
parameter WE_WIDTH=1
)(
input rst,
input clk,
......@@ -99,27 +124,28 @@ module cmd_deser_single#(
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output we
output [WE_WIDTH-1:0] we
);
localparam ADDR_LOW= ADDR & 8'hff;
// localparam ADDR_HIGH=(ADDR>>8) & 8'hff;
localparam ADDR_MASK_LOW= ADDR_MASK & 8'hff;
// localparam ADDR_MASK_HIGH=(ADDR_MASK>>8) & 8'hff;
localparam ADDR_LOW1= ADDR1 & 8'hff;
localparam ADDR_MASK_LOW1= ADDR_MASK1 & 8'hff;
localparam ADDR_LOW2= ADDR2 & 8'hff;
localparam ADDR_MASK_LOW2= ADDR_MASK2 & 8'hff;
reg [7:0] deser_r;
// reg stb_d;
wire match_low;
reg we_r;
wire [2:0] match_low;
reg [WE_WIDTH-1:0] we_r;
assign we=we_r;
assign match_low= ((ad ^ ADDR_LOW) & (8'hff & ADDR_MASK_LOW)) == 0;
assign match_low= { // unused bits will be optimized
((ad ^ ADDR_LOW2) & (8'hff & ADDR_MASK_LOW2)) == 0,
((ad ^ ADDR_LOW1) & (8'hff & ADDR_MASK_LOW1)) == 0,
((ad ^ ADDR_LOW ) & (8'hff & ADDR_MASK_LOW )) == 0};
always @ (posedge rst or posedge clk) begin
if (rst) we_r <= 0;
else we_r <= match_low && stb;
if (rst) deser_r <= 0;
else if (match_low && stb) deser_r <= ad;
end
always @ (posedge clk) begin
if (match_low && stb) deser_r <= ad;
else if ((|match_low) && stb) deser_r <= ad;
end
assign data={DATA_WIDTH{1'b0}};
assign addr=deser_r[ADDR_WIDTH-1:0];
......@@ -129,7 +155,12 @@ module cmd_deser_dual#(
parameter ADDR=0,
parameter ADDR_MASK='hffff,
parameter ADDR_WIDTH=12, // <=16
parameter DATA_WIDTH=1 // will 0 work?
parameter DATA_WIDTH=1, // will 0 work?
parameter ADDR1=0,
parameter ADDR_MASK1=0,
parameter ADDR2=0,
parameter ADDR_MASK2=0,
parameter WE_WIDTH=1
)(
input rst,
input clk,
......@@ -137,30 +168,45 @@ module cmd_deser_dual#(
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output we
output [WE_WIDTH-1:0] we
);
localparam ADDR_LOW= ADDR & 8'hff;
localparam ADDR_HIGH=(ADDR>>8) & 8'hff;
localparam ADDR_MASK_LOW= ADDR_MASK & 8'hff;
localparam ADDR_MASK_HIGH=(ADDR_MASK>>8) & 8'hff;
localparam ADDR_LOW1= ADDR1 & 8'hff;
localparam ADDR_MASK_LOW1= ADDR_MASK1 & 8'hff;
localparam ADDR_LOW2= ADDR2 & 8'hff;
localparam ADDR_MASK_LOW2= ADDR_MASK2 & 8'hff;
localparam ADDR_HIGH1=(ADDR1>>8) & 8'hff;
localparam ADDR_MASK_HIGH1=(ADDR_MASK1>>8) & 8'hff;
localparam ADDR_HIGH2=(ADDR2>>8) & 8'hff;
localparam ADDR_MASK_HIGH2=(ADDR_MASK2>>8) & 8'hff;
reg [15:0] deser_r;
reg stb_d;
wire match_low;
wire match_high;
reg we_r;
wire [2:0] match_low;
wire [2:0] match_high;
reg [WE_WIDTH-1:0] we_r;
assign we=we_r;
assign match_low= ((ad ^ ADDR_LOW) & (8'hff & ADDR_MASK_LOW)) == 0;
assign match_high= ((ad ^ ADDR_HIGH) & (8'hff & ADDR_MASK_HIGH)) == 0;
assign match_low= {((ad ^ ADDR_LOW2) & (8'hff & ADDR_MASK_LOW2)) == 0,
((ad ^ ADDR_LOW1) & (8'hff & ADDR_MASK_LOW1)) == 0,
((ad ^ ADDR_LOW ) & (8'hff & ADDR_MASK_LOW )) == 0};
assign match_high= {((ad ^ ADDR_HIGH2) & (8'hff & ADDR_MASK_HIGH2)) == 0,
((ad ^ ADDR_HIGH1) & (8'hff & ADDR_MASK_HIGH1)) == 0,
((ad ^ ADDR_HIGH ) & (8'hff & ADDR_MASK_HIGH )) == 0};
always @ (posedge rst or posedge clk) begin
if (rst) stb_d <= 1'b0;
else stb_d <= match_low && stb;
if (rst) we_r <= 1'b0;
else we_r <= match_high && stb_d;
end
always @ (posedge clk) begin
if ((match_low && stb) || (match_high && stb_d)) deser_r[15:0] <= {ad,deser_r[15:8]};
if (rst) deser_r[15:0] <= 0;
else if ((match_low && stb) || (match_high && stb_d)) deser_r[15:0] <= {ad,deser_r[15:8]};
end
assign data=0; // {DATA_WIDTH{1'b0}};
assign addr=deser_r[ADDR_WIDTH-1:0];
......@@ -171,7 +217,12 @@ module cmd_deser_multi#(
parameter ADDR_MASK='hffff,
parameter NUM_CYCLES=6, // >=3
parameter ADDR_WIDTH=16,
parameter DATA_WIDTH=32
parameter DATA_WIDTH=32,
parameter ADDR1=0,
parameter ADDR_MASK1=0,
parameter ADDR2=0,
parameter ADDR_MASK2=0,
parameter WE_WIDTH=1
)(
input rst,
input clk,
......@@ -179,36 +230,58 @@ module cmd_deser_multi#(
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output we
output [WE_WIDTH-1:0] we
);
localparam ADDR_LOW= ADDR & 8'hff;
localparam ADDR_HIGH=(ADDR>>8) & 8'hff;
localparam ADDR_MASK_LOW= ADDR_MASK & 8'hff;
localparam ADDR_MASK_HIGH=(ADDR_MASK>>8) & 8'hff;
localparam ADDR_LOW1= ADDR1 & 8'hff;
localparam ADDR_MASK_LOW1= ADDR_MASK1 & 8'hff;
localparam ADDR_LOW2= ADDR2 & 8'hff;
localparam ADDR_MASK_LOW2= ADDR_MASK2 & 8'hff;
localparam ADDR_HIGH1=(ADDR1>>8) & 8'hff;
localparam ADDR_MASK_HIGH1=(ADDR_MASK1>>8) & 8'hff;
localparam ADDR_HIGH2=(ADDR2>>8) & 8'hff;
localparam ADDR_MASK_HIGH2=(ADDR_MASK2>>8) & 8'hff;
reg [8*NUM_CYCLES-1:0] deser_r;
reg stb_d;
wire match_low;
wire match_high;
reg [2:0] stb_d;
wire [2:0] match_low;
wire [2:0] match_high;
reg [NUM_CYCLES-2:0] sr;
// wire [31:0] debug_addr= ADDR;
// wire [31:0] debug_mask= ADDR_MASK;
// wire [31:0] debug_addr_low= ADDR_LOW;
// wire [31:0] debug_addr_high= ADDR_HIGH;
// wire [31:0] debug_mask_low= ADDR_MASK_LOW;
// wire [31:0] debug_mask_high= ADDR_MASK_HIGH;
reg [NUM_CYCLES-2:0] sr1;
reg [NUM_CYCLES-2:0] sr2;
assign we=sr[0]; // we_r;
assign match_low= ((ad ^ ADDR_LOW) & (8'hff & ADDR_MASK_LOW)) == 0;
assign match_high= ((ad ^ ADDR_HIGH) & (8'hff & ADDR_MASK_HIGH)) == 0;
assign match_low= {((ad ^ ADDR_LOW2) & (8'hff & ADDR_MASK_LOW2)) == 0,
((ad ^ ADDR_LOW1) & (8'hff & ADDR_MASK_LOW1)) == 0,
((ad ^ ADDR_LOW ) & (8'hff & ADDR_MASK_LOW )) == 0};
assign match_high= {((ad ^ ADDR_HIGH2) & (8'hff & ADDR_MASK_HIGH2)) == 0,
((ad ^ ADDR_HIGH1) & (8'hff & ADDR_MASK_HIGH1)) == 0,
((ad ^ ADDR_HIGH ) & (8'hff & ADDR_MASK_HIGH )) == 0};
always @ (posedge rst or posedge clk) begin
if (rst) stb_d <= 1'b0;
else stb_d <= match_low && stb;
if (rst) sr <= 0;
// else if (match_high && stb_d) sr <= {NUM_CYCLES-1{1'b1}};
else if (match_high && stb_d) sr <= 1 << (NUM_CYCLES-2);
else sr <= {1'b0,sr[NUM_CYCLES-2:1]};
end
always @ (posedge clk) begin
if ((match_low && stb) || (match_high && stb_d) || (|sr)) deser_r[8*NUM_CYCLES-1:0] <= {ad,deser_r[8*NUM_CYCLES-1:8]};
if (rst) stb_d <= 0;
else stb_d <= stb?match_low:3'b0;
if (rst) sr <= 0;
else if (match_high[0] && stb_d) sr <= 1 << (NUM_CYCLES-2);
else sr <= {1'b0,sr[NUM_CYCLES-2:1]};
if (rst) sr1<= 0;
else if (match_high[1] && stb_d) sr1 <= 1 << (NUM_CYCLES-2);
else sr1 <= {1'b0,sr1[NUM_CYCLES-2:1]};
if (rst) sr2 <= 0;
else if (match_high[2] && stb_d) sr2 <= 1 << (NUM_CYCLES-2);
else sr2 <= {1'b0,sr2[NUM_CYCLES-2:1]};
if (rst) deser_r[8*NUM_CYCLES-1:0] <= 0;
else if ((match_low && (|stb)) ||
(match_high && (|stb_d)) ||
(|sr) || (|sr1) || (|sr2)) deser_r[8*NUM_CYCLES-1:0] <= {ad,deser_r[8*NUM_CYCLES-1:8]};
end
assign data=deser_r[DATA_WIDTH+15:16];
assign addr=deser_r[ADDR_WIDTH-1:0];
......
/*******************************************************************************
* Module: iobuf
* Date:2015-05-15
* Author: andrey
* Description: Wrapper for IOBUF primitive
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* iobuf.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.
*
* iobuf.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/> .
*******************************************************************************/
`timescale 1ns/1ps
module iobuf#(
parameter integer DRIVE = 12,
parameter IBUF_LOW_PWR = "TRUE",
parameter IOSTANDARD = "DEFAULT",
`ifdef XIL_TIMING
parameter LOC = " UNPLACED",
`endif
parameter SLEW = "SLOW"
)(
output O,
inout IO,
input I,
input T
);
IOBUF #(
.DRIVE (DRIVE),
.IBUF_LOW_PWR (IBUF_LOW_PWR),
.IOSTANDARD (IOSTANDARD),
.SLEW (SLEW)
) IOBUF_i (
.O (O), // output
.IO (IO), // inout
.I (I), // input
.T (T) // input
);
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