OpenCores
URL https://opencores.org/ocsvn/memory_sizer/memory_sizer/trunk

Subversion Repositories memory_sizer

[/] [memory_sizer/] [web_uploads/] [memory_sizer_dual_path.v] - Blame information for rev 6

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 6 root
//----------------------------------------------------------------------------
2
// Wishbone memory_sizer_dual_path core
3
//
4
// This file is part of the "memory_sizer" project.
5
// http://www.opencores.org/cores/memory_sizer
6
// 
7
//
8
// Description: See description below (which suffices for IP core
9
//                                     specification document.)
10
//
11
// Copyright (C) 2001 John Clayton and OPENCORES.ORG
12
//
13
// This source file may be used and distributed without restriction provided
14
// that this copyright statement is not removed from the file and that any
15
// derivative work contains the original copyright notice and the associated
16
// disclaimer.
17
//
18
// This source file is free software; you can redistribute it and/or modify
19
// it under the terms of the GNU Lesser General Public License as published
20
// by the Free Software Foundation;  either version 2.1 of the License, or
21
// (at your option) any later version.
22
//
23
// This source is distributed in the hope that it will be useful, but WITHOUT
24
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
25
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
26
// License for more details.
27
//
28
// You should have received a copy of the GNU Lesser General Public License
29
// along with this source.
30
// If not, download it from http://www.opencores.org/lgpl.shtml
31
//
32
//----------------------------------------------------------------------------
33
//
34
// Author: John Clayton
35
// Date  : November  5, 2001
36
// Update: 11/05/01 copied this file from rs232_syscon.v (pared down).
37
// Update: 11/16/01 Continued coding efforts.  Redesigned logic with scalable
38
//                  "byte sized barrel shifter" and byte reversal blocks (byte
39
//                  reversal is implemented as a function "byte_reversal").
40
//                  Changed encoding of memory_width_i and access_width_i.
41
//                  Implemented new counting and byte enable logic.
42
// Update: 12/04/01 Realized there was a mistake in the byte enable logic.
43
//                  Fixed it by using dat_shift to shift the byte enables.
44
//                  Made "byte_enable_source" twice as wide.
45
// Update: 12/05/01 Eliminated the "count" in favor of using "dat_shift" along
46
//                  with new terminal_count logic, in order to fix flaws found
47
//                  in the terminal_count signal.  Fixed byte steering for
48
//                  stores.  Tested using N_PP = 4, and LOG2_N_PP = 2 and saw
49
//                  correct operation for all sizes of store operations.
50
// Update: 12/13/01 Began testing with read logic.  Found byte enable problem
51
//                  during writes.  Removed "byte_dirty" bits.
52
// Update: 12/14/01 Added "latch_be_source" to create byte enables for reading
53
//                  which are based on the size of the memory (which fixed a
54
//                  bug in reading.)  The module appears to be fully working,
55
//                  except for "big_endian" reads.
56
// Update: 12/17/01 Introduced the "middle_bus" in order to decouple the
57
//                  byte reverser from the byte steering logic, so that for
58
//                  writes byte reversing is done first, but for reads then
59
//                  byte reversing is done last.  Introduced "latch_be_adjust"
60
//                  to cover big endian reads -- all is now working.
61
// Update: 12/17/01 Removed "middle_bus" (the two units are still decoupled!)
62
//                  because it seemed unnecessary.  This freed up Tbuffs, but
63
//                  had no effect on resource utilization (slices).  Also, the
64
//                  maximum reported clock speed increased.  Removed debug
65
//                  port.
66
// Update: 12/18/01 Copied this from "memory_sizer_tristate_switching.v"
67
//                  This file will now have duplicate byte steering and byte
68
//                  swapping logic.  Changed module name to 
69
//                  "memory_sizer_dual_path"
70
//
71
// Description
72
//-------------------------------------------------------------------------------------
73
//
74
// This module is just like "memory_sizer" except that it provides separate
75
// paths for the writing and the reading of memory.  By avoiding the tri-state
76
// bus switching, it uses less tri-state buffers, and should operate faster
77
// than the original "memory_sizer"
78
//
79
// ORIGINAL DESCRIPTION:
80
// This logic module takes care of sizing bus transfers between a small
81
// microprocessor and its memory.  It enables the microprocessor to
82
// generate access requests for different widths (read/write BYTE, WORD and
83
// DWORD, etc.) using memory which is sized independently of the accesses.
84
//
85
// Thus, a 32-bit microprocessor using 32-bit wide accesses can use this block
86
// in order to boot from an 8-bit wide flash device.  This block takes care of 
87
// generating the four 8-bit memory cycles that are required in order to read
88
// each DWORD for alimentation of the microprocessor.
89
//
90
// Also, if the memory supports byte enables during a write cycle, then this
91
// block "steers" a smaller data word to the appropriate location within a
92
// larger memory word, and activates the appropriate byte enables so that only
93
// the BYTEs which are affected by the write cycle are actually overwritten.
94
//
95
// Moreover, the memory_sizer block takes care of translating little-endian
96
// formats into big-endian formats and vice-versa.  This is accomplished by the
97
// use of a single input bit "endianness_i"
98
//
99
// The memory_sizer block does not latch or store the parameters which it uses
100
// for operation.  The input signals determine its operation on an ongoing
101
// basis.  In fact, the only data storage present in this block is the latching
102
// provided for data which must be held during multiple cycle read operations.
103
// (There are also some counters, which don't count as data storage...)
104
//
105
// Encoding for access_width_i and memory_width_i is as follows:
106
//
107
// Bits           Significance
108
// ------         ------------
109
// 0001            8-bits wide (1 byte)
110
// 0010           16-bits wide (2 bytes)
111
// 0100           32-bits wide (4 bytes)
112
// 1000           64-bits wide (8 bytes)
113
//
114
// (The access_width_i and memory_width_i inputs are sized according to the
115
// parameter LOG2_N_PP, but the significance is the same, using whatever
116
// lsbs are present.)
117
//
118
// It is envisioned that a designer may include this block for flexibility.
119
// If all of the memory accesses are of a single width, and the memory matches
120
// that width, and there is no need for endianness translation, then the user
121
// could hard-code the "memory_width_i" and "access_width_i" to correspond
122
// to the same width, hard-code the "endianness_i" input to the desired value
123
// and then the memory_sizer block would effectively do nothing, or very little.
124
// Most of its size and resources would be optimized out of the design at
125
// compile time.  The dat_shift counter and read-storage latches would not be
126
// used, and so they would not even be synthesized.
127
//
128
// On the other hand, if the memory in the SOC (system on a chip) comprises
129
// various width devices, then the decode logic which selects the blocks of
130
// memory is ORed (for each like-sized block) and then concatenated in the
131
// proper order to generate a dynamic "mem_width_i" signal to the
132
// memory_sizer, so that the different size accesses are accomodated.  The
133
// processor side, meanwhile (being "access_width_i"), could still be
134
// hard-wired to a given width, or be connected so that different width loads
135
// and stores are generated as needed.
136
//
137
// This block may generate exceptions to the processor, in the case of a write
138
// request, for example, to store a BYTE into a DWORD wide memory which doesn't
139
// support the use of byte enables.  Although this could be done by reading the
140
// wider memory and masking in the correct BYTE, followed by storing the
141
// results back into the memory, this was deemed too complex a task for this
142
// block.  Responsibility for such operations, if desired, would devolve upon
143
// the microprocessor itself.  Support of byte enables is indicated by a "1" on
144
// the "memory_has_be_i" line.
145
//
146
// The clock used by memory_sizer is not limited to the speed of the clock used
147
// by the microprocessor.  Since the memory_sizer contains only combinational
148
// logic, simple counters and some possible latches, it might run much faster
149
// than the microprocessor.  In that case, generate two clocks which are
150
// synchronous:  one for the processor, and another for memory_sizer.
151
// The memory_sizer clock could be 2x, 4x or even 8x that of the processor.
152
// In this way, the memory_sizer block can complete multiple memory read cycles
153
// in the same time as a single processor cycle -- assuming the memory is fast
154
// enough to support it -- and thereby the memory latency can be reduced.
155
//
156
// The memory_sizer block is not responsible for implementing wait states for
157
// the memory, especially since the number of wait states required can vary
158
// for each type and width of memory used.  Instead, there is an "access_ack_o"
159
// signal to indicate completion of the entire requested memory access to the
160
// processor.  On the memory side, there is "memory_ack_i" used to indicate to
161
// the memory_sizer block that the memory has completed the current cycle in
162
// progress.  Therefore, in order to implement wait states, the memory sytem
163
// address decoder logic should generate the "memory_ack_i" signal based on the
164
// different types of memory present within the system, which can also be
165
// programmable.  A parameterized watchdog timer inside of the memory sizer block
166
// indicates when "memory_ack_i" has not been asserted in a reasonable number
167
// of clock cycles.  When this occurs, an exception is raised.  The timer
168
// is started when "sel_i" is active (high).  sel_i must remain active
169
// until the access is completed, otherwise the timer will reset and the
170
// access is aborted.  If you don't want to use the watchdog portion of this
171
// block then simply don't connect the exception_watchdog_o line, and the watchdog
172
// timer will be optimized out of the logic.
173
//
174
// If desired, registers can be placed on the memory side of the block.  They
175
// are treated just like memory of a given width, although access requests for
176
// misaligned writes, or writes which are smaller than the size of the registers,
177
// should generate exceptions, unless the registers support byte enables.
178
//
179
// Addresses are always assumed to be byte addresses in this unit, since the
180
// smallest granularity of data used in it is the BYTE.  Also, the data bus
181
// size used must be a multiple of 8 bits, for the same reason.
182
//
183
//-------------------------------------------------------------------------------------
184
 
