// Copyright 2018 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this 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. // //------------------------------------------------------------------------------- //-- Title : Register Interface //-- File : plic_target_slice.sv //-- Author : Gian Marti <gimarti.student.ethz.ch> //-- Author : Thomas Kramer <tkramer.student.ethz.ch> //-- Author : Thomas E. Benz <tbenz.student.ethz.ch> //-- Company : Integrated Systems Laboratory, ETH Zurich //-- Created : 2018-03-31 //-- Last update: 2018-03-31 //-- Platform : ModelSim (simulation), Synopsys (synthesis) //-- Standard : SystemVerilog IEEE 1800-2012 //------------------------------------------------------------------------------- //-- Description: Implementation of the plic's register interface //------------------------------------------------------------------------------- //-- Revisions : //-- Date Version Author Description //-- 2018-03-31 2.0 tbenz Created header //------------------------------------------------------------------------------- module plic_interface #( parameter int ADDR_WIDTH = 32, // width of external address bus parameter int DATA_WIDTH = 32, // width of external data bus parameter int ID_BITWIDTH = 10, // width of the gateway indecies parameter int PARAMETER_BITWIDTH = 3, // width of the internal parameter e.g. priorities parameter int NUM_TARGETS = 1, // number of target slices parameter int NUM_GATEWAYS = 1 // number of gateways )( input logic clk_i, // the clock signal input logic rst_ni, // asynchronous reset active low input logic[ID_BITWIDTH-1:0] id_of_largest_priority_i[NUM_TARGETS], // input array id of largest priority input logic pending_array_i[NUM_GATEWAYS], // array with the interrupt pending , idx0 is gateway 1 output reg [PARAMETER_BITWIDTH-1:0] thresholds_o[NUM_TARGETS], // save internally the thresholds, communicate values over this port to the core output reg [PARAMETER_BITWIDTH-1:0] gateway_priorities_o[NUM_GATEWAYS], // save internally the the priorities, communicate values output logic irq_enables_o[NUM_GATEWAYS][NUM_TARGETS], // communicate enable bits over this port output logic target_irq_claims_o[NUM_TARGETS], // claim signals output logic target_irq_completes_o[NUM_TARGETS], // complete signals output logic[ID_BITWIDTH-1:0] target_irq_completes_id_o[NUM_TARGETS], // the id of the gateway to be completed REG_BUS.in external_bus_io // the bus ); //define ennumerated types enum logic [2:0] {INV, PRI, IPA, IEB, THR, CCP} funct; // the address mapping will primarily check what // function should be performed // INV: invalid, PRI: priorities, IPA: interrupt pending // IEB: interrupt enable, THR: threshold and claim/complete // CCP: claim/clear pulses //calculate some parameter needed later localparam int num_gateway_bundles = (NUM_GATEWAYS-1) / DATA_WIDTH + 1; // how many bundles we have to consider localparam int bpw = DATA_WIDTH / 8; // how many bytes a data word consist of //internal signals logic [ADDR_WIDTH-12-1:0 ] page_address; // the upper part of the address logic [11:0 ] page_offset; // the lowest 12 bit describes the page offset logic [11-$clog2(bpw):0 ] page_word_offset; // the word address of each page offset logic [$clog2(bpw)-1:0 ] word_offset; // the byte in the word logic [DATA_WIDTH/8-1:0 ] write_active; // 0 if inactive, else what byte ahs to be written logic read_active; // 0 if inactive, 1 when reading the word //bundle definitions logic [DATA_WIDTH-1:0 ] irq_pending_bundle[num_gateway_bundles]; //registers logic [PARAMETER_BITWIDTH-1:0] thresholds_d[NUM_TARGETS]; logic [PARAMETER_BITWIDTH-1:0] thresholds_q[NUM_TARGETS]; logic [PARAMETER_BITWIDTH-1:0] priorities_d[NUM_GATEWAYS]; logic [PARAMETER_BITWIDTH-1:0] priorities_q[NUM_GATEWAYS]; logic [ID_BITWIDTH-1:0 ] id_of_largest_priority_d[NUM_TARGETS]; logic [ID_BITWIDTH-1:0 ] id_of_largest_priority_q[NUM_TARGETS]; logic [DATA_WIDTH/bpw-1:0 ] ena_bundles_d[num_gateway_bundles][NUM_TARGETS][DATA_WIDTH/8]; logic [DATA_WIDTH/bpw-1:0 ] ena_bundles_q[num_gateway_bundles][NUM_TARGETS][DATA_WIDTH/8]; // assignments assign id_of_largest_priority_d = id_of_largest_priority_i; // assign addresses assign page_address = external_bus_io.addr[ADDR_WIDTH-1:12]; assign page_offset = external_bus_io.addr[11:0 ]; assign page_word_offset = external_bus_io.addr[11:$clog2(bpw) ]; assign word_offset = external_bus_io.addr[$clog2(bpw)-1:0]; assign write_active = (external_bus_io.valid & external_bus_io.write) ? external_bus_io.wstrb : '0; assign read_active = external_bus_io.valid & !external_bus_io.write; // bundle signals for (genvar bundle = 0; bundle < num_gateway_bundles; bundle++) begin for (genvar ip_bit = 0; ip_bit < DATA_WIDTH; ip_bit++) begin if (bundle * DATA_WIDTH + ip_bit < NUM_GATEWAYS) begin assign irq_pending_bundle[bundle][ip_bit] = pending_array_i[bundle * DATA_WIDTH + ip_bit]; end else begin assign irq_pending_bundle[bundle][ip_bit] = '0; end end end for (genvar bundle = 0; bundle < num_gateway_bundles; bundle++) begin for (genvar target = 0; target < NUM_TARGETS; target++) begin for (genvar byte_in_word = 0; byte_in_word < DATA_WIDTH/8; byte_in_word++) begin for (genvar enable_bit = 0; enable_bit < 8; enable_bit++) begin assign irq_enables_o[bundle * DATA_WIDTH + enable_bit + byte_in_word * 8][target] = ena_bundles_q[bundle][target][byte_in_word][enable_bit]; end end end end // determine the function to be performed always_comb begin : proc_address_map // default values funct = INV; // only aligned access is allowed: if (word_offset == '0) begin // we have now an word alligned access -> check out page offset to determine // what type of access this is. if (page_address[13:0] == 0) begin // we access the gateway priority bits // the page_word_offset tells us now which gateway we consider // in order to grant or deny access, we have to check if the gateway // in question really exist. // Gateway 0 does not exist, so return an error if (page_word_offset <= NUM_GATEWAYS && page_word_offset > 0) begin //the gateway in question exists // set the current operation to be an access to the priority registers funct = PRI; end // we now access the IP Bits, read only end else if (page_address[13:0] == 1) begin // the page_word_offset tells us now, which word we have to consider, // the word, which includes the IP bit in question should be returned if (page_word_offset<num_gateway_bundles) begin funct = IPA; end // access of the enable bits for each target end else if (page_address[13:9] == 0) begin // the bottom part page_word_offset now tells us which gateway bundle we have to consider // part of the page_address and the upper part of the page_word_offset give us the target nr. if (page_offset[6:$clog2(bpw)] < num_gateway_bundles) begin if (({page_address[8:0], page_offset[11:7]} - 64) < NUM_TARGETS) begin funct = IEB; end end // priority / claim / complete end else begin // page address - 0h20 gives the target number if (page_address[13:0] - 'h200 < NUM_TARGETS) begin // check lowest bit of the page_word_offset to get the exact function if (page_word_offset == 0) begin funct = THR; end else if (page_word_offset == 1) begin funct = CCP; end end end end end always_comb begin : proc_read_write // defalt values external_bus_io.rdata = 0; external_bus_io.error = 0; external_bus_io.ready = 0; for (integer target = 0; target<NUM_TARGETS; target++) begin target_irq_claims_o [target] = '0; target_irq_completes_o [target] = '0; target_irq_completes_id_o[target] = '0; end //just keep the values untouched as default priorities_d = priorities_q; ena_bundles_d = ena_bundles_q; thresholds_d = thresholds_q; case (funct) PRI: begin // read case if (read_active != 0) begin external_bus_io.rdata = priorities_q[page_word_offset-1]; external_bus_io.ready = 1; // write case end else if (write_active[0] == 1) begin priorities_d[page_word_offset-1] = external_bus_io.wdata[PARAMETER_BITWIDTH-1:0]; external_bus_io.ready = 1; end end IPA: begin // read case if (read_active != 0) begin external_bus_io.rdata = irq_pending_bundle[page_word_offset]; external_bus_io.ready = 1; // write case end else if (write_active != 0) begin external_bus_io.error = 1; //not allowed end end IEB: begin // read case if (read_active != 0) begin for (integer byte_in_word = 0; byte_in_word < DATA_WIDTH/8; byte_in_word++) begin external_bus_io.rdata[8*(byte_in_word) +: 8] = ena_bundles_q[page_offset[6:$clog2(bpw)]][({page_address[8:0], page_offset[11:7]} - 64)][byte_in_word]; end external_bus_io.ready = 1; // write case end else if(write_active != 0) begin for (integer byte_in_word=0; byte_in_word<DATA_WIDTH/8; byte_in_word++) begin if(write_active[byte_in_word]) begin ena_bundles_d[page_offset[6:$clog2(bpw)]][({page_address[8:0], page_offset[11:7]} - 64)][byte_in_word] = external_bus_io.wdata[8*(byte_in_word) +: 8]; end end external_bus_io.ready = 1; end end THR: begin // read case if (read_active != 0) begin external_bus_io.rdata[PARAMETER_BITWIDTH-1:0] = thresholds_q[(page_address[13:0] - 'h200)]; external_bus_io.ready = 1; // write case end else if (write_active != 0) begin thresholds_d[(page_address[13:0] - 'h200)] = external_bus_io.wdata[PARAMETER_BITWIDTH-1:0]; external_bus_io.ready = 1; end end CCP: begin // read case if (read_active != 0) begin target_irq_claims_o[(page_address[13:0] - 'h200)] = 1; external_bus_io.rdata[ID_BITWIDTH-1:0] = id_of_largest_priority_q[(page_address[13:0] - 'h200)]; external_bus_io.ready = 1; // write case end else if (write_active != 0) begin target_irq_completes_o[(page_address[13:0] - 'h200)] = 1; target_irq_completes_id_o[(page_address[13:0] - 'h200)] = external_bus_io.wdata[ID_BITWIDTH-1:0]; external_bus_io.ready = 1; end end default : begin //per default: error external_bus_io.error = 1; external_bus_io.ready = 1; end endcase // funct end // store data in flip flops always_ff @(posedge clk_i or negedge rst_ni) begin : proc_update_ff if (~rst_ni) begin // set all registers to 0 for (integer gateway = 0; gateway < NUM_GATEWAYS; gateway++) priorities_q[gateway] <= 0; for (integer bundle = 0; bundle < num_gateway_bundles; bundle++) for (integer target = 0; target < NUM_TARGETS; target++) for (integer byte_in_word = 0; byte_in_word < DATA_WIDTH/8; byte_in_word++) ena_bundles_q[bundle][target][byte_in_word] <= 0; for (integer target = 0; target < NUM_TARGETS; target++) begin thresholds_q[target] <= 0; id_of_largest_priority_q[target] <= 0; end end else begin priorities_q <= priorities_d; ena_bundles_q <= ena_bundles_d; thresholds_q <= thresholds_d; id_of_largest_priority_q <= id_of_largest_priority_d; end end //assign outputs assign thresholds_o = thresholds_q; assign gateway_priorities_o = priorities_q; // pragma translate_off `ifndef VERILATOR initial begin assert ((ADDR_WIDTH==32) | (ADDR_WIDTH==64)) else $error("Address width has to bei either 32 or 64 bit"); assert ((DATA_WIDTH==32) | (DATA_WIDTH==64)) else $error("Data width has to bei either 32 or 64 bit"); assert (ID_BITWIDTH>0) else $error("ID_BITWIDTH has to be larger than 1"); assert (ID_BITWIDTH<10) else $error("ID_BITWIDTH has to be smaller than 10"); assert (PARAMETER_BITWIDTH>0) else $error("PARAMETER_BITWIDTH has to be larger than 1"); assert (PARAMETER_BITWIDTH<8) else $error("PARAMETER_BITWIDTH has to be smaller than 8"); assert (NUM_GATEWAYS>0) else $error("Num od Gateways has to be larger than 1"); assert (NUM_GATEWAYS<512) else $error("Num of Gateways has to be smaller than 512"); assert (NUM_TARGETS>0) else $error("Num Target slices has to be larger than 1"); assert (NUM_TARGETS<15872) else $error("Num target slices has to be smaller than 15872"); end `endif // pragma translate_on endmodule