Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
10 years ago

Stratix V MLAB Byte Enable Issue

Hi,

I am trying to implement a very small memory as a data buffer attached to an Avalon-MM interface.

As the memory is only 16bytes, and I require that the output be unregistered, I am using MLABs to implement this ram using the following altdpram instantiation:


    altdpram# (
        .byte_size                         (8),
        .indata_aclr                       ("OFF"),
        .indata_reg                        ("INCLOCK"),
        .intended_device_family            ("Stratix V"),
        .lpm_type                          ("altdpram"),
        .outdata_aclr                      ("OFF"),
        .outdata_reg                       ("UNREGISTERED"),
        .ram_block_type                    ("MLAB"),
        .rdaddress_aclr                    ("OFF"),
        .rdaddress_reg                     ("UNREGISTERED"),
        .rdcontrol_aclr                    ("OFF"),
        .rdcontrol_reg                     ("UNREGISTERED"),
        .read_during_write_mode_mixed_ports("DONT_CARE"),
        .width                             (32),
        .widthad                           (2),
        .width_byteena                     (4),
        .wraddress_aclr                    ("OFF"),
        .wraddress_reg                     ("INCLOCK"),
        .wrcontrol_aclr                    ("OFF"),
        .wrcontrol_reg                     ("INCLOCK")
    ) transferMemRead (
        //Write port (from I2C)
        .inclock                           (clock),
        .inclocken                         (busy),
        .wraddress                         (src_address),
        .byteena                           (src_byteen),
        .wren                              (src_write),
        .data                              ({(4){src_data}}), //copy src data to all byte locations
        //Read port (to AvMM)
        .rdaddress                         (csr_address),
        .q                                 (csr_data),
          
        .aclr                              (1'b0)
    );

Basically when the busy flag is high, the MLAB contents will be written with data from an I2C read transfer. This transfer is done byte-wise whereas Nios works with 32bit words. As such I am using byte enables to select which byte is written - the byte enable is generated by a one-hot counter which I can see from signaltap is correctly producing the required sequence (4'd1,4'd2,4'd4,4'd8,4'd1,...).

The byte enable is doing its job in that the incoming data byte is not written to all bytes, however, the contents of the non-enabled bytes is being cleared. To explain, I end up with the following sequence:


 Byte En | Data In | Mem Contents
   0001  |   0x45  |  0xFFFFFF45
   0010  |   0x22  |  0xFFFF22FF  <- Should be 0xFFFF2245
   0100  |   0x55  |  0xFF55FFFF  <- Should be 0xFF552245
   1000  |   0xAA  |  0xAAFFFFFF  <- Should be 0xAA552245

Notice how the contents of the memory gets wiped - non-enabled bytes are getting replaced by 0xFF.

I have a second memory going the other direction (from Nios to the I2C controller) which if I do byte-wise writes from Nios (using IOWR_8DIRECT), the same thing happens. If however from Nios I do a 32bit write (IOWR_32DIRECT), then the word is written correctly.

So is there a reason for this? Is there something that I am missing about the byte enables that are causing this weird behavior?

1 Reply

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Never mind. I gave up with altdpram and simply wrote my own module with an inferred RAM. Works perfectly

    
    /* 
     * I2C CSR Zero-Latency RAM 
     * ------------------------ 
     * 
     * This module provides inferred RAM logic with zero read latency 
     * and working byte enables. For some reason the altdpram instances 
     * are failing to use byte enables correctly. 
     * 
     */ 
      
    module i2c_csr_ram_hw# ( 
        parameter DATA_WIDTH   = 32, 
        parameter BYTEEN_WIDTH = 4, 
        parameter ADDR_WIDTH   = 2 
    ) ( 
        input                         clock, 
             
        input       writeAddress, 
        input       writeByteEn, 
        input       writeData, 
        input                         writeEn, 
         
        input       readAddress, 
        output reg  readData 
    ); 
    localparam MEM_WORDS = 1 << ADDR_WIDTH; 
    localparam BYTE_WIDTH = DATA_WIDTH / BYTEEN_WIDTH; 
    //For simplicity the memory for each byte is instantiated separately. 
    genvar i; 
    generate for (i = 0; i < BYTEEN_WIDTH; i = i + 1) begin : ram_loop 
        //Offset into data word 
        localparam j = BYTE_WIDTH * i; 
        //Our memory for this byte 
        reg  memory ; 
        //Write port 
        always @ (posedge clock) begin 
            if (writeByteEn && writeEn) begin 
                //When this byte is enabled and we are performing a write 
                memory <= writeData; //Store this byte from the writeData word. 
            end 
        end 
         
        //Read port with zero latency 
        always @ * begin 
            readData <= memory; 
        end 
    end endgenerate 
         
    endmodule 
    

    Just one more Altera IP core to add to my growing list of IP never to use again...