I made this LUT selection generator which works great and instantly on Modelsim, but in Quartus, it just takes forever to compile instead of the usual ~30 seconds.
Note that there are up to 16 read and 16 write channels with a priority strength parameter assigned to each between 0 and 7.
In Quartus, this code took ~hour during the analysis and synthesis stage:
// Selection table size = req(16),last_accessed channel(4) = 20 bits...
localparam LUT_BASE_last = 0 ; // Defines the LSB position for the 4 bit 'last_accessed'.
localparam LUT_BASE_req = 4 ; // Defines the LSB position for the 16 bit 'req'.
localparam LUT_SIZE = 1<<(20-1) ; // Set the size of the lookup table.
logic [3:0] LUT_RC_SEL [0:LUT_SIZE-1] ; // A lookup table which returns which read channel should be requested next.
logic [2:0] LUT_RC_PRI [0:LUT_SIZE-1] ; // A lookup table which the priority of the requested read channel.
logic [3:0] LUT_WC_SEL [0:LUT_SIZE-1] ; // A lookup table which returns which write channel should be requested next.
logic [2:0] LUT_WC_PRI [0:LUT_SIZE-1] ; // A lookup table which the priority of the requested write channel.
// **************************************************************************************************************************
// Initialize a set of lookup tables which derives which read/write port is next granted access to the DDR3_PHY_SEQ.
// **************************************************************************************************************************
initial begin
// Clear tables.
for (int z=0 ; z<LUT_SIZE ; z++) begin
LUT_RC_SEL[z] = 0 ;
LUT_RC_PRI[z] = 0 ;
LUT_WC_SEL[z] = 0 ;
LUT_WC_PRI[z] = 0 ;
end // for z
// **************************************************
// Construct port selection priority decoding table.
// **************************************************
for (int z=0 ; z<LUT_SIZE ; z++) begin
// *******************************
// Construct read selection lookup table.
// *******************************
loop_break = 0;
for (int p=7 ; p>=0 ; p-- ) begin // 'p' scans in order of highest priority to lowest.
for (int i=0 ; i<16 ; i++ ) begin // 'i' scans channels 0-15.
// round robin arbiter priority selection, ensure the previous accessed R/W channel of equal priority is considered last before
// access is granted once again.
RC_cs = 4'(4'(i) + z[LUT_BASE_last+3:LUT_BASE_last] + 1) ; // RC_cs begins the scan at 'i' + the last read channel + 1
if ( RC_cs<PORT_R_TOTAL && !loop_break ) begin // Only scan from the available read ports.
if ( z[LUT_BASE_req+RC_cs] && ( PORT_R_PRIORITY[RC_cs] == p[2:0] ) ) begin
LUT_RC_SEL[z] = RC_cs; // If there is a DDR3 read_req hit with port priority 'p', set that read channel
LUT_RC_PRI[z] = PORT_R_PRIORITY[RC_cs]; // If there is a DDR3 read_req hit with port priority 'p', set that read channel
loop_break = 1 ; // and ignore the rest of the scan loop.
end
end
end // for i
end // for p
// *******************************
// Construct write selection lookup table.
// *******************************
loop_break = 0;
for (int p=7 ; p>=0 ; p-- ) begin // 'p' scans in order of highest priority to lowest.
for (int i=0 ; i<16 ; i++ ) begin // 'i' scans channels 0-15.
// round robin arbiter priority selection, ensure the previous accessed R/W channel of equal priority is considered last before
// access is granted once again.
WC_cs = 4'(4'(i) + z[LUT_BASE_last+3:LUT_BASE_last] + 1) ; // WC_cs begins the scan at 'i' + the last write channel + 1
if ( WC_cs<PORT_W_TOTAL && !loop_break ) begin // Only scan from the available write ports.
if ( z[LUT_BASE_req+WC_cs] && ( PORT_W_PRIORITY[WC_cs] == p[2:0] ) ) begin
LUT_WC_SEL[z] = WC_cs; // If there is a DDR3 write_req hit with port priority 'p', set that write channel
LUT_WC_PRI[z] = PORT_W_PRIORITY[WC_cs]; // If there is a DDR3 read_req hit with port priority 'p', set that read channel
loop_break = 1 ; // and ignore the rest of the scan loop.
end
end
end // for i
end // for p
end // for z
end // Initial
// Lateron...
read_req_sel = LUT_RC_SEL[{RC_ddr3_read_req, last_read_req_chan }];
write_req_sel = LUT_WC_SEL[{WC_ddr3_write_req, last_write_req_chan}];
Is there anything I can do?
My guess is that Quartus is attempting to simplify down the table to the least number of logic gates.
My attempt to drive the 'read_req_sel' and 'write_req_sel' directly within an 'always_comb', feeding 'R/WC_ddr3_read/write_req' and 'last_read/write_req_chan' directly in place of the 'z' address appears to occasionally skip the right bus selection, but, works in Quartus.
Having ports with assigned priority parameters is too powerful a feature to give up for a dumb round robin arbiter which just cycles through all the requested ports. Coding such a scheme becomes simple-stupid, but I want the ability for some ports to have an optional overriding priority group to prevent the lesser request ports from being granted.