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
|
// The MLAB
// --------
// In addition to Logic Array Blocks (LABs) that contain ten Adaptive Logic
// Modules (ALMs, see alm_sim.v), the Cyclone V/10GX also contain
// Memory/Logic Array Blocks (MLABs) that can act as either ten ALMs, or utilise
// the memory the ALM uses to store the look-up table data for general usage,
// producing a 32 address by 20-bit block of memory. MLABs are spread out
// around the chip, so they can be placed near where they are needed, rather than
// being comparatively limited in placement for a deep but narrow memory such as
// the M10K memory block.
//
// MLABs are used mainly for shallow but wide memories, such as CPU register
// files (which have perhaps 32 registers that are comparatively wide (16/32-bit))
// or shift registers (by using the output of the Nth bit as input for the N+1th
// bit).
//
// Oddly, instead of providing a block 32 address by 20-bit cell, Quartus asks
// synthesis tools to build MLABs out of 32 address by 1-bit cells, and tries
// to put these cells in the same MLAB during cell placement. Because of this
// a MISTRAL_MLAB cell represents one of these 32 address by 1-bit cells, and
// 20 of them represent a physical MLAB.
//
// How the MLAB works
// ------------------
// MLABs are poorly documented, so the following information is based mainly
// on the simulation model and my knowledge of how memories like these work.
// Additionally, note that the ports of MISTRAL_MLAB are the ones auto-generated
// by the Yosys `memory_bram` pass, and it doesn't make sense to me to use
// `techmap` just for the sake of renaming the cell ports.
//
// The MLAB can be initialised to any value, but unfortunately Quartus only
// allows memory initialisation from a file. Since Yosys doesn't preserve input
// file information, or write the contents of an `initial` block to a file,
// Yosys can't currently initialise the MLAB in a way Quartus will accept.
//
// The MLAB takes in data from A1DATA at the rising edge of CLK1, and if A1EN
// is high, writes it to the address in A1ADDR. A1EN can therefore be used to
// conditionally write data to the MLAB.
//
// Simultaneously, the MLAB reads data from B1ADDR, and outputs it to B1DATA,
// asynchronous to CLK1 and ignoring A1EN. If a synchronous read is needed
// then the output can be fed to embedded flops. Presently, Yosys assumes
// Quartus will pack external flops into the MLAB, but this is an assumption
// that needs testing.
// The vendor sim model outputs 'x for a very short period (a few
// combinational delta cycles) after each write. This has been omitted from
// the following model because it's very difficult to trigger this in practice
// as clock cycles will be much longer than any potential blip of 'x, so the
// model can be treated as always returning a defined result.
(* abc9_box, lib_whitebox *)
module MISTRAL_MLAB(input [4:0] A1ADDR, input A1DATA, A1EN,
(* clkbuf_sink *) input CLK1,
input [4:0] B1ADDR, output B1DATA);
reg [31:0] mem = 32'b0;
`ifdef cyclonev
specify
$setup(A1ADDR, posedge CLK1, 86);
$setup(A1DATA, posedge CLK1, 86);
$setup(A1EN, posedge CLK1, 86);
(B1ADDR[0] => B1DATA) = 487;
(B1ADDR[1] => B1DATA) = 475;
(B1ADDR[2] => B1DATA) = 382;
(B1ADDR[3] => B1DATA) = 284;
(B1ADDR[4] => B1DATA) = 96;
endspecify
`endif
`ifdef arriav
specify
$setup(A1ADDR, posedge CLK1, 62);
$setup(A1DATA, posedge CLK1, 62);
$setup(A1EN, posedge CLK1, 62);
(B1ADDR[0] => B1DATA) = 370;
(B1ADDR[1] => B1DATA) = 292;
(B1ADDR[2] => B1DATA) = 218;
(B1ADDR[3] => B1DATA) = 74;
(B1ADDR[4] => B1DATA) = 177;
endspecify
`endif
`ifdef cyclone10gx
// TODO: Cyclone 10 GX timings; the below timings are for Cyclone V
specify
$setup(A1ADDR, posedge CLK1, 86);
$setup(A1DATA, posedge CLK1, 86);
$setup(A1EN, posedge CLK1, 86);
(B1ADDR[0] => B1DATA) = 487;
(B1ADDR[1] => B1DATA) = 475;
(B1ADDR[2] => B1DATA) = 382;
(B1ADDR[3] => B1DATA) = 284;
(B1ADDR[4] => B1DATA) = 96;
endspecify
`endif
always @(posedge CLK1)
if (A1EN) mem[A1ADDR] <= A1DATA;
assign B1DATA = mem[B1ADDR];
endmodule
// The M10K
// --------
// TODO
module MISTRAL_M10K(CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
parameter CFG_ABITS = 10;
parameter CFG_DBITS = 10;
(* clkbuf_sink *) input CLK1;
input [CFG_ABITS-1:0] A1ADDR, B1ADDR;
input [CFG_DBITS-1:0] A1DATA;
input A1EN, B1EN;
output reg [CFG_DBITS-1:0] B1DATA;
reg [2**CFG_ABITS * CFG_DBITS - 1 : 0] mem = 0;
`ifdef cyclonev
specify
$setup(A1ADDR, posedge CLK1, 125);
$setup(A1DATA, posedge CLK1, 97);
$setup(A1EN, posedge CLK1, 140);
$setup(B1ADDR, posedge CLK1, 125);
$setup(B1EN, posedge CLK1, 161);
if (B1EN) (posedge CLK1 => (B1DATA : A1DATA)) = 1004;
endspecify
`endif
`ifdef arriav
specify
$setup(A1ADDR, posedge CLK1, 97);
$setup(A1DATA, posedge CLK1, 74);
$setup(A1EN, posedge CLK1, 109);
$setup(B1ADDR, posedge CLK1, 97);
$setup(B1EN, posedge CLK1, 126);
if (B1EN) (posedge CLK1 => (B1DATA : A1DATA)) = 787;
endspecify
`endif
always @(posedge CLK1) begin
if (!A1EN)
mem[(A1ADDR + 1) * CFG_DBITS - 1 : A1ADDR * CFG_DBITS] <= A1DATA;
if (B1EN)
B1DATA <= mem[(B1ADDR + 1) * CFG_DBITS - 1 : B1ADDR * CFG_DBITS];
end
endmodule
|