0
votes

I have an application where I'm continuously writing to a block ram at a slow clock speed (clk_a) and within this slow clock cycle need to read three indexes from the block ram at a fast clock speed (clk_b) to use these values as operands in a math module, the result being written back to the block ram on the next slow clock. These three indexes are the current address written to at posedge of the slow clock, plus the two immediate neighbouring addresses (addr_a -1 and addr_a +1).

What is an efficient way to synthesize this? My best attempt to date uses a small counter (triplet) running at fast clock rate that increments the addresses but I end up running out of logic as it looks like Yosys does not infer the ram properly. What is a good strategy for this?

here is what I have:

module myRam2 (
 input clk_a,
 input clk_b,
 input we_a,
 input re_a,
 input [10:0] addr_a,
 input [10:0] addr_b,
 input [11:0] din_a,
 output [11:0] leftNeighbor,
 output [11:0] currentX,
 output [11:0] rightNeighbor
);
  parameter MEM_INIT_FILE2 = "";
 initial
    if (MEM_INIT_FILE2 != "")
      $readmemh(MEM_INIT_FILE2, ram2);
     
reg [11:0] ram2 [0:2047];
reg [1:0] triplet = 3;
reg [10:0] old_addr_a;
reg [11:0] temp;

always @(posedge clk_a) begin
    ram2[addr_a] <= din_a;
end

always@(posedge clk_b) 
if (old_addr_a != addr_a) begin
        triplet <= 0;
        old_addr_a <= addr_a;
        end
    else 
        if(triplet < 3) begin
            triplet <= triplet +1;
        end



  
  always @(posedge clk_b) begin
        temp <= ram2[addr_a + (triplet - 1)];
end

always @(posedge clk_b) begin
case(triplet)
0: leftN <= temp;
1: X <= temp;
2: rightN <= temp;
endcase
end



reg signed [11:0] leftN;
reg signed [11:0] X;
reg signed [11:0] rightN;


assign leftNeighbor = leftN;
assign currentX = X;
assign rightNeighbor = rightN;

endmodule
1
I tested this code and it infers memory fine using the Yosys I have here, and I don't see any obvious inference-related issues either (haven't checked that the logic is actually OK, though).gatecat
The logic utilisation I see is ``` SB_CARRY 11 SB_DFFE 49 SB_LUT4 32 SB_RAM40_4K 6 ``` which looks very reasonablegatecat
Thanks David. I'm not sure how to interpret ` SB_CARRY 11 SB_DFFE 49 SB_LUT4 32 SB_RAM40_4K 6 ', and why it looks reasonable. Can you explain further? I guess I really am running out of logic with this design. I have two other similar ram modules for different clock domains: would there be a way of doing all these reads at different rates with the same module? Would this be more economical logic-wise?ke10g
For example, I can imagine scheduling all the reads for the different clock rates at the fast clock rate and distributing them to registers that then get read off at those slower rates... but as far as I can tell that might save some memory but not logic.ke10g
6 SB_RAM40_4Ks is the expected number of RAMs. 32 LUT4s and 49 flipflops is a fairly small amount of logic given the various storage elements and control here.gatecat

1 Answers

0
votes

Regarding the efficiency the following approach should work and removes the need for a faster clock:

module myRam2 (
 input wire clk,
 input wire we,
 input wire re,
 input wire [10:0] addr_a,
 input wire [10:0] addr_b,
 input wire [11:0] din_a,
 output reg [11:0] leftNeighbor,
 output reg [11:0] currentX,
 output reg [11:0] rightNeighbor
);

reg [11:0] ram2 [2047:0];/* synthesis syn_ramstyle = "no_rw_check" */;

always @(posedge clk) begin
    if(we)  ram2[addr_a]                            <= din_a;
    if(re)  {leftNeighbor,currentX,rightNeighbor}   <= {ram2[addr_b-1],ram2[addr_b],ram2[addr_b+1]};
end

endmodule

The synthesis keyword helped me in the past to increase the likelyhood of correctly inferred ram.

EDIT: removed second example suggesting a 1D mapping. It turned out that at least Lattice LSE cannot deal with that approach. However the first code snipped should work according to Active-HDL and Lattice LSE.