Ok, this one for me is unusual as I am accustomed to 'always_comb' being purely for combinational statements where 2 '=' events after one another should just mess everything up. Yest this simulates fine in ModelSim.
This is a code which selects which read port/channel, 0 through 15, has the next priority selection to my DDR3 memory controler:
always_comb begin
// **************************************************************************************************************************
// Combinational Read Request Priority Selection Logic: (1 of 2)
// Establish all the metrics of all the source read channel command input FIFOs.
// **************************************************************************************************************************
for (int i=0 ; i<PORT_R_TOTAL ; i ++ ) begin
// Make a flag which identifies if the next FIFO queued read command address is within the current read cache's address.
// Used to bypass any unnecessary read requests to the DDR3.
RC_cache_hit[i] = ( RC_raddr[i][PORT_ADDR_SIZE-1:CACHE_ADDR_WIDTH] == FIFO_raddr[i][PORT_ADDR_SIZE-1:CACHE_ADDR_WIDTH] ) && RC_ready[i] ;
// Make a flag which identifies if the next FIFO queued read command address is within the same DDR3 row as the previous DDR3's accessed address.
// Used to raise the priority of the current read channel so that sequential reads bursts within the same row address get coalesced together.
RC_page_hit[i] = ( SEQ_ADDR[PORT_ADDR_SIZE-1:CACHE_ROW_BASE] == FIFO_raddr[i][PORT_ADDR_SIZE-1:CACHE_ROW_BASE] );
// Coalesces all the required DDR3 read requests into a single logic word.
RC_ddr3_read_req[i] = (read_req[i] && !RC_cache_hit[i] && !RC_ddr3_busy[i]) ;
// Advance the read FIFO. No advance should be allowed if any other read channels are busy waiting for the DDR3 to read.
read_req_ack[i] = (read_req[i] && RC_cache_hit[i]) || (SEQ_RDATA_RDY_t != last_rdata_rdy && SEQ_RDATA_VECT_IN[3:0] == 4'(i)) ;
// Output a read ready pulse on the CMD/RC_read_ready[] port when DDR3_PHY read is done or a read cache hit occurs.
RC_read_ready[i] = RC_cache_ack[i] || RC_ddr3_ack[i] ;
end // for i
// **************************************************************************************************************************
// Combinational Read Request Priority Selection Logic: (2 of 2)
// Select which read_ack active channel goes next based on the previous DDR3 read access.
// **************************************************************************************************************************
any_read_req = (RC_ddr3_read_req != 0) ; // Go high if there are any DDR3 reads required.
read_req_sel = 4'd0 ;
RC_pri_weight = 4'd0 ;
RC_pri_pos = last_read_req_chan ; // Begin the priority scan with the last DDR3 read request channel as the lowest priority by order.
for (int i=0 ; i<PORT_R_TOTAL ; i ++ ) begin // Cycle around the available read ports.
// Use the RC_pri_pos as the bottom priority.
// Compare the weight of the read request channel against the channel's set parameter priority's weight of 0-7, with a +8 boost if the
// read request is in the same row as the previous DDR3 access. The +8 boost coalesces same port page bursts until the burst_limit has been reached.
if ( RC_ddr3_read_req[RC_pri_pos] && ( (PORT_R_PRIORITY[RC_pri_pos] || ((RC_page_hit[RC_pri_pos] && !RC_burst_limit[8])<<3)) >= RC_pri_weight ) ) begin
read_req_sel = RC_pri_pos ; // Update the selection of the potential next DDR3 access read channel until the 'for i loop' is finished.
// Set the current priority weight including a +8 boost for a write request within the same row unless the burst limit counter reaches -1, IE 256.
RC_pri_weight = (PORT_R_PRIORITY[RC_pri_pos] || ((RC_page_hit[RC_pri_pos] && !RC_burst_limit[8])<<3)); // Set the current priority weight including a +8 boost for a read request within the same row.
end
RC_pri_pos = (RC_pri_pos==0) ? (PORT_R_TOTAL-1) : (RC_pri_pos - 1) ; // Proceed to the next position, wrap around to the top once position 0 has been reached.
end // for i
end // always_comb
Part 1 of 2 is obviously perfectly fine.
Part 2 of 2 is where I'm in new ground. I used to do this in synchronous logic to select between 2 to 4 ports with equal weight, however, for a variable number of ports with variable weights, combined with all the features stacked, it seemed it was time to try this combinational method.
At the end of it all, logic 'read_req_sel' should contain a number, 0 through 15, of which port should have access to the next DDR3 read.
Am I doing anything illegal here?
Can I expect this code to not work properly in the field?
Is this logic sound?
Has anyone coded like this before?