241 lines
8.0 KiB
Systemverilog
241 lines
8.0 KiB
Systemverilog
|
/*
|
|||
|
Copyright 2021 Blue Liang, liangkangnan@163.com
|
|||
|
|
|||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|||
|
you may not use this file except in compliance with the License.
|
|||
|
You may obtain a copy of the License at
|
|||
|
|
|||
|
http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
|
|||
|
Unless required by applicable law or agreed to in writing, software
|
|||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
|
See the License for the specific language governing permissions and
|
|||
|
limitations under the License.
|
|||
|
*/
|
|||
|
|
|||
|
`include "../core/defines.sv"
|
|||
|
|
|||
|
// RISC-V中断控制器
|
|||
|
// 支持32个中断源,每个中断源支持256级优先级
|
|||
|
module rvic #(
|
|||
|
|
|||
|
)(
|
|||
|
|
|||
|
input logic clk_i,
|
|||
|
input logic rst_ni,
|
|||
|
|
|||
|
input logic [31:0] src_i,
|
|||
|
output logic irq_o, // 中断请求信号
|
|||
|
output logic [7:0] irq_id_o, // 中断号
|
|||
|
|
|||
|
input logic [31:0] addr_i,
|
|||
|
input logic [31:0] data_i,
|
|||
|
input logic [3:0] be_i,
|
|||
|
input logic we_i,
|
|||
|
output logic [31:0] data_o
|
|||
|
|
|||
|
);
|
|||
|
|
|||
|
// 寄存器地址偏移
|
|||
|
parameter logic [7:0] IE_OFFSET = 8'h0;
|
|||
|
parameter logic [7:0] IP_OFFSET = 8'h4;
|
|||
|
parameter logic [7:0] PRIO0_OFFSET = 8'h8;
|
|||
|
parameter logic [7:0] PRIO1_OFFSET = 8'hc;
|
|||
|
parameter logic [7:0] PRIO2_OFFSET = 8'h10;
|
|||
|
parameter logic [7:0] PRIO3_OFFSET = 8'h14;
|
|||
|
parameter logic [7:0] PRIO4_OFFSET = 8'h18;
|
|||
|
parameter logic [7:0] PRIO5_OFFSET = 8'h1c;
|
|||
|
parameter logic [7:0] PRIO6_OFFSET = 8'h20;
|
|||
|
parameter logic [7:0] PRIO7_OFFSET = 8'h24;
|
|||
|
parameter logic [7:0] ID_OFFSET = 8'h28;
|
|||
|
|
|||
|
logic ie_we;
|
|||
|
logic ip_we;
|
|||
|
logic [7:0] prio_we;
|
|||
|
|
|||
|
logic [31:0] prio_q[8];
|
|||
|
logic [31:0] ie_q;
|
|||
|
logic [31:0] ip_q;
|
|||
|
logic [31:0] id_q;
|
|||
|
|
|||
|
logic [10:0] addr_hit;
|
|||
|
logic [7:0] reg_addr = addr_i[7:0];
|
|||
|
|
|||
|
always_comb begin
|
|||
|
addr_hit = 11'h0;
|
|||
|
addr_hit[ 0] = (reg_addr == IE_OFFSET);
|
|||
|
addr_hit[ 1] = (reg_addr == IP_OFFSET);
|
|||
|
addr_hit[ 2] = (reg_addr == PRIO0_OFFSET);
|
|||
|
addr_hit[ 3] = (reg_addr == PRIO1_OFFSET);
|
|||
|
addr_hit[ 4] = (reg_addr == PRIO2_OFFSET);
|
|||
|
addr_hit[ 5] = (reg_addr == PRIO3_OFFSET);
|
|||
|
addr_hit[ 6] = (reg_addr == PRIO4_OFFSET);
|
|||
|
addr_hit[ 7] = (reg_addr == PRIO5_OFFSET);
|
|||
|
addr_hit[ 8] = (reg_addr == PRIO6_OFFSET);
|
|||
|
addr_hit[ 9] = (reg_addr == PRIO7_OFFSET);
|
|||
|
addr_hit[10] = (reg_addr == ID_OFFSET);
|
|||
|
end
|
|||
|
|
|||
|
assign ie_we = we_i & addr_hit[0];
|
|||
|
assign ip_we = we_i & addr_hit[1];
|
|||
|
for (genvar p = 0; p < 8; p = p + 1) begin
|
|||
|
assign prio_we[p] = we_i & addr_hit[p + 2];
|
|||
|
end
|
|||
|
|
|||
|
// 写寄存器
|
|||
|
logic [31:0] reg_wdata;
|
|||
|
|
|||
|
always_comb begin
|
|||
|
reg_wdata = 32'h0;
|
|||
|
|
|||
|
// IP寄存器是写1清零的,因此要区别对待
|
|||
|
if (ip_we) begin
|
|||
|
reg_wdata = ip_q;
|
|||
|
if (be_i[0])
|
|||
|
reg_wdata[7:0] = ip_q[7:0] & (~data_i[7:0]);
|
|||
|
if (be_i[1])
|
|||
|
reg_wdata[15:8] = ip_q[15:8] & (~data_i[15:8]);
|
|||
|
if (be_i[2])
|
|||
|
reg_wdata[23:16] = ip_q[23:16] & (~data_i[23:16]);
|
|||
|
if (be_i[3])
|
|||
|
reg_wdata[31:24] = ip_q[31:24] & (~data_i[31:24]);
|
|||
|
end else begin
|
|||
|
if (ie_we) begin
|
|||
|
reg_wdata = ie_q;
|
|||
|
end
|
|||
|
for (int j = 0; j < 8; j = j + 1) begin
|
|||
|
if (prio_we[j]) begin
|
|||
|
reg_wdata = prio_q[j];
|
|||
|
end
|
|||
|
end
|
|||
|
if (be_i[0])
|
|||
|
reg_wdata[7:0] = data_i[7:0];
|
|||
|
if (be_i[1])
|
|||
|
reg_wdata[15:8] = data_i[15:8];
|
|||
|
if (be_i[2])
|
|||
|
reg_wdata[23:16] = data_i[23:16];
|
|||
|
if (be_i[3])
|
|||
|
reg_wdata[31:24] = data_i[31:24];
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
gen_en_dff #(32) ie_ff(clk_i, rst_ni, ie_we, reg_wdata, ie_q);
|
|||
|
|
|||
|
logic [31:0] ip_wdata;
|
|||
|
|
|||
|
assign ip_wdata = ip_we ? reg_wdata : (ip_q | src_i);
|
|||
|
gen_en_dff #(32) ip_ff(clk_i, rst_ni, 1'b1, ip_wdata, ip_q);
|
|||
|
|
|||
|
for (genvar m = 0; m < 8; m = m + 1) begin
|
|||
|
gen_en_dff #(32) prio_ff(clk_i, rst_ni, prio_we[m], reg_wdata, prio_q[m]);
|
|||
|
end
|
|||
|
|
|||
|
// 读寄存器
|
|||
|
always_ff @ (posedge clk_i or negedge rst_ni) begin
|
|||
|
if (!rst_ni) begin
|
|||
|
data_o <= 32'h0;
|
|||
|
end else begin
|
|||
|
case (addr_i[7:0])
|
|||
|
IE_OFFSET: data_o <= ie_q;
|
|||
|
IP_OFFSET: data_o <= ip_q;
|
|||
|
PRIO0_OFFSET: data_o <= prio_q[0];
|
|||
|
PRIO1_OFFSET: data_o <= prio_q[1];
|
|||
|
PRIO2_OFFSET: data_o <= prio_q[2];
|
|||
|
PRIO3_OFFSET: data_o <= prio_q[3];
|
|||
|
PRIO4_OFFSET: data_o <= prio_q[4];
|
|||
|
PRIO5_OFFSET: data_o <= prio_q[5];
|
|||
|
PRIO6_OFFSET: data_o <= prio_q[6];
|
|||
|
PRIO7_OFFSET: data_o <= prio_q[7];
|
|||
|
ID_OFFSET: data_o <= id_q;
|
|||
|
default: data_o <= 32'h0;
|
|||
|
endcase
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
// 找出优先级最高(优先级值最大)的中断源
|
|||
|
// 二分法查找
|
|||
|
|
|||
|
logic [7:0] each_prio[32];
|
|||
|
|
|||
|
for (genvar i = 0; i < 8; i = i + 1) begin
|
|||
|
for (genvar j = 0; j < 4; j = j + 1) begin
|
|||
|
assign each_prio[i*4+j] = prio_q[i][8*j+7:8*j] & {8{ie_q[i*4+j]}};
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
typedef struct packed {
|
|||
|
logic [7:0] id;
|
|||
|
logic [7:0] prio;
|
|||
|
} int_info_t;
|
|||
|
|
|||
|
int_info_t l1_max[16];
|
|||
|
always_comb begin
|
|||
|
for (int i = 0; i < 16; i = i + 1) begin
|
|||
|
if (each_prio[2*i+1] > each_prio[2*i]) begin
|
|||
|
l1_max[i].id = 2*i+1;
|
|||
|
l1_max[i].prio = each_prio[2*i+1];
|
|||
|
end else begin
|
|||
|
l1_max[i].id = 2*i;
|
|||
|
l1_max[i].prio = each_prio[2*i];
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
int_info_t l2_max[8];
|
|||
|
always_comb begin
|
|||
|
for (int i = 0; i < 8; i = i + 1) begin
|
|||
|
if (l1_max[2*i+1].prio > l1_max[2*i].prio) begin
|
|||
|
l2_max[i].id = l1_max[2*i+1].id;
|
|||
|
l2_max[i].prio = l1_max[2*i+1].prio;
|
|||
|
end else begin
|
|||
|
l2_max[i].id = l1_max[2*i].id;
|
|||
|
l2_max[i].prio = l1_max[2*i].prio;
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
int_info_t l3_max[4];
|
|||
|
always_comb begin
|
|||
|
for (int i = 0; i < 4; i = i + 1) begin
|
|||
|
if (l2_max[2*i+1].prio > l2_max[2*i].prio) begin
|
|||
|
l3_max[i].id = l2_max[2*i+1].id;
|
|||
|
l3_max[i].prio = l2_max[2*i+1].prio;
|
|||
|
end else begin
|
|||
|
l3_max[i].id = l2_max[2*i].id;
|
|||
|
l3_max[i].prio = l2_max[2*i].prio;
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
int_info_t l4_max[2];
|
|||
|
always_comb begin
|
|||
|
for (int i = 0; i < 2; i = i + 1) begin
|
|||
|
if (l3_max[2*i+1].prio > l3_max[2*i].prio) begin
|
|||
|
l4_max[i].id = l3_max[2*i+1].id;
|
|||
|
l4_max[i].prio = l3_max[2*i+1].prio;
|
|||
|
end else begin
|
|||
|
l4_max[i].id = l3_max[2*i].id;
|
|||
|
l4_max[i].prio = l3_max[2*i].prio;
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
logic [7:0] irq_id;
|
|||
|
|
|||
|
assign irq_id = (l4_max[1].prio > l4_max[0].prio) ? l4_max[1].id : l4_max[0].id;
|
|||
|
|
|||
|
always_ff @ (posedge clk_i or negedge rst_ni) begin
|
|||
|
if (!rst_ni) begin
|
|||
|
irq_id_o <= 8'h0;
|
|||
|
irq_o <= 1'b0;
|
|||
|
end else begin
|
|||
|
irq_id_o <= irq_id;
|
|||
|
irq_o <= |((src_i | ip_q) & ie_q);
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
assign id_q = {24'h0, irq_id};
|
|||
|
|
|||
|
endmodule
|