mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			111 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			Verilog
		
	
	
	
	
	
			
		
		
	
	
			111 lines
		
	
	
	
		
			3.6 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 also contains
 | |
| // 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).
 | |
| //
 | |
| // For historical reasons a MISTRAL_MLAB cell represents a 32 address by 1-bit cell,
 | |
| // 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.
 | |
| //
 | |
| // 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.
 | |
| 
 | |
| // 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
 | |
| 
 | |
| 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 INIT = 0;
 | |
| 
 | |
| 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 = INIT;
 | |
| 
 | |
| `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
 | |
| 
 | |
| 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
 |