Commit 10e88af6 authored by Andrey Filippov's avatar Andrey Filippov

Added modules to byte-serialize write commands and de-serialize them at destinations

parent 56a59c77
/*******************************************************************************
* Module: cmd_mux
* Date:2015-01-11
* Author: andrey
* Description: Command multiplexer between AXI and frame-based command sequencer
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* cmd_mux.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_mux.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_mux #(
parameter AXI_WR_ADDR_BITS= 13,
parameter CONTROL_ADDR = 'h1000, // AXI write address of control write registers
parameter CONTROL_ADDR_MASK = 'h1400, // AXI write address of control registers
// parameter CONTROL_SS_ADDR= 'h0200, // single-cycle command (2-6 cycles decoded by ROM form some address bits)
// parameter CONTROL_SS_MASK= 'h0200,
parameter NUM_CYCLES_LOW_BIT= 6 // decode addresses [NUM_CYCLES_LOW_BIT+:4] into command a/d length
// now all control addresses may generate busy, but only for command sequencer and multy-byte commands
// parameter BUSY_WR_ADDR = 'h1800, // AXI write address to generate busy
// parameter BUSY_WR_ADDR_MASK = 'h1c00 // AXI write address mask to generate busy
) (
input clk,
input mclk,
input rst,
// direct commands from AXI. No wait but for multi-cycle output and command sequencer (having higher priority)
input [AXI_WR_ADDR_BITS-1:0] pre_waddr, // AXI write address, before actual writes (to generate busy), valid@start_burst
input start_wburst, // burst start - should generate ~ready (should be AND-ed with !busy internally)
input [AXI_WR_ADDR_BITS-1:0] waddr, // write address, valid with wr_en
input wr_en, // write enable
input [31:0] wdata, // write data, valid with waddr and wr_en
output busy, // interface busy (combinatorial delay from start_wburst and pre_addr), controls AXI FIFO
// frame-based commands from the command sequencer (no wait but for multi-cycle output
input [AXI_WR_ADDR_BITS-1:0] cseq_waddr, // write address, valid with cseq_wr_en
input cseq_wr_en, // write enable
input [31:0] cseq_wdata, // write data, valid with cseq_waddr and cseq_wr_en
output cseq_ackn, // command sequencer address/data accepted
// Write address /data/strobe to slaves. Both parallel and byte-serial data available. COmbined from AXI and command sequencer
output [AXI_WR_ADDR_BITS-1:0] par_waddr, // parallel address
output [31:0] par_data, // parallel 32-bit data
output [7:0] byte_ad, // byte-wide address/data (AL-AH-DB0-DB1-DB2-DB3)
output ad_stb // low address output strobe (and parallel A/D)
);
// Minimal - 1 cycle, AH=DB0=DB1=DB2=DB3=0;
reg busy_r=0;
reg selected=0; // address range to be processed here (outside - buffer(s) and command sequencer?)
wire fifo_half_empty; // just debugging with (* keep = "true" *)
wire selected_w;
wire ss; // current command (in par_waddr) is a single-cycle one
reg [47:0] par_ad;
reg ad_stb_r; // low address output strobe (and parallel A/D)
reg cmdseq_full_r; // address/data from the command sequencer is loaded to internal register (cseq_waddr_r,cseq_wdata_r)
reg [AXI_WR_ADDR_BITS-1:0] cseq_waddr_r; // registered command address from the sequencer
reg [31:0] cseq_wdata_r; // registered command data from the sequencer
reg [3:0] seq_length; // encoded ROM output - number of cycles in command sequence, [3] - single cycle
reg [4:0] seq_busy_r; // shift register loaded by decoded seq_length
wire [3:0] seq_length_rom_a; // address range used to determine command length
wire can_start_w; // can start command cycle (either from sequencer or from AXI)
wire start_w; // start cycle
wire start_axi_w; // start cycle from the AXI (==fifo_re)
wire fifo_nempty;
wire [AXI_WR_ADDR_BITS-1:0] waddr_fifo_out;
wire [31:0] wdata_fifo_out;
assign selected_w=((pre_waddr ^ CONTROL_ADDR) & CONTROL_ADDR_MASK)==0;
assign busy=busy_r && (start_wburst? selected_w: selected);// should be just combinatorial delay from start_wburst and decoded command
assign par_waddr=par_ad[AXI_WR_ADDR_BITS-1:0]; // parallel address
assign par_data=par_ad[47:16]; // parallel 32-bit data
assign byte_ad=par_ad[7:0]; // byte-wide address/data (AL-AH-DB0-DB1-DB2-DB3)
assign ad_stb=ad_stb_r; // low address output strobe (and parallel A/D)
assign seq_length_rom_a=par_ad[NUM_CYCLES_LOW_BIT+:4];
assign ss= seq_length[3];
always @ (posedge clk or posedge rst) begin
if (rst) selected <= 1'b0;
else if (start_wburst) selected <= selected_w;
if (rst) busy_r <= 1'b0;
else busy_r <= !fifo_half_empty;
end
// ROM command length decoder TODO: put actual data
// always @ (seq_length_rom_a) begin
always @*
case (seq_length_rom_a) // just temporary - fill out later
4'h00:seq_length<=9; // single-cycle
4'h01:seq_length<=2; // 2-cycle
4'h02:seq_length<=3;
4'h03:seq_length<=4;
4'h04:seq_length<=5;
4'h05:seq_length<=6; // 6-cycle (full)
4'h06:seq_length<=6;
4'h07:seq_length<=6;
4'h08:seq_length<=6;
4'h09:seq_length<=6;
4'h0a:seq_length<=6;
4'h0b:seq_length<=6;
4'h0c:seq_length<=6;
4'h0d:seq_length<=6;
4'h0e:seq_length<=6;
4'h0f:seq_length<=6;
endcase
always @ (posedge rst or posedge mclk) begin
if (rst) seq_busy_r<=0;
else begin
if (ad_stb) begin
case (seq_length)
4'h2: seq_busy_r<=5'h01;
4'h3: seq_busy_r<=5'h03;
4'h4: seq_busy_r<=5'h07;
4'h5: seq_busy_r<=5'h0f;
4'h6: seq_busy_r<=5'h1f;
default: seq_busy_r<=5'h00;
endcase
end else seq_busy_r <= {1'b0,seq_busy_r[3:0]};
end
end
assign can_start_w= ad_stb_r? ss: !seq_busy_r[1];
assign start_axi_w= can_start_w && ~cmdseq_full_r && fifo_nempty;
assign start_w= can_start_w && (cmdseq_full_r || fifo_nempty);
always @ (posedge rst or posedge mclk) begin
if (rst) ad_stb_r <= 0;
else ad_stb_r <= start_w;
end
always @ (posedge mclk) begin
if (start_w) par_ad <={cmdseq_full_r?cseq_wdata_r:wdata_fifo_out,{(16-AXI_WR_ADDR_BITS){1'b0}},cmdseq_full_r?cseq_waddr_r:waddr_fifo_out};
else par_ad <={8'b0,par_ad[39:0]};
end
assign cseq_ackn= cseq_wr_en && (!cmdseq_full_r || can_start_w); // cmddseq_full has priority over axi, so (can_start_w && cmdseq_full_r)
always @ (posedge rst or posedge mclk) begin
if (rst) cmdseq_full_r <= 0;
else cmdseq_full_r <= cseq_ackn || (cmdseq_full_r && !can_start_w);
end
always @ (posedge mclk) begin
if (cseq_ackn) begin
cseq_waddr_r <= cseq_waddr;
cseq_wdata_r <= cseq_wdata;
end
end
/* FIFO to cross clock boundary */
fifo_cross_clocks #(
.DATA_WIDTH (AXI_WR_ADDR_BITS+32),
.DATA_DEPTH (4)
) fifo_cross_clocks_i (
.rst (rst), // input
.rclk (mclk), // input
.wclk (clk), // input
.we (wr_en && selected), // input
.re (start_axi_w), // input
.data_in ({waddr[AXI_WR_ADDR_BITS-1:0],wdata[31:0]}), // input[15:0]
.data_out ({waddr_fifo_out[AXI_WR_ADDR_BITS-1:0],wdata_fifo_out[31:0]}), // output[15:0]
.nempty (fifo_nempty), // output
.half_empty (fifo_half_empty) // output
);
endmodule
...@@ -177,7 +177,7 @@ module ddrc_control #( ...@@ -177,7 +177,7 @@ module ddrc_control #(
reg busy_r=0; reg busy_r=0;
reg selected=0; reg selected=0;
reg selected_busy=0; reg selected_busy=0; // decoded from address, if false - busy_r is ignored (always ready)
wire fifo_half_empty; // just debugging with (* keep = "true" *) wire fifo_half_empty; // just debugging with (* keep = "true" *)
wire [AXI_WR_ADDR_BITS-1:0] waddr_fifo_out; wire [AXI_WR_ADDR_BITS-1:0] waddr_fifo_out;
......
/*******************************************************************************
* Module: cmd_deser
* Date:2015-01-12
* Author: andrey
* Description: Expand command address/data from a byte-wide
*
* Copyright (c) 2015 <set up in Preferences-Verilog/VHDL Editor-Templates> .
* cmd_deser.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_deser.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_deser#(
parameter ADDR=0,
parameter ADDR_MASK='hffff,
parameter NUM_CYCLES=6,
parameter ADDR_WIDTH=16,
parameter DATA_WIDTH=32
)(
input rst,
input clk,
input [7:0] ad,
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output we
);
generate
if (NUM_CYCLES==1)
cmd_deser_single # (
.ADDR(ADDR),
.ADDR_MASK(ADDR_MASK),
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
) i_cmd_deser_single (
.rst(rst),
.clk(clk),
.ad(ad),
.stb(stb),
.addr(addr),
.data(data),
.we(we)
);
else if (NUM_CYCLES==2)
cmd_deser_dual # (
.ADDR(ADDR),
.ADDR_MASK(ADDR_MASK),
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
) i_cmd_deser_single (
.rst(rst),
.clk(clk),
.ad(ad),
.stb(stb),
.addr(addr),
.data(data),
.we(we)
);
else
cmd_deser_multi # (
.ADDR(ADDR),
.ADDR_MASK(ADDR_MASK),
.NUM_CYCLES(NUM_CYCLES),
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
) i_cmd_deser_single (
.rst(rst),
.clk(clk),
.ad(ad),
.stb(stb),
.addr(addr),
.data(data),
.we(we)
);
endgenerate
endmodule
module cmd_deser_single#(
parameter ADDR=0,
parameter ADDR_MASK='hffff,
parameter ADDR_WIDTH=8, // <=8
parameter DATA_WIDTH=1 // will 0 work?
)(
input rst,
input clk,
input [7:0] ad,
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output 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;
reg [7:0] deser_r;
// reg stb_d;
wire match_low;
reg we_r;
assign we=we_r;
assign match_low= ((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;
end
assign data={DATA_WIDTH{1'b0}};
assign addr=deser_r[ADDR_WIDTH-1:0];
endmodule
module cmd_deser_dual#(
parameter ADDR=0,
parameter ADDR_MASK='hffff,
parameter ADDR_WIDTH=12, // <=16
parameter DATA_WIDTH=1 // will 0 work?
)(
input rst,
input clk,
input [7:0] ad,
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output 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;
reg [15:0] deser_r;
reg stb_d;
wire match_low;
wire match_high;
reg 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;
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]};
end
assign data={DATA_WIDTH{1'b0}};
assign addr=deser_r[ADDR_WIDTH-1:0];
endmodule
module cmd_deser_multi#(
parameter ADDR=0,
parameter ADDR_MASK='hffff,
parameter NUM_CYCLES=6, // >=3
parameter ADDR_WIDTH=16,
parameter DATA_WIDTH=32
)(
input rst,
input clk,
input [7:0] ad,
input stb,
output [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output 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;
reg [8*NUM_CYCLES-1:0] deser_r;
reg stb_d;
wire match_low;
wire match_high;
reg [NUM_CYCLES-2:0] sr;
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;
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 sr <= {1'b0,sr[NUM_CYCLES-3:0]};
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]};
end
assign data=deser_r[DATA_WIDTH+15:16];
assign addr=deser_r[ADDR_WIDTH-1: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