mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-23 13:27:10 -04:00
Fix all remaining issues reported by Verible lint. It turns out that #965 undid some of the fixes in `ibex_alu.sv` that were done in #980 around the `SHUFFLE_*`/`FLIP_*` signals.
154 lines
5 KiB
Systemverilog
154 lines
5 KiB
Systemverilog
// Copyright lowRISC contributors.
|
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Example memory mapped timer
|
|
|
|
`include "prim_assert.sv"
|
|
|
|
module timer #(
|
|
// Bus data width (must be 32)
|
|
parameter int unsigned DataWidth = 32,
|
|
// Bus address width
|
|
parameter int unsigned AddressWidth = 32
|
|
) (
|
|
input logic clk_i,
|
|
input logic rst_ni,
|
|
// Bus interface
|
|
input logic timer_req_i,
|
|
|
|
input logic [AddressWidth-1:0] timer_addr_i,
|
|
input logic timer_we_i,
|
|
input logic [ DataWidth/8-1:0] timer_be_i,
|
|
input logic [ DataWidth-1:0] timer_wdata_i,
|
|
output logic timer_rvalid_o,
|
|
output logic [ DataWidth-1:0] timer_rdata_o,
|
|
output logic timer_err_o,
|
|
output logic timer_intr_o
|
|
);
|
|
|
|
// The timers are always 64 bits
|
|
localparam int unsigned TW = 64;
|
|
// Upper bits of address are decoded into timer_req_i
|
|
localparam int unsigned ADDR_OFFSET = 10; // 1kB
|
|
// Register map
|
|
localparam bit [9:0] MTIME_LOW = 0;
|
|
localparam bit [9:0] MTIME_HIGH = 4;
|
|
localparam bit [9:0] MTIMECMP_LOW = 8;
|
|
localparam bit [9:0] MTIMECMP_HIGH = 12;
|
|
|
|
logic timer_we;
|
|
logic mtime_we, mtimeh_we;
|
|
logic mtimecmp_we, mtimecmph_we;
|
|
logic [DataWidth-1:0] mtime_wdata, mtimeh_wdata;
|
|
logic [DataWidth-1:0] mtimecmp_wdata, mtimecmph_wdata;
|
|
logic [TW-1:0] mtime_q, mtime_d, mtime_inc;
|
|
logic [TW-1:0] mtimecmp_q, mtimecmp_d;
|
|
logic interrupt_q, interrupt_d;
|
|
logic error_q, error_d;
|
|
logic [DataWidth-1:0] rdata_q, rdata_d;
|
|
logic rvalid_q;
|
|
|
|
// Global write enable for all registers
|
|
assign timer_we = timer_req_i & timer_we_i;
|
|
|
|
// mtime increments every cycle
|
|
assign mtime_inc = mtime_q + 64'd1;
|
|
|
|
// Generate write data based on byte strobes
|
|
for (genvar b = 0; b < DataWidth / 8; b++) begin : gen_byte_wdata
|
|
|
|
assign mtime_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
|
|
mtime_q[(b*8)+:8];
|
|
assign mtimeh_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
|
|
mtime_q[DataWidth+(b*8)+:8];
|
|
assign mtimecmp_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
|
|
mtimecmp_q[(b*8)+:8];
|
|
assign mtimecmph_wdata[(b*8)+:8] = timer_be_i[b] ? timer_wdata_i[b*8+:8] :
|
|
mtimecmp_q[DataWidth+(b*8)+:8];
|
|
end
|
|
|
|
// Generate write enables
|
|
assign mtime_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIME_LOW);
|
|
assign mtimeh_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIME_HIGH);
|
|
assign mtimecmp_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIMECMP_LOW);
|
|
assign mtimecmph_we = timer_we & (timer_addr_i[ADDR_OFFSET-1:0] == MTIMECMP_HIGH);
|
|
|
|
// Generate next data
|
|
assign mtime_d = {(mtimeh_we ? mtimeh_wdata : mtime_inc[63:32]),
|
|
(mtime_we ? mtime_wdata : mtime_inc[31:0])};
|
|
assign mtimecmp_d = {(mtimecmph_we ? mtimecmph_wdata : mtimecmp_q[63:32]),
|
|
(mtimecmp_we ? mtimecmp_wdata : mtimecmp_q[31:0])};
|
|
|
|
// Generate registers
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
mtime_q <= 'b0;
|
|
end else begin
|
|
mtime_q <= mtime_d;
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
mtimecmp_q <= 'b0;
|
|
end else if (mtimecmp_we | mtimecmph_we) begin
|
|
mtimecmp_q <= mtimecmp_d;
|
|
end
|
|
end
|
|
|
|
// interrupt remains set until mtimecmp is written
|
|
assign interrupt_d = ((mtime_q >= mtimecmp_q) | interrupt_q) & ~(mtimecmp_we | mtimecmph_we);
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
interrupt_q <= 'b0;
|
|
end else begin
|
|
interrupt_q <= interrupt_d;
|
|
end
|
|
end
|
|
|
|
assign timer_intr_o = interrupt_q;
|
|
|
|
// Read data
|
|
always_comb begin
|
|
rdata_d = 'b0;
|
|
error_d = 1'b0;
|
|
unique case (timer_addr_i[ADDR_OFFSET-1:0])
|
|
MTIME_LOW: rdata_d = mtime_q[31:0];
|
|
MTIME_HIGH: rdata_d = mtime_q[63:32];
|
|
MTIMECMP_LOW: rdata_d = mtimecmp_q[31:0];
|
|
MTIMECMP_HIGH: rdata_d = mtimecmp_q[63:32];
|
|
default: begin
|
|
rdata_d = 'b0;
|
|
// Error if no address matched
|
|
error_d = 1'b1;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
// error_q and rdata_q are only valid when rvalid_q is high
|
|
always_ff @(posedge clk_i) begin
|
|
if (timer_req_i) begin
|
|
rdata_q <= rdata_d;
|
|
error_q <= error_d;
|
|
end
|
|
end
|
|
|
|
assign timer_rdata_o = rdata_q;
|
|
|
|
// Read data is always valid one cycle after a request
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
rvalid_q <= 1'b0;
|
|
end else begin
|
|
rvalid_q <= timer_req_i;
|
|
end
|
|
end
|
|
|
|
assign timer_rvalid_o = rvalid_q;
|
|
assign timer_err_o = error_q;
|
|
|
|
// Assertions
|
|
`ASSERT_INIT(param_legal, DataWidth == 32)
|
|
endmodule
|