185
 
186
`define BYTE_SIZE  8         // Number of bits in one byte
187
 
188
 
189
module memory_sizer_dual_path (
190
  clk_i,
191
  reset_i,
192
  sel_i,
193
  memory_ack_i,
194
  memory_has_be_i,
195
  memory_width_i,
196
  access_width_i,
197
  access_big_endian_i,
198
  adr_i,
199
  we_i,
200
  dat_io,
201
  memory_dat_io,
202
  memory_adr_o,              // Same width as adr_i (only lsbs are modified)
203
  memory_we_o,
204
  memory_be_o,
205
  access_ack_o,
206
  exception_be_o,
207
  exception_watchdog_o
208
  );
209
 
210
// Parameters
211
 
212
// The timer value can be from [0 to (2^WATCHDOG_TIMER_BITS_PP)-1] inclusive.
213
parameter N_PP                    = 4;   // number of bytes in data bus
214
parameter LOG2_N_PP               = 2;   // log base 2 of data bus size (bytes)
215
parameter ADR_BITS_PP             = 32;  // # of bits in adr buses
216
parameter WATCHDOG_TIMER_VALUE_PP = 12;  // # of sys_clks before ack expected
217
parameter WATCHDOG_TIMER_BITS_PP  = 4;   // # of bits needed for timer
218
 
219
 
220
// I/O declarations
221
input clk_i;           // Memory sub-system clock input
222
input reset_i;         // Reset signal for this module
223
input sel_i;           // Enables watchdog timer, activates memory_sizer
224
input memory_ack_i;    // Ack from memory (delay for wait states)
225
input memory_has_be_i; // Indicates memory at current address has byte enables
226
input [LOG2_N_PP:0] memory_width_i;        // Width code of memory
227
input [LOG2_N_PP:0] access_width_i;        // Width code of access request
228
input access_big_endian_i;                 // 0=little endian, 1=big endian
229
input [ADR_BITS_PP-1:0] adr_i;             // Address bus input
230
input we_i;                                // type of access
231
inout [`BYTE_SIZE*N_PP-1:0] dat_io;        // processor data bus
232
inout [`BYTE_SIZE*N_PP-1:0] memory_dat_io; // data bus to memory
233
output [ADR_BITS_PP-1:0] memory_adr_o;     // address bus to memory
234
output memory_we_o;                        // we to memory
235
output [N_PP-1:0] memory_be_o;             // byte enables to memory
236
output access_ack_o;         // shows that access is completed
237
output exception_be_o;       // exception for write to non-byte-enabled memory
238
output exception_watchdog_o; // exception for memory_ack_i watch dog timeout
239
 
240
// Internal signal declarations
241
wire [2*N_PP-1:0] memory_be_source;     // Unshifted byte enables for writing
242
wire [2*N_PP-1:0] latch_be_source;      // Unshifted byte enables for reading
243
wire [N_PP-1:0] latch_be;               // "latch_be" is like "memory_be_o" 
244
wire [N_PP-1:0] latch_be_lil_endian;    //    but used internally for reads.
245
wire [N_PP-1:0] latch_be_big_endian;
246
wire [LOG2_N_PP-1:0] latch_be_adjust;
247
wire [LOG2_N_PP+1:0] dat_shift_next;    // Next dat_shift value (extra bit
248
                                        //   is for terminal count compare.)
249
wire [LOG2_N_PP-1:0] alignment;         // shows aligment of access
250
wire terminal_count;                    // signifies last store cycle
251
 
252
reg [LOG2_N_PP-1:0] rd_byte_mux_select; // selects which bytes to transfer
253
reg [LOG2_N_PP-1:0] wr_byte_mux_select; // selects which bytes to transfer
254
reg [LOG2_N_PP:0] dat_shift;            // shift amt. for data and byte enables
255
reg [`BYTE_SIZE*N_PP-1:0] wr_steer_dat_o; // data from byte steering logic
256
reg [`BYTE_SIZE*N_PP-1:0] wr_revrs_dat_o; // data from byte reversing logic
257
reg [`BYTE_SIZE*N_PP-1:0] rd_revrs_dat_o; // data from byte reversing logic
258
reg [`BYTE_SIZE*N_PP-1:0] rd_steer_dat_o; // data from byte steering logic
259
reg [`BYTE_SIZE*N_PP-1:0] read_dat;     // read data (after latch bypassing)
260
reg [`BYTE_SIZE*N_PP-1:0] latched_read_dat; // read values before latch bypass
261
 
262
 
263
reg [WATCHDOG_TIMER_BITS_PP-1:0] watchdog_count;
264
 
265
//--------------------------------------------------------------------------
266
// Instantiations
267
//--------------------------------------------------------------------------
268
 
269
 
270
//--------------------------------------------------------------------------
271
// Functions & Tasks
272
//--------------------------------------------------------------------------
273
 
274
function [`BYTE_SIZE*N_PP-1:0] byte_reversal;
275
  input [`BYTE_SIZE*N_PP-1:0] din;
276
  integer k;
277
  begin
278
    for (k=0; k<N_PP; k=k+1)
279
      byte_reversal[`BYTE_SIZE*(N_PP-k)-1:`BYTE_SIZE*(N_PP-k-1)]
280
        <= din[`BYTE_SIZE*(k+1)-1:`BYTE_SIZE*k];
281
  end
282
endfunction
283
 
284
//--------------------------------------------------------------------------
285
// Module code
286
//--------------------------------------------------------------------------
287
 
288
// Mask off the address bits that don't matter for alignment
289
assign alignment = (memory_width_i - 1) & adr_i[LOG2_N_PP-1:0];
290
 
291
// Setting up the basic (alignment shifted) byte enables
292
assign memory_be_source = ((1<<access_width_i)-1);
293
assign latch_be_source = ((1<<memory_width_i)-1);
294
 
295
// Assigning the byte enables and latch enables
296
assign memory_be_o = we_i?((memory_be_source << alignment) >> dat_shift)
297
                          :{N_PP{1'b1}};
298
                          // (memory byte enables are all high for reads!)
299
 
300
// For big_endian reads, the latch byte enables (and indeed the data also)
301
// are shifted using a special mapping, which causes the data to appear at 
302
// the opposite end of the "read_data" bus.
303
assign latch_be_lil_endian = ((latch_be_source << dat_shift) >> alignment);
304
assign latch_be_adjust = ~(access_width_i[LOG2_N_PP-1:0]-1);
305
assign latch_be_big_endian = latch_be_lil_endian << latch_be_adjust;
306
assign latch_be = (access_big_endian_i)?latch_be_big_endian
307
                                       :latch_be_lil_endian;
308
 
309
// Exceptions
310
assign exception_be_o = (alignment != 0) && ~memory_has_be_i;
311
assign exception_watchdog_o = (watchdog_count == WATCHDOG_TIMER_VALUE_PP);
312
 
313
// Pass signals to memory
314
assign memory_we_o = we_i;
315
 
316
 
317
// Enable the data bus outputs in each direction
318
assign dat_io = (sel_i && ~we_i)?rd_revrs_dat_o:{`BYTE_SIZE*N_PP{1'bZ}};
319
assign memory_dat_io = (sel_i && we_i)?wr_steer_dat_o:{`BYTE_SIZE*N_PP{1'bZ}};
320
 
321
 
322
 
323
// THIS LOGIC IS FOR THE WRITING PATH
324
//-------------------------------------
325
// Byte reversal logic
326
always @(
327
         dat_io              or
328
         access_big_endian_i
329
         )
330
begin
331
  // Reverse the bytes of the data bus, if needed
332
  if (access_big_endian_i) wr_revrs_dat_o <= byte_reversal(dat_io);
333
  else wr_revrs_dat_o <= dat_io;
334
end
335
 
336
 
337
// Steering logic
338
always @(
339
         wr_revrs_dat_o      or
340
         dat_shift           or
341
         alignment           or
342
         we_i                or
343
         access_width_i      or
344
         access_big_endian_i
345
         )
346
begin
347
  // If bytes are reversed, an extra "bit inversion mask" is applied
348
  // to reflect a new mapping which is correct for reversed bytes.
349
  if (access_big_endian_i)
350
    wr_byte_mux_select <= (dat_shift[LOG2_N_PP-1:0] ^ ~(access_width_i-1))
351
                           - alignment;
352
  else wr_byte_mux_select <= dat_shift - alignment;
353
 
354
  // Rotate the data bus (byte-sized barrel shifter!)
355
  wr_steer_dat_o <= (
356
                     (wr_revrs_dat_o >> `BYTE_SIZE*wr_byte_mux_select)
357
                    |(wr_revrs_dat_o << `BYTE_SIZE*(N_PP-wr_byte_mux_select))
358
                     );
359
end
360
 
361
 
362
// THIS LOGIC IS FOR THE READING PATH
363
//-------------------------------------
364
 
365
// Steering logic
366
always @(
367
         memory_dat_io       or
368
         dat_shift           or
369
         alignment           or
370
         we_i                or
371
         access_width_i      or
372
         access_big_endian_i
373
         )
374
begin
375
    // For reads, negate the shift amount
376
  if (access_big_endian_i)
377
    rd_byte_mux_select <= alignment
378
                          - (dat_shift[LOG2_N_PP-1:0] ^ ~(access_width_i-1));
379
  else rd_byte_mux_select <= alignment - dat_shift;
380
 
381
  // Rotate the data bus (byte-sized barrel shifter!)
382
  rd_steer_dat_o <= (
383
                     (memory_dat_io >> `BYTE_SIZE*rd_byte_mux_select)
384
                    |(memory_dat_io << `BYTE_SIZE*(N_PP-rd_byte_mux_select))
385
                    );
386
end
387
 
388
 
389
// This logic latches the data bytes which are read during the first cycles
390
// of an access.  During the final cycle of the access, then "terminal_count"
391
// is asserted by the counting logic, which causes the latches which are
392
// "non-dirty" (i.e. which do not yet contain data) to be bypassed by muxes.
393
// This means that for single cycle accesses, the data will flow directly
394
// around the latches and an extra clock cycle will not be needed in order
395
// to latch the data...
396
always @(posedge clk_i)
397
begin: BYTE_LATCHES
398
  integer i;
399
 
400
  if (reset_i || terminal_count || ~sel_i)
401
  begin
402
    latched_read_dat <= 0;
403
  end
404
  else if (sel_i && ~we_i && memory_ack_i)
405
  begin
406
    for (i=0;i<N_PP;i=i+1)
407
    begin
408
      if (latch_be[i]) latched_read_dat[`BYTE_SIZE*(i+1)-1:`BYTE_SIZE*i]
409
                       <= rd_steer_dat_o[`BYTE_SIZE*(i+1)-1:`BYTE_SIZE*i];
410
    end
411
  end
412
end
413
 
414
// This part handles the bypass muxes
415
always @(
416
         terminal_count      or
417
         latch_be            or
418
         rd_steer_dat_o      or
419
         latched_read_dat
420
         )
421
begin: LATCH_BYPASS
422
  integer j;
423
 
424
  for (j=0;j<N_PP;j=j+1)
425
  begin
426
    if (terminal_count && latch_be[j])
427
         read_dat[`BYTE_SIZE*(j+1)-1:`BYTE_SIZE*j]
428
         <= rd_steer_dat_o[`BYTE_SIZE*(j+1)-1:`BYTE_SIZE*j];
429
    else read_dat[`BYTE_SIZE*(j+1)-1:`BYTE_SIZE*j]
430
         <= latched_read_dat[`BYTE_SIZE*(j+1)-1:`BYTE_SIZE*j];
431
  end
432
end
433
 
434
// Byte reversal logic
435
always @(
436
         read_dat            or
437
         access_big_endian_i
438
         )
439
begin
440
  // Reverse the bytes of the data bus, if needed
441
  if (access_big_endian_i) rd_revrs_dat_o <= byte_reversal(read_dat);
442
  else rd_revrs_dat_o <= read_dat;
443
end
444
 
445
 
446
// THIS LOGIC IS GENERAL
447
//--------------------------
448
// This is the counting logic.
449
// It is implemented using "count_next" in order to detect when
450
// the last cycle is being performed, which is when the "next" count
451
// equals or exceeds the total size of the access requested in bytes.
452
// (Using the "next" approach avoids issues relating to different
453
//  memory sizes!)
454
always @(posedge clk_i)
455
begin
456
  if (reset_i || terminal_count || ~sel_i) dat_shift <= 0;
457
  else if (memory_ack_i) dat_shift <= dat_shift_next;
458
end
459
 
460
assign dat_shift_next = dat_shift + memory_width_i;
461
assign terminal_count = (dat_shift_next >= (access_width_i + alignment));
462
assign memory_adr_o = adr_i + dat_shift;
463
assign access_ack_o = terminal_count && sel_i;
464
 
465
// This is the watchdog timer
466
// It runs whenever the memory_sizer is selected for an access, and the
467
// memory has not yet responded with an ack signal.
468
always @(posedge clk_i)
469
begin
470
  if (reset_i || ~sel_i || memory_ack_i) watchdog_count <= 0;
471
  else if (~exception_watchdog_o) watchdog_count <= watchdog_count + 1;
472
end
473
 
474
 
475
endmodule
476
 
477
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.