FPGA Intellectual Property
PCI Express*, Networking and Connectivity, Memory Interfaces, DSP IP, and Video IP
6486 Discussions

M9K BRAM on Max 10: Data at wrong addresses?

DWolf
Beginner
1,533 Views

I'm using an M9K BRAM in single clock, dual port mode. It is controlled by two state machines, one writing data, and one reading data. I'm looking at all of the signals in signal tap, and everything looks like it's operating correctly. However, assuming I'm writing data words {D1, D2, D3, D4} to adresses {A1, A2, A3, A4}, during readout I get {D1, D1, D2, D3} for addresses {A1, A2, A3, A4}. My first thought was that there was a setup time violation between data and the write clock, or address and the read clock. I can't find any documentation on timing requirements for the BRAM, but just to experiment, I increased the latency between data and write and address and read to 3 clock cycles. I still get the same error. Anyone know what's going on?

0 Kudos
2 Replies
AndyN
New Contributor I
667 Views

Can you share the code for your write state machine? The most likely situation is that the address and data are out of step with each other...

DWolf
Beginner
667 Views

 

So a little background. The purpose of the write module is to grab bits serially and put them into a shift register, and then write the contents of that shift register to the BRAM. This process is repeated for a programmable number of bits, depending on the amount of data in the shift register for a given cycle. New data is available to write as soon as the WAIT_READOUT state is done. You can see where I put some extra states between WAIT_READOUT and WRITE_MEM in to increase the latency between the address changing and the rising edge of the BRAM clock. This does not seem to have any effect. There is also at least one clock cycle of latency between the rising edge of the BRAM clock and the address being incremented.

 

Edit:

 

Forgot to include the block which assigns the address bits and the shift register. Those probably make the code more understandable. Also, the relevant signals for the BRAM are:

 

logic [20:0] timestampData - this is a module port which gets padded out to 32 bits and connected to the BRAM data input.

 

logic [3:0] timestampAddr - this is a module port, and gets directly connected to the BRAM write address.

 

logic timestampWriteClk - this is a module port, and gets ORed with the read clock before being connected to BRAM.

/************************************* State Machine *************************************/   // State transition block. always_ff @ (posedge clk or negedge rst_n) begin if (!rst_n) currentState <= IDLE; else currentState <= nextState; end   // Next state block. always_comb begin case (currentState) // Waiting for startReadout. IDLE: begin if ((startReadout == 1'b1) && (numSamplesReg > 0) && (numSamplesReg <= 12)) nextState = RESET_COUNTER; else nextState = IDLE; end // Reset the readout counter. RESET_COUNTER: begin nextState = RESET_SYNC; end // De-assert counter reset. RESET_SYNC: begin if (timestampAddr == '0) nextState = SAMPLE_FIRST; else nextState = READOUT; end // Sample the first bit in the ROIC buffer. SAMPLE_FIRST: begin nextState = SAMPLE_SYNC; end // De-assert the sample first bit signal. //This state avoids errors due to ORing dataClk and the sample first bit signal. SAMPLE_SYNC: begin nextState = READOUT; end // Read out the timestamp bits. READOUT: begin if (readoutTimerDone == 1'b1) nextState = WAIT_READOUT; else nextState = READOUT; end // Wait until the last readout clock cycle has been completed. WAIT_READOUT: begin if (readoutCounterDone == 1'b1) nextState = WRITE_SYNC1; else nextState = WAIT_READOUT; end // Wait one clock cycle to ensure fresh data for the memory write. WRITE_SYNC1: begin nextState = WRITE_SYNC2; end WRITE_SYNC2: begin nextState = WRITE_SYNC3; end WRITE_SYNC3: begin nextState = WRITE_MEM; end // Assert write clock to write the timestamp bits to memory. WRITE_MEM: begin nextState = WRITE_MEM_2; end // De-assert write clock. WRITE_MEM_2: begin nextState = INC_ADDRESS; end // Increment the memory adress for the next write. INC_ADDRESS: begin nextState = CHECK_DONE; end // Check if the readout cycle is complete. CHECK_DONE: begin if (timestampAddr == numSamplesReg) nextState = IDLE; else nextState = RESET_COUNTER; end default: begin nextState = IDLE; end endcase end   // Output logic block. always_comb begin case (currentState) // Waiting to begin readout cycle. IDLE: begin readoutCounterRst = 1'b0; dataRdy = 1'b1; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b1; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // Reset the readout counter to load the correct period value. RESET_COUNTER: begin readoutCounterRst = 1'b1; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // De-assert counter reset. RESET_SYNC: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // Sample the first bit. The ROIC makes the first bit of the timestamps // available before the first read clock cycle. SAMPLE_FIRST: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b1; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // De-assert the sample first bit signal. SAMPLE_SYNC: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // Read out the rest of the timestamp bits. READOUT: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b1; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // Wait for the last readout clock tick. WAIT_READOUT: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // Wait a clock cycle to ensure fresh data for the memory write. WRITE_SYNC1: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end WRITE_SYNC2: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end WRITE_SYNC3: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // Strobe the write clock to write the timestamp to memory. WRITE_MEM: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b1; end // De-assert the write clock. WRITE_MEM_2: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end // Increment the memory address. This corresponds directly to the timestamp number. INC_ADDRESS: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b1; timestampWriteClk = 1'b0; end // Check to see if all of the requested timestamps have been read out of the ROIC. CHECK_DONE: begin readoutCounterRst = 1'b0; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; timestampWriteClk = 1'b0; end default: begin readoutCounterRst = 1'b1; dataRdy = 1'b0; sampleFirstBit = 1'b0; dataClkEnQ = 1'b0; resetAddr = 1'b0; incrementAddr = 1'b0; end endcase end   // Timestamp data shift register. always_ff @ (negedge sampleClk or negedge rst_n) begin if (!rst_n) timestampData <= '0; else begin timestampData <= {dataOut, timestampData[20:1]}; end end   // Timestamp memory address block. always_ff @ (posedge incrementAddr or posedge resetAddr) begin if (resetAddr) timestampAddr <= '0; else timestampAddr <= timestampAddr + 1'b1; end

 

0 Kudos
Reply