Wishbone CLINT Slave - Teknik Dokümantasyon¶
İçindekiler¶
Genel Bakış¶
Amaç¶
wb_clint_slave modülü, RISC-V Core Local Interruptor (CLINT) fonksiyonalitesini Wishbone bus arayüzüyle sunar. Timer interrupt ve software interrupt yönetimi sağlar.
Dosya Konumu¶
CLINT Blok Diyagramı¶
┌─────────────────────────────────────────────────────────────────────────────────┐
│ WB_CLINT_SLAVE │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ Wishbone Bus CLINT Registers │
│ │
│ ┌────────────┐ ┌─────────────────┐ │
│ │ cyc/stb │───────────────────────►│ MSIP[0] │──────► sw_irq │
│ │ we │ │ (0x0000) │ │
│ │ addr │ └─────────────────┘ │
│ │ wdata │ │
│ │ sel │ ┌─────────────────┐ │
│ └────────────┘ │ MTIMECMP[0] │ │
│ │ (0x4000-07) │ │
│ ┌────────────┐ └────────┬────────┘ │
│ │ ack │◄───────────────────┐ │ │
│ │ rdata │ │ │ compare │
│ │ err │ │ ▼ │
│ └────────────┘ │ ┌───────────────┐ │
│ │ │ >= │──────► timer_irq │
│ │ └───────┬───────┘ │
│ │ │ │
│ │ ┌───────┴───────┐ │
│ └───┤ MTIME │◄────── clk (+1/cyc) │
│ │ (0xBFF8-FF) │ │
│ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
Modül Arayüzü¶
Parametreler¶
module wb_clint_slave
import ceres_param::*;
#(
parameter int NUM_HARTS = 1 // Number of harts (cores)
)
Port Tanımları¶
(
input logic clk_i,
input logic rst_ni,
// Wishbone Slave Interface
input wb_master_t wb_m_i, // Master request
output wb_slave_t wb_s_o, // Slave response
// Interrupt Outputs
output logic timer_irq_o, // Timer interrupt
output logic sw_irq_o // Software interrupt
);
Wishbone Struct Detayları¶
// wb_master_t (from ceres_param)
typedef struct packed {
logic cyc; // Bus cycle active
logic stb; // Strobe (valid transfer)
logic we; // Write enable
logic [31:0] adr; // Address
logic [31:0] dat; // Write data
logic [3:0] sel; // Byte select
logic [2:0] cti; // Cycle type identifier
logic [1:0] bte; // Burst type extension
} wb_master_t;
// wb_slave_t (from ceres_param)
typedef struct packed {
logic ack; // Acknowledge
logic err; // Error
logic rty; // Retry
logic [31:0] dat; // Read data
} wb_slave_t;
Register Map¶
CLINT Address Layout (SiFive Spec)¶
┌──────────────────────────────────────────────────────────────────────────┐
│ CLINT REGISTER MAP │
├────────────────┬──────────┬───────────────────────────────────────────────┤
│ Offset │ Width │ Description │
├────────────────┼──────────┼───────────────────────────────────────────────┤
│ 0x0000 │ 4 bytes │ MSIP[0] - Software interrupt pending │
│ 0x0004 │ 4 bytes │ MSIP[1] - (multi-hart only) │
│ ... │ │ │
├────────────────┼──────────┼───────────────────────────────────────────────┤
│ 0x4000 │ 8 bytes │ MTIMECMP[0] - Timer compare value │
│ 0x4008 │ 8 bytes │ MTIMECMP[1] - (multi-hart only) │
│ ... │ │ │
├────────────────┼──────────┼───────────────────────────────────────────────┤
│ 0xBFF8 │ 8 bytes │ MTIME - Timer counter (shared) │
└────────────────┴──────────┴───────────────────────────────────────────────┘
Register Definitions¶
// Address offsets
localparam logic [15:0] MSIP_OFFSET = 16'h0000;
localparam logic [15:0] MTIMECMP_OFFSET = 16'h4000;
localparam logic [15:0] MTIME_OFFSET = 16'hBFF8;
// CLINT Registers
logic [63:0] mtime_q; // 64-bit free-running counter
logic [63:0] mtimecmp_q; // 64-bit timer compare
logic msip_q; // Software interrupt pending
Timer Implementasyonu¶
MTIME Counter¶
// Free-running 64-bit timer
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
mtime_q <= '0;
end else begin
mtime_q <= mtime_q + 1;
end
end
MTIME Yazma¶
// mtime yazılabilir (debugging/test için)
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
mtime_q <= '0;
end else if (wb_req && wb_we && (addr_offset == MTIME_OFFSET)) begin
mtime_q[31:0] <= wb_m_i.dat;
end else if (wb_req && wb_we && (addr_offset == MTIME_OFFSET + 4)) begin
mtime_q[63:32] <= wb_m_i.dat;
end else begin
mtime_q <= mtime_q + 1;
end
end
MTIMECMP Register¶
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
mtimecmp_q <= '1; // Max value (interrupt disabled at reset)
end else if (wb_req && wb_we) begin
case (addr_offset)
MTIMECMP_OFFSET: mtimecmp_q[31:0] <= wb_m_i.dat;
MTIMECMP_OFFSET + 4: mtimecmp_q[63:32] <= wb_m_i.dat;
endcase
end
end
Wishbone Protokol¶
Request Detection¶
wire wb_req = wb_m_i.cyc && wb_m_i.stb;
wire wb_we = wb_m_i.we;
wire [15:0] addr_offset = wb_m_i.adr[15:0];
Read Logic¶
logic [31:0] rdata;
always_comb begin
rdata = '0;
case (addr_offset)
// MSIP
MSIP_OFFSET: begin
rdata = {31'b0, msip_q};
end
// MTIMECMP (lower 32 bits)
MTIMECMP_OFFSET: begin
rdata = mtimecmp_q[31:0];
end
// MTIMECMP (upper 32 bits)
MTIMECMP_OFFSET + 4: begin
rdata = mtimecmp_q[63:32];
end
// MTIME (lower 32 bits)
MTIME_OFFSET: begin
rdata = mtime_q[31:0];
end
// MTIME (upper 32 bits)
MTIME_OFFSET + 4: begin
rdata = mtime_q[63:32];
end
default: begin
rdata = '0;
end
endcase
end
Write Logic¶
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
msip_q <= 1'b0;
mtimecmp_q <= '1;
end else if (wb_req && wb_we) begin
case (addr_offset)
MSIP_OFFSET: begin
msip_q <= wb_m_i.dat[0];
end
MTIMECMP_OFFSET: begin
mtimecmp_q[31:0] <= wb_m_i.dat;
end
MTIMECMP_OFFSET + 4: begin
mtimecmp_q[63:32] <= wb_m_i.dat;
end
endcase
end
end
Response Generation¶
// Single-cycle response
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
wb_s_o.ack <= 1'b0;
wb_s_o.dat <= '0;
end else begin
wb_s_o.ack <= wb_req;
wb_s_o.dat <= rdata;
end
end
assign wb_s_o.err = 1'b0;
assign wb_s_o.rty = 1'b0;
Interrupt Üretimi¶
Timer Interrupt¶
Software Interrupt¶
Interrupt Clearing¶
// Timer interrupt: mtimecmp'ye mtime'dan büyük değer yazarak
// Software interrupt: msip'e 0 yazarak
Timing Diyagramı¶
Timer Interrupt Sequence¶
┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
clk_i ───┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └───
mtime_q ────┬───┬───┬───┬───┬───┬───┬───┬───
│100│101│102│103│104│105│106│107│
mtimecmp_q ────────────────────────────────────
104
timer_irq_o ┌──────────────
─────────────────────┘
mtime >= mtimecmp
MSIP Write Sequence¶
┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
clk_i ───┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └───
wb_m_i.cyc ┌───────┐ ┌───────┐
────┘ └───────────┘ └───
wb_m_i.stb ┌───────┐ ┌───────┐
────┘ └───────────┘ └───
wb_m_i.we ┌───────┐ ┌───────┐
────┘ └───────────┘ └───
wb_m_i.adr ────┤0x0000 ├───────────┤0x0000 ├───
wb_m_i.dat ────┤ 0x1 ├───────────┤ 0x0 ├───
wb_s_o.ack ┌───┐ ┌───┐
────────┘ └───────────────┘ └───
msip_q ┌───────────────────┐
────────┘ └───────
sw_irq_o ┌───────────────────┐
────────┘ └───────
Software Usage¶
C Header¶
#ifndef _CLINT_H_
#define _CLINT_H_
#include <stdint.h>
#define CLINT_BASE 0x30000000
#define CLINT_MSIP (*(volatile uint32_t *)(CLINT_BASE + 0x0000))
#define CLINT_MTIMECMP (*(volatile uint64_t *)(CLINT_BASE + 0x4000))
#define CLINT_MTIME (*(volatile uint64_t *)(CLINT_BASE + 0xBFF8))
// Convenience macros for 32-bit access
#define CLINT_MTIMECMP_LO (*(volatile uint32_t *)(CLINT_BASE + 0x4000))
#define CLINT_MTIMECMP_HI (*(volatile uint32_t *)(CLINT_BASE + 0x4004))
#define CLINT_MTIME_LO (*(volatile uint32_t *)(CLINT_BASE + 0xBFF8))
#define CLINT_MTIME_HI (*(volatile uint32_t *)(CLINT_BASE + 0xBFFC))
#endif
Timer Interrupt Setup¶
void timer_init(uint64_t interval) {
// Read current time
uint64_t now = CLINT_MTIME;
// Set compare value
// Write high word first to avoid spurious interrupt
CLINT_MTIMECMP_HI = 0xFFFFFFFF;
CLINT_MTIMECMP_LO = (uint32_t)(now + interval);
CLINT_MTIMECMP_HI = (uint32_t)((now + interval) >> 32);
}
void timer_handler(void) {
// Reschedule next interrupt
uint64_t next = CLINT_MTIMECMP + TIMER_INTERVAL;
CLINT_MTIMECMP_HI = 0xFFFFFFFF;
CLINT_MTIMECMP_LO = (uint32_t)next;
CLINT_MTIMECMP_HI = (uint32_t)(next >> 32);
}
Software Interrupt¶
void trigger_sw_interrupt(void) {
CLINT_MSIP = 1;
}
void clear_sw_interrupt(void) {
CLINT_MSIP = 0;
}
Özet¶
wb_clint_slave modülü:
- MTIME: 64-bit free-running timer
- MTIMECMP: 64-bit timer compare
- MSIP: Software interrupt pending
- Wishbone: Single-cycle response
- Interrupts: Timer (mtime >= mtimecmp), SW (msip)
Bu modül, RISC-V standardına uygun CLINT fonksiyonalitesi sağlar.