/*!
* Module:mcntrl_ps_pio
* @file mcntrl_ps_pio.v
* @date 2015-01-27
* @author Andrey Filippov
*
* @brief Read/write channels to DDR3 memory with software-programmable
* command sequence
*
* @copyright Copyright (c) 2015 Elphel, Inc.
*
* License:
*
* mcntrl_ps_pio.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.
*
* mcntrl_ps_pio.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 .
*
* 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"
`undef DEBUG_FIFO
module mcntrl_ps_pio#(
parameter MCNTRL_PS_ADDR= 'h100,
parameter MCNTRL_PS_MASK= 'h3e0, // both channels 0 and 1
parameter MCNTRL_PS_STATUS_REG_ADDR= 'h2,
parameter MCNTRL_PS_EN_RST= 'h0,
parameter MCNTRL_PS_CMD= 'h1,
parameter MCNTRL_PS_STATUS_CNTRL= 'h2
)(
input mrst,
input mclk,
// programming interface
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, // byte-wide address/data
output status_rq, // request to send downstream (last byte with rq==0)
input status_start, // acknowledge of address (first byte) from downsteram
// buffers R/W access
// read port 0
input port0_clk,
input port0_re,
input port0_regen,
input [9:0] port0_addr, // includes page
output [31:0] port0_data,
// write port 1
input port1_clk,
input port1_we,
input [9:0] port1_addr, // includes page
input [31:0] port1_data,
// memory controller interface
// read port 0
output reg want_rq,
output reg need_rq,
input channel_pgm_en,
output [9:0] seq_data, // only address
output seq_set,
input seq_done,
input buf_wr,
input buf_wpage_nxt,
input buf_run, // @ posedge, use to force page nimber in the buffer (use fifo)
input buf_wrun, // @ negedge, use to force page nimber in the buffer (use fifo)
input [63:0] buf_wdata,
input buf_rpage_nxt,
input buf_rd, //buf_rd_chn1,
output [63:0] buf_rdata // buf_rdata_chn1
);
localparam CMD_WIDTH=15;
localparam CMD_FIFO_DEPTH=4;
localparam PAGE_FIFO_DEPTH = 4;// fifo depth to hold page numbers for channels (2 bits should be OK now)
localparam PAGE_CNTR_BITS = 4;
reg [PAGE_CNTR_BITS-1:0] pending_pages;
wire [4:0] cmd_a; // just to compare
wire [31:0] cmd_data;
wire cmd_we;
wire [1:0] status_data;
wire [CMD_WIDTH-1:0] cmd_out;
wire cmd_nempty;
wire cmd_half_full; // to status bit
// decoded commands
wire set_cmd_w;
wire set_status_w;
wire set_en_rst; // set enable, reset register
reg [1:0] en_reset;//
wire chn_rst = ~en_reset[0]; // resets command, including fifo;
wire chn_en = &en_reset[1]; // enable requests by channel (continue ones in progress)
// reg mem_run; // sequencer pgm granted and set, waiting/executing memory transfer to/from buffur 0/1
wire busy;
wire short_busy; // does not include memory transaction
wire start;
//reg [1:0] page;
reg [1:0] cmd_set_d;
// command bit fields
wire [9:0] cmd_seq_a= cmd_out[9:0];
wire [1:0] cmd_page= cmd_out[11:10];
wire cmd_need= cmd_out[12];
wire cmd_wr= cmd_out[13]; // chn= cmd_out[13]; command write, not read
wire cmd_wait= cmd_out[14]; // wait cmd finished before proceeding
reg cmd_set;
reg cmd_wait_r;
wire [1:0] page_out;
reg nreset_page_fifo;
reg nreset_page_fifo_neg;
wire cmd_wr_out;
reg [1:0] page_out_r;
reg [1:0] page_out_r_negedge;
reg page_r_set;
reg page_w_set_early;
reg page_w_set_early_negedge;
reg en_page_w_set;
reg page_w_set_negedge;
// assign short_busy= want_rq || need_rq ||want_rq1 || need_rq1 || cmd_set; // cmd_set - advance FIFO
assign short_busy= want_rq || need_rq || cmd_set; // cmd_set - advance FIFO
assign busy= short_busy || (pending_pages != 0); // mem_run;
assign start= chn_en && !short_busy && cmd_nempty && ((pending_pages == 0) || !cmd_wait_r); //(!mem_run || !cmd_wait_r); // do not wait memory transaction if wait
assign seq_data= cmd_seq_a;
assign seq_set=cmd_set;
assign status_data= {cmd_half_full,cmd_nempty | busy};
assign set_cmd_w = cmd_we && (cmd_a== MCNTRL_PS_CMD);
assign set_status_w = cmd_we && (cmd_a== MCNTRL_PS_STATUS_CNTRL);
assign set_en_rst = cmd_we && (cmd_a== MCNTRL_PS_EN_RST);
//PAGE_CNTR_BITS
always @ (posedge mclk) begin
if (mrst) pending_pages <= 0;
else if (chn_rst) pending_pages <= 0;
else if ( cmd_set && !seq_done) pending_pages <= pending_pages + 1;
else if (!cmd_set && seq_done) pending_pages <= pending_pages - 1;
if (mrst) nreset_page_fifo <= 0;
else nreset_page_fifo <= cmd_nempty | busy;
if (mrst) cmd_wait_r <= 0;
else if (channel_pgm_en) cmd_wait_r <= cmd_wait;
if (mrst) en_reset <= 0;
else if (set_en_rst) en_reset <= cmd_data[1:0];
if (mrst) begin
want_rq <= 0;
need_rq <= 0;
end else if (chn_rst || channel_pgm_en) begin
want_rq <= 0;
need_rq <= 0;
end else if (start) begin
want_rq <= 1; // !cmd_chn;
need_rq <= cmd_need; // !cmd_chn && cmd_need;
end
if (mrst) cmd_set <= 0;
else if (chn_rst) cmd_set <= 0;
else cmd_set <= channel_pgm_en;
if (mrst) cmd_set_d <= 0;
else cmd_set_d <= {cmd_set_d[0],cmd_set & ~cmd_wr}; // only for channel0 (memory read)
end
cmd_deser #(
.ADDR (MCNTRL_PS_ADDR),
.ADDR_MASK (MCNTRL_PS_MASK),
.NUM_CYCLES (6),
.ADDR_WIDTH (5),
.DATA_WIDTH (32)
) cmd_deser_mcontr_32bit_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 (MCNTRL_PS_STATUS_REG_ADDR),
.PAYLOAD_BITS (2)
) status_generate_i (
.rst (1'b0), // rst), // input
.clk (mclk), // input
.srst (mrst), // input
.we (set_status_w), // input
.wd (cmd_data[7:0]), // input[7:0]
.status (status_data), // input[25:0]
.ad (status_ad), // output[7:0]
.rq (status_rq), // output
.start (status_start) // input
);
fifo_same_clock #(
.DATA_WIDTH(CMD_WIDTH),
.DATA_DEPTH(CMD_FIFO_DEPTH)
) cmd_fifo_i (
.rst (1'b0),
.clk (mclk),
.sync_rst (chn_rst), // synchronously reset fifo;
.we (set_cmd_w),
.re (cmd_set),
.data_in (cmd_data[CMD_WIDTH-1:0]),
.data_out (cmd_out), //SuppressThisWarning ISExst Assignment to awsize_out ignored, since the identifier is never used
.nempty (cmd_nempty),
.half_full (cmd_half_full)
`ifdef DEBUG_FIFO
,
.under (waddr_under), // output reg
.over (waddr_over), // output reg
.wcount (waddr_wcount), // output[3:0] reg
.rcount (waddr_rcount), // output[3:0] reg
.num_in_fifo(waddr_num_in_fifo) // output[3:0]
`endif
);
// Port 0 (read DDR to AXI) buffer
mcntrl_buf_rd #(
.LOG2WIDTH_RD(5)
) chn0_buf_i (
.ext_clk (port0_clk), // input
.ext_raddr (port0_addr), // input[9:0]
.ext_rd (port0_re), // input
.ext_regen (port0_regen), // input
.ext_data_out (port0_data), // output[31:0]
// .emul64 (1'b0), // input Modify buffer addresses (used for JP4 until a 64-wide mode is implemented)
.wclk (!mclk), // input
.wpage_in (page_out_r_negedge), // page_neg), // input[1:0]
.wpage_set (page_w_set_negedge), // wpage_set_chn0_neg), // input
.page_next (buf_wpage_nxt), // input
.page (), // output[1:0]
.we (buf_wr), // input
.data_in (buf_wdata) // input[63:0]
);
// Port 1 (write DDR from AXI) buffer
mcntrl_buf_wr #(
.LOG2WIDTH_WR(5)
) chn1_buf_i (
.ext_clk (port1_clk), // input
.ext_waddr (port1_addr), // input[9:0]
.ext_we (port1_we), // input
.ext_data_in (port1_data), // input[31:0]
.rclk (mclk), // input
.rpage_in (page_out_r), // page), // input[1:0]
.rpage_set (page_r_set), // rpage_set_chn1), // input
.page_next (buf_rpage_nxt), // input
.page (), // output[1:0]
.rd (buf_rd), // input
.data_out (buf_rdata) // output[63:0]
);
fifo_same_clock #(
.DATA_WIDTH(3),
.DATA_DEPTH(PAGE_FIFO_DEPTH)
) page_fifo1_i (
.rst (1'b0),
.clk (mclk), // posedge
.sync_rst (mrst || !nreset_page_fifo), // synchronously reset fifo;
.we (channel_pgm_en),
.re (buf_run),
.data_in ({cmd_wr,cmd_page}), // page),
.data_out ({cmd_wr_out,page_out}),
.nempty (), // page_fifo1_nempty),
.half_full ()
);
always @ (posedge mclk) begin
if (mrst) page_out_r <= 0;
else if (buf_run) page_out_r <= page_out;
end
always @ (posedge mclk) begin
page_r_set <= cmd_wr_out && buf_run; // page_out_r, page_r_set - output to buffer
page_w_set_early <= !cmd_wr_out && buf_run;
end
always @ (negedge mclk) begin
nreset_page_fifo_neg <= nreset_page_fifo;
page_w_set_early_negedge <= page_w_set_early;
page_out_r_negedge <= page_out_r;
if (!nreset_page_fifo_neg || buf_wrun) en_page_w_set <= 0;
else if (page_w_set_early_negedge) en_page_w_set <= 1;
page_w_set_negedge <= en_page_w_set && buf_wrun;
end
endmodule