aboutsummaryrefslogtreecommitdiffstats
path: root/projects/U50-HBM/verilog/cmd_gen.v
blob: 38930737b599bc95e693e525de660248fa0c44e8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
`include "project.vh"
`include "parameters.vh"

// The DRAM Bender programmmable core that generates 4 DDR instructions per cycle and is clocked at 1/4 DDR4 memory clock
// This is due to the fact that the DDR4 PHY interface can process 4 commands per dfi_clock cycle

// Unlike the DDR4 PHY interface, the HBM PHY can process 2 commands per dfi_clock cycle
// Since DRAM Bender is designed in a way to generate 4 DDR instructions, we will buffer 4 instructions per fab_clock cycle,
// and clock the HBM_adapter at 2x fab_clock (e.g. dfi_clock = 2x fab_clock). And then we will have HBM_clock = 2x dfi_clock.
// In this way, we will still be able to process 4 instructions per fab_clock cycle.

module cmd_gen #(parameter CKE_WIDTH = 1, RANK_WIDTH = 1, DQ_WIDTH = 64, DRAM_CMD_SLOTS = 4,
				 DATA_BUF_ADDR_WIDTH = 5, DBUF_WIDTH = 4, DQ_BURST = 8)
	(
      // common signals
      input                                             clk,
      input                                             rst,
      //input                                             dfi_clk,
      //input                                             dfi_rst_n,
      
      //other control signals
      //input                                             init_calib_complete,
      //input                                             process_iseq,
      
      // ddr_pipeline <-> outer module if
      input [3:0]                 ddr_write,
      input [3:0]                 ddr_read,
      input [3:0]                 ddr_pre,
      input [3:0]                 ddr_act,
      input [3:0]                 ddr_ref,
      input [3:0]                 hbm_sel_ch, // used for channel select
      input [3:0]                 ddr_nop,
      input [3:0]                 ddr_ap,
    //input [3:0]                 ddr_half_bl,
      input [3:0]                 ddr_rank, // can be used to indicate PC
      input [3:0]                 ddr_pall,
      input [4*`HBM_CH_WIDTH-1:0] hbm_ch,
      input [4*`BG_WIDTH-1:0]     ddr_bg,
      input [4*`BANK_WIDTH-1:0]   ddr_bank,
      input [4*`COL_WIDTH-1:0]    ddr_col,
      input [4*`ROW_WIDTH-1:0]    ddr_row,
      input [511:0]               ddr_wdata,
      
      //output	[4*`ROW_ADDR_WIDTH-1:0]	   row_addr_4,
      //output	[4*`COL_ADDR_WIDTH-1:0]	   col_addr_4,
      //output	[4*`BA_ADDR_WIDTH-1:0]	   ba_addr_4,
      //output	[511:0]                    wrdata_4,
      //output	[4*`CMD_TYPE_WIDTH-1:0]	   cmd_type_4,
      //output 	[4*`PC_WIDTH-1:0]		   BA4_4, // indicates target PC
      
      output [127:0]             fifo_data,
      output [1023:0]            wrdata    // have one output of 1024 bit for fifo, the other signals are now wires. Make sure to layout data as needed.
      
    );
    
    reg	[4*`ROW_ADDR_WIDTH-1:0]	   row_addr_ns;
    reg	[4*`COL_ADDR_WIDTH-1:0]	   col_addr_ns;
    reg	[4*`BA_ADDR_WIDTH-1:0]	   ba_addr_ns;
    reg	[511:0]                    wrdata_ns;
    reg	[4*`CMD_TYPE_WIDTH-1:0]	   cmd_type_ns;
    reg [4*`PC_WIDTH-1:0]	       BA4_ns;
    reg [4*`HBM_CH_WIDTH-1:0]  channel_id_ns; 
    
    reg	[4*`ROW_ADDR_WIDTH-1:0]	   row_addr_r;
    reg	[4*`COL_ADDR_WIDTH-1:0]	   col_addr_r;
    reg	[4*`BA_ADDR_WIDTH-1:0]	   ba_addr_r;
    reg	[511:0]                    wrdata_r;
    reg	[4*`CMD_TYPE_WIDTH-1:0]	   cmd_type_r;
    reg [4*`PC_WIDTH-1:0]	       BA4_r;
    reg [4*`HBM_CH_WIDTH-1:0]  channel_id_r; 
    
    integer i;
    
    /*
    assign row_addr_4 = row_addr_r;
    assign col_addr_4 = col_addr_r;
    assign ba_addr_4 = ba_addr_r;
    assign wrdata_4 = wrdata_r;
    assign cmd_type_4 = cmd_type_r;
    assign BA4_4 = BA4_r;
    */
    
    assign fifo_data[127:64]    =  {    channel_id_r[0 +: 2*`HBM_CH_WIDTH], 
                                        BA4_r[0 +: 2*`PC_WIDTH],
                                        ba_addr_r[0 +: 2*`BA_ADDR_WIDTH], 
                                        col_addr_r[0 +: 2*`COL_ADDR_WIDTH], 
                                        row_addr_r[0 +: 2*`ROW_ADDR_WIDTH],
                                        cmd_type_r[0 +: 2*`CMD_TYPE_WIDTH]    };
                                    
    assign fifo_data[63:0]      =  {    channel_id_r[2*`HBM_CH_WIDTH +: 2*`HBM_CH_WIDTH], 
                                        BA4_r[2*`PC_WIDTH +: 2*`PC_WIDTH], 
                                        ba_addr_r[2*`BA_ADDR_WIDTH +: 2*`BA_ADDR_WIDTH], 
                                        col_addr_r[2*`COL_ADDR_WIDTH +: 2*`COL_ADDR_WIDTH], 
                                        row_addr_r[2*`ROW_ADDR_WIDTH +: 2*`ROW_ADDR_WIDTH],
                                        cmd_type_r[2*`CMD_TYPE_WIDTH +: 2*`CMD_TYPE_WIDTH]    };
                                    
    assign wrdata[511:0]    = wrdata_r; // here we are writing double the data redundantly. If we can fix it its better.
    assign wrdata[1023:512] = wrdata_r;
    
    
    always @ (*)
    begin
        row_addr_ns = {4*`ROW_ADDR_WIDTH{1'b0}};
        col_addr_ns = {4*`COL_ADDR_WIDTH{1'b0}};
        ba_addr_ns = {4*`BA_ADDR_WIDTH{1'b0}};
        cmd_type_ns = {4*`CMD_TYPE_WIDTH{1'b1}};
        BA4_ns = {4*`PC_WIDTH{1'b0}};
        wrdata_ns = {512{1'b0}};
        channel_id_ns = {4*`HBM_CH_WIDTH{1'b0}};
        
        for(i = 0 ; i < 4 ; i = i + 1) begin
            if (ddr_write[i]) begin
            	if (ddr_ap[i])
            		cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `WRA;
            	else
            		cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `WR;
            end 
            else if (ddr_read[i]) begin
            	if (ddr_ap[i])
            		cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RDA;
            	else
            		cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RD;
            end
            else if (ddr_pre[i]) begin
            	if (ddr_pall[i])
            		cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `PREA;
            	else
            		cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `PREE;
            end
            else if (ddr_act[i]) begin
            	cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `ACTT;
            end
            else if (ddr_ref[i]) begin
            	cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `REFF;
            end
            else if (ddr_nop[i]) begin
            	cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RNOP;
            end
            else if (hbm_sel_ch[i]) begin // this is now a command to select channel
            	cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RNOP;
            	// duplicate channel ID four times. This would make it easier to appropriately select channel.
            	// This is fine since we initially assume we would need to wait for some delay before switching between channels 
            	channel_id_ns[0 +: `HBM_CH_WIDTH]                   = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH];
            	channel_id_ns[`HBM_CH_WIDTH +: `HBM_CH_WIDTH]   = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH];
            	channel_id_ns[`HBM_CH_WIDTH*2 +: `HBM_CH_WIDTH] = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH];
            	channel_id_ns[`HBM_CH_WIDTH*3 +: `HBM_CH_WIDTH] = hbm_ch[`HBM_CH_WIDTH*i +: `HBM_CH_WIDTH];
            end
            else begin
            	cmd_type_ns[`CMD_TYPE_WIDTH*i +: `CMD_TYPE_WIDTH] = `RNOP;
            end
            
            row_addr_ns[`ROW_ADDR_WIDTH*i +: `ROW_ADDR_WIDTH] = ddr_row[`ROW_WIDTH*i +: `ROW_ADDR_WIDTH];
            col_addr_ns[`COL_ADDR_WIDTH*i +: `COL_ADDR_WIDTH] = ddr_col[`COL_WIDTH*i +: `COL_ADDR_WIDTH];
            ba_addr_ns[`BA_ADDR_WIDTH*i + `BANK_WIDTH +: `BG_WIDTH] = ddr_bg[`BG_WIDTH*i +: `BG_WIDTH];
            ba_addr_ns[`BA_ADDR_WIDTH*i +: `BANK_WIDTH] = ddr_bank[`BANK_WIDTH*i +: `BANK_WIDTH];
            BA4_ns[i] = ddr_rank[i]; // In our case it represents the PC
        end
        
        wrdata_ns = ddr_wdata;

    end
    
    always @(posedge clk) begin // slow clock
		if(rst) begin
		  row_addr_r <= {4*`ROW_ADDR_WIDTH{1'b0}};
		  col_addr_r <= {4*`COL_ADDR_WIDTH{1'b0}};
		  ba_addr_r <= {4*`BA_ADDR_WIDTH{1'b0}};
		  BA4_r <= {4*`PC_WIDTH{1'b0}};
		  wrdata_r <= {512{1'b0}};
		  cmd_type_r <= {4*`CMD_TYPE_WIDTH{1'b1}};
		  channel_id_r <= {4*`HBM_CH_WIDTH{1'b0}};
		end
		else begin
          row_addr_r <= row_addr_ns;
          col_addr_r <= col_addr_ns;
          ba_addr_r  <= ba_addr_ns;
          BA4_r      <= BA4_ns;
          wrdata_r   <= wrdata_ns;
          cmd_type_r <= cmd_type_ns;
          
          if (hbm_sel_ch[3] | hbm_sel_ch[2] | hbm_sel_ch[1] | hbm_sel_ch[0]) begin // only update channel_id when we have a select channel command. It always needs to be the first command out of the 4.
            channel_id_r <= channel_id_ns;
          end else begin    // otherwise, keep value from last select channel command
            channel_id_r <= channel_id_r;
          end
		end
  	end
    
endmodule