mirror of https://github.com/YosysHQ/yosys.git
110 lines
4.1 KiB
Verilog
110 lines
4.1 KiB
Verilog
// 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, CLK1, input [4:0] B1ADDR, output B1DATA);
|
|
|
|
reg [31:0] mem = 32'b0;
|
|
|
|
// 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
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
specify
|
|
$setup(A1ADDR, posedge CLK1, 0);
|
|
$setup(A1DATA, posedge CLK1, 0);
|
|
|
|
if (B1EN) (posedge CLK1 => (B1DATA : A1DATA)) = 0;
|
|
endspecify
|
|
|
|
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
|