Rotating priority interupt decoder/selector

B

Brian Guralnick

Guest
I'm looking for an easy method to do the following in verilog:


I need the logic to do the following,

If the priority is 0, select the first active interrupt starting from in 0
to thru 7. next priority = the first active interrupt # + 1.
if the priority is 1, select the first active interrupt starting from in 1
to thru 7, then last 0. next priority = the first active interrupt # + 1.
if the priority is 2, select the first active interrupt starting from in 2
to thru 7, then 0 thru 1. next priority = the first active interrupt # + 1.
if the priority is 3, select the first active interrupt starting from in 3
to thru 7, then 0 thru 2. next priority = the first active interrupt # + 1.

ect...

If there are no interrupts, priority will hold it's value.

Now, I can easily make an if / else if table, but that looks messy. Is
there a better shortcut method to do it in verilog?


_____________
Brian G.
 
How about an 8-wide case? Case statements result in priority order so use
something like
always @(priority and interrupt)
case(1'b1)
interrupt[(priority+3'h0)&3'h7]: next_priority = (priority + 3'h1)&3'h7;
interrupt[(priority+3'h1)&3'h7]: next_priority = (priority + 3'h2)&3'h7;
interrupt[(priority+3'h2)&3'h7]: next_priority = (priority + 3'h3)&3'h7;
interrupt[(priority+3'h3)&3'h7]: next_priority = (priority + 3'h4)&3'h7;
interrupt[(priority+3'h4)&3'h7]: next_priority = (priority + 3'h5)&3'h7;
interrupt[(priority+3'h5)&3'h7]: next_priority = (priority + 3'h6)&3'h7;
interrupt[(priority+3'h6)&3'h7]: next_priority = (priority + 3'h7)&3'h7;
interrupt[(priority+3'h7)&3'h7]: next_priority = (priority + 3'h0)&3'h7;
default: nex_priority = priority;
endcase

Its a little ugly because I've tried to keep the values to 3 bits
explicitly. You could use a rotated interrupt to make the case condition
"prettier" and/or use a conditional staement instead of a case. Assuming a
7:0 order,

wire [7:0] rot_interrupt = {interrupt[6:0],interrupt[7:0]} >> priority;
wire [2:0] next_step = rot_interrupt[0] ? 3'h1
: rot_interrupt[1] ? 3'h2
: rot_interrupt[2] ? 3'h3
: rot_interrupt[3] ? 3'h4
: rot_interrupt[4] ? 3'h5
: rot_interrupt[5] ? 3'h6
: rot_interrupt[6] ? 3'h7
: rot_interrupt[7] ? 3'h0 : 3'h0;
wire [2:0] next_priority = rot_interrupt + next_step;


"Brian Guralnick" <vergent.tech@sympatico.ca> wrote in message
news:RbXDc.5556$207.485037@news20.bellglobal.com...
I'm looking for an easy method to do the following in verilog:


I need the logic to do the following,

If the priority is 0, select the first active interrupt starting from in 0
to thru 7. next priority = the first active interrupt # + 1.
if the priority is 1, select the first active interrupt starting from in 1
to thru 7, then last 0. next priority = the first active interrupt # + 1.
if the priority is 2, select the first active interrupt starting from in 2
to thru 7, then 0 thru 1. next priority = the first active interrupt # +
1.
if the priority is 3, select the first active interrupt starting from in 3
to thru 7, then 0 thru 2. next priority = the first active interrupt # +
1.

ect...

If there are no interrupts, priority will hold it's value.

Now, I can easily make an if / else if table, but that looks messy. Is
there a better shortcut method to do it in verilog?


_____________
Brian G.
 
"Brian Guralnick" <vergent.tech@sympatico.ca> wrote:

I'm looking for an easy method to do the following in verilog:


I need the logic to do the following,

If the priority is 0, select the first active interrupt starting from in 0
to thru 7. next priority = the first active interrupt # + 1.
if the priority is 1, select the first active interrupt starting from in 1
to thru 7, then last 0. next priority = the first active interrupt # + 1.
if the priority is 2, select the first active interrupt starting from in 2
to thru 7, then 0 thru 1. next priority = the first active interrupt # + 1.
if the priority is 3, select the first active interrupt starting from in 3
to thru 7, then 0 thru 2. next priority = the first active interrupt # + 1.

ect...

If there are no interrupts, priority will hold it's value.

Now, I can easily make an if / else if table, but that looks messy. Is
there a better shortcut method to do it in verilog?
If your synthesiser supports non constant shifts then rotate the interrupts
by priority like

wire [7:0] si;

assign si = interrupts << priority | interrupts >> (8 - priority);

Then do a simple 8 way if else if to priority encode si. The encode will be
offset by priority, next priority can be assigned at the same time.
 
Ok, John, what you wrote is close to what I'm interested in, if I understand
it correctly.

On my side, this is what I have done before reading your posts (its been
cut-up, so excuse the missing stuff):
YUK, the tabs dont line up.....

The design is globally synchronous to posedge clk,
the ena_chan are the 8 inputs which are the interupts,
the code is designed to select the next active "ena_chan" in order, jumping
past the last active one as its done so that a contineous active channel is
never selected twice in a row, unless it's the only active channel. The
code also adds 3 dummy cycles between each selected active channel. The var
pcount sets the priority order & also adds the 3 dummy cycles.
the ena_chan_out should only fire 1 chosen interupt & the addr out should
have 1 of the corresponding of the addr ins...

------------------------------------------------------------------

parameter REFRESH_AD_SIZE = 15;
parameter REFRESH_TIMEOUT = 32767;
parameter ADDR_SIZE = 20;

input reset, clk, ena;
input [7:0] ena_chan, ena_rnw ;
input [ADDR_SIZE:0]
addr_in0,addr_in1,addr_in2,addr_in3,addr_in4,addr_in5,addr_in6,addr_in7;

output ena_cmd_out;
reg ena_cmd_out;

output [1:0] cmd_out;
reg [1:0] cmd_out;

output [7:0] ena_chan_out;
reg [7:0] ena_chan_out;

output [ADDR_SIZE:0] addr_out;
reg [ADDR_SIZE:0] addr_out;

output [REFRESH_AD_SIZE:0] refresh_timer;
reg [REFRESH_AD_SIZE:0] refresh_timer;

reg [5:0] pcount;

//
****************************************************************************

always @ (posedge clk) begin

if (reset) begin

pcount <= 6'b100000 ;
ena_cmd_out <= 0;
ena_chan_out <= 0;

end else begin

if (!refresh_timer[REFRESH_AD_SIZE]) refresh_timer <= refresh_timer -1;

if (pcount[1:0] != 3) begin // the first 3 counts are the dummy cycles.
Only the last 4 bits concern the rest of the code.
pcount <= pcount + 1;
ena_cmd_out <= 0;
ena_chan_out <= 0;
end else begin // on the last count, run the rest of the code.

if (ena) begin // basically a recieved input from the ram controler
saying that it's ready for the next r/w command.
if (refresh_timer[REFRESH_AD_SIZE]) begin // this refresh timer
overides the outputs and stops the rest of the code for 1 cycle.

refresh_timer <= REFRESH_TIMEOUT; // reset the refresh timer.
cmd_out[0] <= 1; // self refresh
cmd_out[1] <= 0; // self refresh
ena_cmd_out <= 1;
pcount[1:0] <= 2'b00; // set up for another 3 dummy cycles
without reseting the rest of the location of the priority counter.

end else begin
if (pcount[5:2] == 4'b0000) begin // Priority #0
if (ena_chan[0]) begin
ena_chan_out <= 8'b00000001;
// set only the 1 chosen ena_chan_out
addr_out[ADDR_SIZE:0] <= addr_in0[ADDR_SIZE:0]; // output the
right addr
cmd_out[0] <= ena_rnw[0];
// r/!w
cmd_out[1] <= 1;
// r&w commands
ena_cmd_out <= 1;
// xmit the new command the the ram controler.
pcount <= 6'b000100;
// set the next count position
end else if (ena_chan[1]) begin
ena_chan_out <= 8'b00000010;
addr_out[ADDR_SIZE:0] <= addr_in1[ADDR_SIZE:0];
cmd_out[0] <= ena_rnw[1]; // rnw
cmd_out[1] <= 1; // r&w
ena_cmd_out <= 1;
pcount <= 6'b001000;
end else if (ena_chan[2]) begin
ena_chan_out <= 8'b00000100;
addr_out[ADDR_SIZE:0] <= addr_in2[ADDR_SIZE:0];
cmd_out[0] <= ena_rnw[2]; // rnw
cmd_out[1] <= 1; // r&w
ena_cmd_out <= 1;
pcount <= 6'b001100;
end else if (ena_chan[3]) begin
ena_chan_out <= 8'b00001000;
addr_out[ADDR_SIZE:0] <= addr_in3[ADDR_SIZE:0];
cmd_out[0] <= ena_rnw[3]; // rnw
cmd_out[1] <= 1; // r&w
ena_cmd_out <= 1;
pcount <= 6'b010000;
end else if (ena_chan[4]) begin
ena_chan_out <= 8'b00010000;
addr_out[ADDR_SIZE:0] <= addr_in4[ADDR_SIZE:0];
cmd_out[0] <= ena_rnw[4]; // rnw
cmd_out[1] <= 1; // r&w
ena_cmd_out <= 1;
pcount <= 6'b010100;
end else if (ena_chan[5]) begin
ena_chan_out <= 8'b00100000;
addr_out[ADDR_SIZE:0] <= addr_in5[ADDR_SIZE:0];
cmd_out[0] <= ena_rnw[5]; // rnw
cmd_out[1] <= 1; // r&w
ena_cmd_out <= 1;
pcount <= 6'b011000;
end else if (ena_chan[6]) begin
ena_chan_out <= 8'b01000000;
addr_out[ADDR_SIZE:0] <= addr_in6[ADDR_SIZE:0];
cmd_out[0] <= ena_rnw[6]; // rnw
cmd_out[1] <= 1; // r&w
ena_cmd_out <= 1;
pcount <= 6'b011100;
end else if (ena_chan[7]) begin
ena_chan_out <= 8'b10000000;
addr_out[ADDR_SIZE:0] <= addr_in7[ADDR_SIZE:0];
cmd_out[0] <= ena_rnw[7]; // rnw
cmd_out[1] <= 1; // r&w
ena_cmd_out <= 1;
pcount <= 6'b000000;
end

end else if (pcount[5:2] == 4'b0001) begin // Priority #1, basically the
same as above priority #0, but, the order of if(ena_chan[x]) is shifted back
by 1.
******************************
This rotation of priority is done another 6 times....
******************************

end else if ( pcount[5] ) begin // reset vectors
pcount <= pcount + 1;
cmd_out[0] <= 0; // powerup
cmd_out[1] <= 0; // powerup
ena_cmd_out <= 1;
ena_chan_out <= 0;
end


end // !refresh
end // ena
end // pcount[1:0] != 3
end // !reset
end // always

endmodule


_____________
Brian Guralnick


"John_H" <johnhandwork@mail.com> wrote in message
news:3JYDc.5$E51.961@news-west.eli.net...
How about an 8-wide case? Case statements result in priority order so use
something like
always @(priority and interrupt)
case(1'b1)
interrupt[(priority+3'h0)&3'h7]: next_priority = (priority + 3'h1)&3'h7;
interrupt[(priority+3'h1)&3'h7]: next_priority = (priority + 3'h2)&3'h7;
interrupt[(priority+3'h2)&3'h7]: next_priority = (priority + 3'h3)&3'h7;
interrupt[(priority+3'h3)&3'h7]: next_priority = (priority + 3'h4)&3'h7;
interrupt[(priority+3'h4)&3'h7]: next_priority = (priority + 3'h5)&3'h7;
interrupt[(priority+3'h5)&3'h7]: next_priority = (priority + 3'h6)&3'h7;
interrupt[(priority+3'h6)&3'h7]: next_priority = (priority + 3'h7)&3'h7;
interrupt[(priority+3'h7)&3'h7]: next_priority = (priority + 3'h0)&3'h7;
default: nex_priority = priority;
endcase

Its a little ugly because I've tried to keep the values to 3 bits
explicitly. You could use a rotated interrupt to make the case condition
"prettier" and/or use a conditional staement instead of a case. Assuming a
7:0 order,

wire [7:0] rot_interrupt = {interrupt[6:0],interrupt[7:0]} >> priority;
wire [2:0] next_step = rot_interrupt[0] ? 3'h1
: rot_interrupt[1] ? 3'h2
: rot_interrupt[2] ? 3'h3
: rot_interrupt[3] ? 3'h4
: rot_interrupt[4] ? 3'h5
: rot_interrupt[5] ? 3'h6
: rot_interrupt[6] ? 3'h7
: rot_interrupt[7] ? 3'h0 : 3'h0;
wire [2:0] next_priority = rot_interrupt + next_step;


"Brian Guralnick" <vergent.tech@sympatico.ca> wrote in message
news:RbXDc.5556$207.485037@news20.bellglobal.com...
I'm looking for an easy method to do the following in verilog:


I need the logic to do the following,

If the priority is 0, select the first active interrupt starting from in 0
to thru 7. next priority = the first active interrupt # + 1.
if the priority is 1, select the first active interrupt starting from in 1
to thru 7, then last 0. next priority = the first active interrupt # + 1.
if the priority is 2, select the first active interrupt starting from in 2
to thru 7, then 0 thru 1. next priority = the first active interrupt # +
1.
if the priority is 3, select the first active interrupt starting from in 3
to thru 7, then 0 thru 2. next priority = the first active interrupt # +
1.

ect...

If there are no interrupts, priority will hold it's value.

Now, I can easily make an if / else if table, but that looks messy. Is
there a better shortcut method to do it in verilog?


_____________
Brian G.
 
"Brian Guralnick" <vergent.tech@sympatico.ca> wrote in message
news:9hbEc.73031$Ax1.490991@news20.bellglobal.com...
Ok, John, what you wrote is close to what I'm interested in, if I
understand
it correctly.

On my side, this is what I have done before reading your posts (its been
cut-up, so excuse the missing stuff):
YUK, the tabs dont line up.....

The design is globally synchronous to posedge clk,
the ena_chan are the 8 inputs which are the interupts,
the code is designed to select the next active "ena_chan" in order,
jumping
past the last active one as its done so that a contineous active channel
is
never selected twice in a row, unless it's the only active channel. The
code also adds 3 dummy cycles between each selected active channel. The
var
pcount sets the priority order & also adds the 3 dummy cycles.
the ena_chan_out should only fire 1 chosen interupt & the addr out should
have 1 of the corresponding of the addr ins...

[snipped allll of the following stuff]

No wonder you wanted something cleaner!
Sometimes I hate Microsoft Outlook Express - I can't quite get fixed fonts
to work for authoring.

I thought a little bit about the synthesis and figured how a fixed priority
structure would give nice, compact results.
I'm not sure what the pcount[5] is doing so this suggestion is for pcount[5]
always 0. You need to work in the difference.

The wide_ena_chan includes a mask that only looks at 8 ena_chan bits at a
time where the lowest enabled bit in the vector is the highest priority
channel. The priority structure that results is a 14-bit long cascade -
typically implemented in FPGAs with MUXCY or CASCADE style primitives - for
each bit of output. The loop goes from the highest bit in the vector to the
lowest where the last encountered ena_chan gets the service.

wire [14:0] wide_ena_chan = {ena_chan[6:0],ena_chan[7:0]}
& (15'h00ff << pcount[4:2]);
integer i;
reg [4:2] use_chan;
always @(*)
for( i=0; i<15; i=i+1 ) // first evaluated is 14
if( wide_ena_chan[14-i] ) // last evaluated is 0
use_chan = (14-i) & 3'h7; // last encountered, allowed ena_chan wins
wire use_chan_ena = |ena_chan[7:0]; // ANY ena_chan indicates service

// make a 2-d array for easy mux-style select
wire [ADDR_SIZE:0] addr_in_arr [7:0];
assign addr_in_arr[0] = addr_in0;
assign addr_in_arr[1] = addr_in1;
assign addr_in_arr[2] = addr_in2;
assign addr_in_arr[3] = addr_in3;
assign addr_in_arr[4] = addr_in4;
assign addr_in_arr[5] = addr_in5;
assign addr_in_arr[6] = addr_in6;
assign addr_in_arr[7] = addr_in7;

With these values, the bulk of your code collapses down to
...
end else if ( pcount[5] ) begin // reset vectors
pcount <= pcount + 1;
cmd_out[0] <= 0; // powerup
cmd_out[1] <= 0; // powerup
ena_cmd_out <= 1;
ena_chan_out <= 0;
end else if( use_chan_ena ) // !pcount[5] & at least 1 ena_chan
begin
ena_chan_out <= 8'h01 << use_chan;
addr_out <= addr_in_arr[use_chan];
cmd_out[0] <= ena_rnw[use_chan];
cmd_out[1] <= 1'b1;
ena_cmd_out <= 1'b1;
pcount <= ((use_chan + 1) & 3'h7) << 2;
end
 
[original post omitted - see thread]

The always @(*) effectively produces a continuous assignment. The
pre-Verilog 2001 format wouls be "always @(wide_ena_chan)" but the newer
wildcard indication forces the sensitivity list to be "everything."

I saw your code arount the pcount[5] later which is why the
if(pcount[5])/else is in the small piece of code.
 
Oh - and the rarely use of the blocking operator for good rather than evil:
integer i;
always @(priority and interrupt)
begin
next_priority = priority; // maintain priority if no interrupts
for (i=0; i<8; i=i+1)
if( interrupt[(priority+3'h7-i)%8] ) next_priority = i;
end // interrupt[priority] is top precedence, interrupt[priority-1] is
lowest


"John_H" <johnhandwork@mail.com> wrote in message
news:3JYDc.5$E51.961@news-west.eli.net...
How about an 8-wide case? Case statements result in priority order so use
something like
always @(priority and interrupt)
case(1'b1)
interrupt[(priority+3'h0)&3'h7]: next_priority = (priority +
3'h1)&3'h7;
interrupt[(priority+3'h1)&3'h7]: next_priority = (priority +
3'h2)&3'h7;
interrupt[(priority+3'h2)&3'h7]: next_priority = (priority +
3'h3)&3'h7;
interrupt[(priority+3'h3)&3'h7]: next_priority = (priority +
3'h4)&3'h7;
interrupt[(priority+3'h4)&3'h7]: next_priority = (priority +
3'h5)&3'h7;
interrupt[(priority+3'h5)&3'h7]: next_priority = (priority +
3'h6)&3'h7;
interrupt[(priority+3'h6)&3'h7]: next_priority = (priority +
3'h7)&3'h7;
interrupt[(priority+3'h7)&3'h7]: next_priority = (priority +
3'h0)&3'h7;
default: nex_priority = priority;
endcase

Its a little ugly because I've tried to keep the values to 3 bits
explicitly. You could use a rotated interrupt to make the case condition
"prettier" and/or use a conditional staement instead of a case. Assuming
a
7:0 order,

wire [7:0] rot_interrupt = {interrupt[6:0],interrupt[7:0]} >> priority;
wire [2:0] next_step = rot_interrupt[0] ? 3'h1
: rot_interrupt[1] ? 3'h2
: rot_interrupt[2] ? 3'h3
: rot_interrupt[3] ? 3'h4
: rot_interrupt[4] ? 3'h5
: rot_interrupt[5] ? 3'h6
: rot_interrupt[6] ? 3'h7
: rot_interrupt[7] ? 3'h0 : 3'h0;
wire [2:0] next_priority = rot_interrupt + next_step;
 
Thanks a million... Now that's tiny... I'm going to try it out now.

Now I hope that Quartus's built in Verilog compiler will properly handle the dynamic bit shifting of wire [14:0] wide_ena_chan. I know the older versions had problems similar instances as well as ram pointed registers, but, Altera claims it has added support for such things in Quartus 4.

So, If I understand your code correctly:

----------------------
always @(*)
for( i=0; i<15; i=i+1 ) // first evaluated is 14
if( wide_ena_chan[14-i] ) // last evaluated is 0
use_chan = (14-i) & 3'h7; // last encountered, allowed ena_chan wins
----------------------

This sitting up in front, outside my synchronous "always @ (posedge clk) begin" tells the compiler to imagine, test, & assign all 15 instances immediately every single clock? The "always @(*)" is new to me. Around 2 years ago, I posted on this forum I once did an "always @ (1) begin" to create immediate logic which was not clocked, but it still simulated within Quartus & I was warned not to do anything like that, ever...


always 0. You need to work in the difference.
If you look at the top of my code:
-------------------------
if (reset) begin

pcount <= 6'b100000 ; <<<<<< bit[5] high here...
ena_cmd_out <= 0;
ena_chan_out <= 0;

end else begin
------------------------
During the reset, this is when the 'pcount' bit[5] goes 1. After the reset is released, the power-up procedure -'reset vector' will run for 32 clocks until it will clear bit[5]. Bit[5] will never set again unless the reset is applied once more. Changing the number of clocks after reset can be done by changing the 6'b1xxxxx to whatever suits me. I find this an efficient method to add a 1 shot sequence with programmable # of clock cycles after something like resets, or powerups without adding a separate reset counter, it just adds 1 bit to the main counter.

_____________
Brian Guralnick


"John_H" <johnhandwork@mail.com> wrote in message news:YBgEc.8$E51.1528@news-west.eli.net...
"Brian Guralnick" <vergent.tech@sympatico.ca> wrote in message
news:9hbEc.73031$Ax1.490991@news20.bellglobal.com...
Ok, John, what you wrote is close to what I'm interested in, if I
understand
it correctly.

On my side, this is what I have done before reading your posts (its been
cut-up, so excuse the missing stuff):
YUK, the tabs dont line up.....

The design is globally synchronous to posedge clk,
the ena_chan are the 8 inputs which are the interupts,
the code is designed to select the next active "ena_chan" in order,
jumping
past the last active one as its done so that a contineous active channel
is
never selected twice in a row, unless it's the only active channel. The
code also adds 3 dummy cycles between each selected active channel. The
var
pcount sets the priority order & also adds the 3 dummy cycles.
the ena_chan_out should only fire 1 chosen interupt & the addr out should
have 1 of the corresponding of the addr ins...

[snipped allll of the following stuff]

No wonder you wanted something cleaner!
Sometimes I hate Microsoft Outlook Express - I can't quite get fixed fonts
to work for authoring.

I thought a little bit about the synthesis and figured how a fixed priority
structure would give nice, compact results.
I'm not sure what the pcount[5] is doing so this suggestion is for pcount[5]
always 0. You need to work in the difference.

The wide_ena_chan includes a mask that only looks at 8 ena_chan bits at a
time where the lowest enabled bit in the vector is the highest priority
channel. The priority structure that results is a 14-bit long cascade -
typically implemented in FPGAs with MUXCY or CASCADE style primitives - for
each bit of output. The loop goes from the highest bit in the vector to the
lowest where the last encountered ena_chan gets the service.

wire [14:0] wide_ena_chan = {ena_chan[6:0],ena_chan[7:0]}
& (15'h00ff << pcount[4:2]);
integer i;
reg [4:2] use_chan;
always @(*)
for( i=0; i<15; i=i+1 ) // first evaluated is 14
if( wide_ena_chan[14-i] ) // last evaluated is 0
use_chan = (14-i) & 3'h7; // last encountered, allowed ena_chan wins
wire use_chan_ena = |ena_chan[7:0]; // ANY ena_chan indicates service

// make a 2-d array for easy mux-style select
wire [ADDR_SIZE:0] addr_in_arr [7:0];
assign addr_in_arr[0] = addr_in0;
assign addr_in_arr[1] = addr_in1;
assign addr_in_arr[2] = addr_in2;
assign addr_in_arr[3] = addr_in3;
assign addr_in_arr[4] = addr_in4;
assign addr_in_arr[5] = addr_in5;
assign addr_in_arr[6] = addr_in6;
assign addr_in_arr[7] = addr_in7;

With these values, the bulk of your code collapses down to
...
end else if ( pcount[5] ) begin // reset vectors
pcount <= pcount + 1;
cmd_out[0] <= 0; // powerup
cmd_out[1] <= 0; // powerup
ena_cmd_out <= 1;
ena_chan_out <= 0;
end else if( use_chan_ena ) // !pcount[5] & at least 1 ena_chan
begin
ena_chan_out <= 8'h01 << use_chan;
addr_out <= addr_in_arr[use_chan];
cmd_out[0] <= ena_rnw[use_chan];
cmd_out[1] <= 1'b1;
ena_cmd_out <= 1'b1;
pcount <= ((use_chan + 1) & 3'h7) << 2;
end
 

Welcome to EDABoard.com

Sponsor

Back
Top