Skip to content

Commit

Permalink
Add ITLB and PSW CSR register and a little bit of refactoring
Browse files Browse the repository at this point in the history
fallen committed Jul 22, 2012
1 parent 2c801a4 commit 70b5f48
Showing 17 changed files with 1,205 additions and 267 deletions.
18 changes: 18 additions & 0 deletions boards/milkymist-one/rtl/lm32_include.v
Original file line number Diff line number Diff line change
@@ -98,6 +98,7 @@
`define CFG_GDBSTUB_ENABLED
//`define CFG_RANDOM_WISHBONE_LATENCY
//`define CFG_VERBOSE_DISPLAY_ENABLED
//`define CFG_PIPELINE_TRACES

// Enable MMU
`define CFG_MMU_ENABLED
@@ -292,12 +293,28 @@
`define LM32_CSR_WP3 `LM32_CSR_WIDTH'h1b
`endif
`ifdef CFG_MMU_ENABLED
`define LM32_CSR_PSW `LM32_CSR_WIDTH'hb
`define LM32_CSR_TLB_CTRL `LM32_CSR_WIDTH'h1c
`define LM32_CSR_TLB_VADDRESS `LM32_CSR_WIDTH'h1d
`define LM32_CSR_TLB_PADDRESS `LM32_CSR_WIDTH'h1e
`define LM32_CSR_TLB_DBG `LM32_CSR_WIDTH'h1f
`endif

`ifdef CFG_MMU_ENABLED
`define LM32_CSR_PSW_IE `LM32_WORD_WIDTH'h0
`define LM32_CSR_PSW_EIE `LM32_WORD_WIDTH'h1
`define LM32_CSR_PSW_BIE `LM32_WORD_WIDTH'h2
`define LM32_CSR_PSW_ITLBE `LM32_WORD_WIDTH'h3
`define LM32_CSR_PSW_EITLBE `LM32_WORD_WIDTH'h4
`define LM32_CSR_PSW_BITLBE `LM32_WORD_WIDTH'h5
`define LM32_CSR_PSW_DTLBE `LM32_WORD_WIDTH'h6
`define LM32_CSR_PSW_EDTLBE `LM32_WORD_WIDTH'h7
`define LM32_CSR_PSW_BDTLBE `LM32_WORD_WIDTH'h8
`define LM32_CSR_PSW_USR `LM32_WORD_WIDTH'h9
`define LM32_CSR_PSW_EUSR `LM32_WORD_WIDTH'ha
`define LM32_CSR_PSW_BUSR `LM32_WORD_WIDTH'hb
`endif

// Values for WPC CSR
`define LM32_WPC_C_RNG 1:0
`define LM32_WPC_C_DISABLED 2'b00
@@ -317,6 +334,7 @@
`define LM32_EID_INTERRUPT `LM32_EID_WIDTH'h6
`define LM32_EID_SCALL `LM32_EID_WIDTH'h7
`define LM32_EID_DTLB_MISS `LM32_EID_WIDTH'h8
`define LM32_EID_ITLB_MISS `LM32_EID_WIDTH'h9

// Pipeline result selection mux controls

129 changes: 120 additions & 9 deletions cores/lm32/rtl/lm32_cpu.v
Original file line number Diff line number Diff line change
@@ -616,7 +616,8 @@ wire mc_stall_request_x; // Multi-cycle arithmetic unit s
wire [`LM32_WORD_RNG] mc_result_x;
`endif

wire [`LM32_WORD_RNG] load_store_csr_read_data_x;// Data read from load store CSRs
wire [`LM32_WORD_RNG] load_store_csr_read_data_x;// Data read from load store unit CSRs
wire [`LM32_WORD_RNG] instruction_csr_read_data_x;// Data read from instruction unit CSRs
// From CSRs
`ifdef CFG_INTERRUPTS_ENABLED
wire [`LM32_WORD_RNG] interrupt_csr_read_data_x;// Data read from interrupt CSRs
@@ -629,11 +630,32 @@ reg [`LM32_WORD_RNG] cc; // Cycle counter CSR
reg [`LM32_WORD_RNG] csr_read_data_x; // Data read from CSRs

// To/from instruction unit
`ifdef CFG_PIPELINE_TRACES
wire [`LM32_PC_RNG] pc_a;
`endif
wire [`LM32_PC_RNG] pc_f; // PC of instruction in F stage
wire [`LM32_PC_RNG] pc_d; // PC of instruction in D stage
wire [`LM32_PC_RNG] pc_x; // PC of instruction in X stage
wire [`LM32_PC_RNG] pc_m; // PC of instruction in M stage
wire [`LM32_PC_RNG] pc_w; // PC of instruction in W stage

`ifdef CFG_PIPELINE_TRACES
always @(posedge clk_i `CFG_RESET_SENSITIVITY)
begin
if (~rst_i)
begin
if (~stall_a)
$display("[%t] Addressing inst @ 0x%08X", $time, pc_a);
if (~stall_f)
$display("[%t] Fetching inst @ 0x%08X", $time, pc_f);
if (~stall_d)
$display("[%t] Decoding inst @ 0x%08X", $time, pc_d);
if (~stall_x)
$display("[%t] Executing inst @ 0x%08X", $time, pc_x);
end
end
`endif

`ifdef CFG_TRACE_ENABLED
reg [`LM32_PC_RNG] pc_c; // PC of last commited instruction
`endif
@@ -777,6 +799,12 @@ reg data_bus_error_seen; // Indicates if a data bus error
reg ext_break_r;
`endif

`ifdef CFG_MMU_ENABLED
wire dtlb_miss_exception;
wire itlb_miss_exception;
reg [`LM32_WORD_RNG] lm32_csr_psw_reg;
`endif

/////////////////////////////////////////////////////
// Functions
/////////////////////////////////////////////////////
@@ -819,6 +847,9 @@ lm32_instruction_unit #(
.branch_target_x (branch_target_x),
`endif
.exception_m (exception_m),
`ifdef CFG_MMU_ENABLED
.exception_x (exception_x),
`endif
.branch_taken_m (branch_taken_m),
.branch_mispredict_taken_m (branch_mispredict_taken_m),
.branch_target_m (branch_target_m),
@@ -834,7 +865,15 @@ lm32_instruction_unit #(
.dcache_restart_request (dcache_restart_request),
.dcache_refill_request (dcache_refill_request),
.dcache_refilling (dcache_refilling),
`endif
`endif
`ifdef CFG_MMU_ENABLED
.csr (csr_x),
.csr_write_data (operand_1_x),
.csr_write_enable (csr_write_enable_q_x),
.eret_q_x (eret_q_x),
.csr_psw (lm32_csr_psw_reg),
.q_x (q_x),
`endif
`ifdef CFG_IWB_ENABLED
// From Wishbone
.i_dat_i (I_DAT_I),
@@ -849,6 +888,9 @@ lm32_instruction_unit #(
`endif
// ----- Outputs -------
// To pipeline
`ifdef CFG_PIPELINE_TRACES
.pc_a (pc_a),
`endif
.pc_f (pc_f),
.pc_d (pc_d),
.pc_x (pc_x),
@@ -863,6 +905,10 @@ lm32_instruction_unit #(
`ifdef CFG_IROM_ENABLED
.irom_data_m (irom_data_m),
`endif
`ifdef CFG_MMU_ENABLED
.itlb_miss (itlb_miss_exception),
.csr_read_data (instruction_csr_read_data_x),
`endif
`ifdef CFG_IWB_ENABLED
// To Wishbone
.i_dat_o (I_DAT_O),
@@ -967,8 +1013,6 @@ lm32_decoder decoder (
.csr_write_enable (csr_write_enable_d)
);

wire dtlb_miss_exception;

// Load/store unit
lm32_load_store_unit #(
.associativity (dcache_associativity),
@@ -1006,10 +1050,13 @@ lm32_load_store_unit #(
`ifdef CFG_IROM_ENABLED
.irom_data_m (irom_data_m),
`endif
`ifdef CFG_MMU_ENABLED
.csr (csr_x),
.csr_write_data (operand_1_x),
.csr_write_enable (csr_write_enable_q_x),
.eret_q_x (eret_q_x),
.csr_psw (lm32_csr_psw_reg),
`endif
// From Wishbone
.d_dat_i (D_DAT_I),
.d_ack_i (D_ACK_I),
@@ -1031,8 +1078,10 @@ lm32_load_store_unit #(
`endif
.load_data_w (load_data_w),
.stall_wb_load (stall_wb_load),
`ifdef CFG_MMU_ENABLED
.dtlb_miss (dtlb_miss_exception),
.csr_read_data (load_store_csr_read_data_x),
`endif
// To Wishbone
.d_dat_o (D_DAT_O),
.d_adr_o (D_ADR_O),
@@ -1771,7 +1820,7 @@ assign non_debug_exception_x = (system_call_exception == `TRUE)
)
`endif
`ifdef CFG_MMU_ENABLED
|| (dtlb_miss_exception == `TRUE)
|| (dtlb_miss_exception == `TRUE || itlb_miss_exception == `TRUE)
`endif
;

@@ -1797,7 +1846,7 @@ assign exception_x = (system_call_exception == `TRUE)
)
`endif
`ifdef CFG_MMU_ENABLED
|| (dtlb_miss_exception == `TRUE)
|| (dtlb_miss_exception == `TRUE || itlb_miss_exception == `TRUE)
`endif
;
`endif
@@ -1847,9 +1896,13 @@ begin
eid_x = `LM32_EID_INTERRUPT;
else
`endif
if (dtlb_miss_exception == `TRUE )
`ifdef CFG_MMU_ENABLED
if (dtlb_miss_exception == `TRUE)
eid_x = `LM32_EID_DTLB_MISS;
else if (itlb_miss_exception == `TRUE)
eid_x = `LM32_EID_ITLB_MISS;
else
`endif
eid_x = `LM32_EID_SCALL;
end

@@ -2144,11 +2197,69 @@ begin
`endif
`LM32_CSR_CFG2: csr_read_data_x = cfg2;
`LM32_CSR_TLB_VADDRESS: csr_read_data_x = load_store_csr_read_data_x;

`LM32_CSR_TLB_PADDRESS: csr_read_data_x = instruction_csr_read_data_x;
`LM32_CSR_PSW: csr_read_data_x = lm32_csr_psw_reg;
default: csr_read_data_x = {`LM32_WORD_WIDTH{1'bx}};
endcase
end

`ifdef CFG_MMU_ENABLED

always @(posedge clk_i `CFG_RESET_SENSITIVITY)
begin
if (rst_i)
begin
lm32_csr_psw_reg <= `LM32_WORD_WIDTH'h0;
end
else
begin
`ifdef CFG_DEBUG_ENABLED
if (non_debug_exception_q_w == `TRUE)
begin
// Save and then clear ITLB enable
lm32_csr_psw_reg[`LM32_CSR_PSW_EITLBE] <= lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE];
lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE] <= `FALSE;
end
else if (debug_exception_q_w == `TRUE)
begin
// Save and then clear TLB enable
lm32_csr_psw_reg[`LM32_CSR_PSW_BITLBE] <= lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE];
lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE] <= `FALSE;
end
`else
if (exception_q_w == `TRUE)
begin
// Save and then clear ITLB enable
lm32_csr_psw_reg[`LM32_CSR_PSW_EITLBE] <= lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE];
lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE] <= `FALSE;
end
`endif
else if (stall_x == `FALSE)
begin
if (eret_q_x == `TRUE)
// Restore ITLB enable
lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE] <= lm32_csr_psw_reg[`LM32_CSR_PSW_EITLBE];
`ifdef CFG_DEBUG_ENABLED
else if (bret_q_x == `TRUE)
// Restore ITLB enable
lm32_csr_psw_reg[`LM32_CSR_PSW_ITLBE] <= lm32_csr_psw_reg[`LM32_CSR_PSW_BITLBE];
`endif
else if (csr_write_enable_q_x == `TRUE)
begin
// Handle wcsr write
case (csr_x)
// operand_1_x is csr_write_data
`LM32_CSR_PSW:
lm32_csr_psw_reg <= operand_1_x;
`LM32_CSR_IE:
lm32_csr_psw_reg[2:0] <= operand_1_x[2:0];
endcase
end
end
end
end
`endif

/////////////////////////////////////////////////////
// Sequential Logic
/////////////////////////////////////////////////////
@@ -2231,7 +2342,7 @@ begin
end
`endif

// Valid bits to indicate whether an instruction in a partcular pipeline stage is valid or not
// Valid bits to indicate whether an instruction in a particular pipeline stage is valid or not

`ifdef CFG_ICACHE_ENABLED
`ifdef CFG_DCACHE_ENABLED
355 changes: 184 additions & 171 deletions cores/lm32/rtl/lm32_dcache.v

Large diffs are not rendered by default.

448 changes: 441 additions & 7 deletions cores/lm32/rtl/lm32_icache.v

Large diffs are not rendered by default.

115 changes: 113 additions & 2 deletions cores/lm32/rtl/lm32_instruction_unit.v
Original file line number Diff line number Diff line change
@@ -99,6 +99,10 @@ module lm32_instruction_unit (
branch_target_x,
`endif
exception_m,
`ifdef CFG_MMU_ENABLED
exception_x,
csr_psw,
`endif
branch_taken_m,
branch_mispredict_taken_m,
branch_target_m,
@@ -115,6 +119,13 @@ module lm32_instruction_unit (
irom_address_xm,
irom_we_xm,
`endif
`ifdef CFG_MMU_ENABLED
csr,
csr_write_data,
csr_write_enable,
eret_q_x,
q_x,
`endif
`ifdef CFG_IWB_ENABLED
// From Wishbone
i_dat_i,
@@ -129,6 +140,9 @@ module lm32_instruction_unit (
`endif
// ----- Outputs -------
// To pipeline
`ifdef CFG_PIPELINE_TRACES
pc_a,
`endif
pc_f,
pc_d,
pc_x,
@@ -143,6 +157,10 @@ module lm32_instruction_unit (
`ifdef CFG_IROM_ENABLED
irom_data_m,
`endif
`ifdef CFG_MMU_ENABLED
itlb_miss,
csr_read_data,
`endif
`ifdef CFG_IWB_ENABLED
// To Wishbone
i_dat_o,
@@ -245,6 +263,16 @@ input [`LM32_BYTE_RNG] jtag_write_data; // JTAG wrirte data
input [`LM32_WORD_RNG] jtag_address; // JTAG read/write address
`endif

`ifdef CFG_MMU_ENABLED
input exception_x; // An exception occured in the X stage
input eret_q_x;
input q_x;
input [`LM32_CSR_RNG] csr; // CSR read/write index
input [`LM32_WORD_RNG] csr_write_data; // Data to write to specified CSR
input csr_write_enable; // CSR write enable
input [`LM32_WORD_RNG] csr_psw;
`endif

/////////////////////////////////////////////////////
// Outputs
/////////////////////////////////////////////////////
@@ -327,11 +355,21 @@ wire [`LM32_INSTRUCTION_RNG] instruction_f;
output [`LM32_INSTRUCTION_RNG] instruction_d; // D stage instruction to be decoded
reg [`LM32_INSTRUCTION_RNG] instruction_d;

`ifdef CFG_MMU_ENABLED
output csr_read_data;
wire [`LM32_WORD_RNG] csr_read_data;
output itlb_miss;
wire itlb_miss;
`endif

/////////////////////////////////////////////////////
// Internal nets and registers
/////////////////////////////////////////////////////

reg [`LM32_PC_RNG] pc_a; // A stage PC
`ifdef CFG_PIPELINE_TRACES
output [`LM32_PC_RNG] pc_a;
`endif

`ifdef LM32_CACHE_ENABLED
reg [`LM32_PC_RNG] restart_address; // Address to restart from after a cache miss
@@ -340,6 +378,9 @@ reg [`LM32_PC_RNG] restart_address; // Address to restart fr
`ifdef CFG_ICACHE_ENABLED
wire icache_read_enable_f; // Indicates if instruction cache miss is valid
wire [`LM32_PC_RNG] icache_refill_address; // Address that caused cache miss
`ifdef CFG_MMU_ENABLED
wire [`LM32_PC_RNG] icache_physical_refill_address; // Physical address that caused cache miss
`endif
reg icache_refill_ready; // Indicates when next word of refill data is ready to be written to cache
reg [`LM32_INSTRUCTION_RNG] icache_refill_data; // Next word of refill data, fetched from Wishbone
wire [`LM32_INSTRUCTION_RNG] icache_data_f; // Instruction fetched from instruction cache
@@ -373,6 +414,11 @@ reg jtag_access; // Indicates if a JTAG W
reg alternate_eba_taken;
`endif

`ifdef CFG_MMU_ENABLED
wire [`LM32_WORD_RNG] physical_address;
wire kernel_mode;
`endif

/////////////////////////////////////////////////////
// Functions
/////////////////////////////////////////////////////
@@ -446,20 +492,48 @@ lm32_icache #(
.rst_i (rst_i),
.stall_a (stall_a),
.stall_f (stall_f),
`ifdef CFG_MMU_ENABLED
.stall_x (stall_x),
.stall_m (stall_m),
`endif
.branch_predict_taken_d (branch_predict_taken_d),
.valid_d (valid_d),
.address_a (pc_a),
.address_f (pc_f),
`ifdef CFG_MMU_ENABLED
.pc_x (pc_x),
.pc_m (pc_m),
.pc_w (pc_w),
`endif
.read_enable_f (icache_read_enable_f),
.refill_ready (icache_refill_ready),
.refill_data (icache_refill_data),
.iflush (iflush),
`ifdef CFG_MMU_ENABLED
.csr (csr),
.csr_write_data (csr_write_data),
.csr_write_enable (csr_write_enable),
.csr_psw (csr_psw),
.exception_x (exception_x),
.eret_q_x (eret_q_x),
.exception_m (exception_m),
.q_x (q_x),
`endif
// ----- Outputs -----
.stall_request (icache_stall_request),
.restart_request (icache_restart_request),
.refill_request (icache_refill_request),
.refill_address (icache_refill_address),
`ifdef CFG_MMU_ENABLED
.physical_refill_address (icache_physical_refill_address),
`endif
.refilling (icache_refilling),
`ifdef CFG_MMU_ENABLED
.itlb_miss_int (itlb_miss),
.kernel_mode (kernel_mode),
.pa (physical_address),
.csr_read_data (csr_read_data),
`endif
.inst (icache_data_f)
);
`endif
@@ -487,27 +561,52 @@ begin
// The request from the latest pipeline stage must take priority
`ifdef CFG_DCACHE_ENABLED
if (dcache_restart_request == `TRUE)
begin
`ifdef CFG_PIPELINE_TRACES
$display("[%t] We restart to 0x%08X because of DCACHE", $time, restart_address);
`endif
pc_a = restart_address;
end
else
`endif
if (branch_taken_m == `TRUE)
if ((branch_mispredict_taken_m == `TRUE) && (exception_m == `FALSE))
begin
`ifdef CFG_PIPELINE_TRACES
$display("[%t] Mispredict, goto pc_x == 0x%08X", $time, pc_x);
`endif
pc_a = pc_x;
end
else
begin
`ifdef CFG_PIPELINE_TRACES
$display("[%t] Correctly predicted, goto branch_target_m == 0x%08X", $time, branch_target_m);
`endif
pc_a = branch_target_m;
`ifdef CFG_FAST_UNCONDITIONAL_BRANCH
end
`ifdef CFG_FAST_UNCONDITIONAL_BRANCH
else if (branch_taken_x == `TRUE)
pc_a = branch_target_x;
`endif
else
if ( (valid_d == `TRUE) && (branch_predict_taken_d == `TRUE) )
begin
`ifdef CFG_PIPELINE_TRACES
$display("[%t] We go to branch_predict_address_d == 0x%08X", $time, branch_predict_address_d);
`endif
pc_a = branch_predict_address_d;
end
else
`ifdef CFG_ICACHE_ENABLED
if (icache_restart_request == `TRUE)
begin
`ifdef CFG_PIPELINE_TRACES
$display("[%t] We restart to 0x%08X because of ICACHE", $time, restart_address);
`endif
pc_a = restart_address;
end
else
`endif
`endif
pc_a = pc_f + 1'b1;
end

@@ -555,21 +654,33 @@ generate
assign first_cycle_type = `LM32_CTYPE_END;
assign next_cycle_type = `LM32_CTYPE_END;
assign last_word = `TRUE;
`ifdef CFG_MMU_ENABLED
assign first_address = icache_physical_refill_address;
`else
assign first_address = icache_refill_address;
`endif
end
8:
begin
assign first_cycle_type = `LM32_CTYPE_INCREMENTING;
assign next_cycle_type = `LM32_CTYPE_END;
assign last_word = i_adr_o[addr_offset_msb:addr_offset_lsb] == 1'b1;
`ifdef CFG_MMU_ENABLED
assign first_address = {icache_physical_refill_address[`LM32_PC_WIDTH+2-1:addr_offset_msb+1], {addr_offset_width{1'b0}}};
`else
assign first_address = {icache_refill_address[`LM32_PC_WIDTH+2-1:addr_offset_msb+1], {addr_offset_width{1'b0}}};
`endif
end
16:
begin
assign first_cycle_type = `LM32_CTYPE_INCREMENTING;
assign next_cycle_type = i_adr_o[addr_offset_msb] == 1'b1 ? `LM32_CTYPE_END : `LM32_CTYPE_INCREMENTING;
assign last_word = i_adr_o[addr_offset_msb:addr_offset_lsb] == 2'b11;
`ifdef CFG_MMU_ENABLED
assign first_address = {icache_physical_refill_address[`LM32_PC_WIDTH+2-1:addr_offset_msb+1], {addr_offset_width{1'b0}}};
`else
assign first_address = {icache_refill_address[`LM32_PC_WIDTH+2-1:addr_offset_msb+1], {addr_offset_width{1'b0}}};
`endif
end
endcase
endgenerate
2 changes: 1 addition & 1 deletion cores/lm32/rtl/lm32_interrupt.v
Original file line number Diff line number Diff line change
@@ -266,7 +266,7 @@ begin
else if (csr_write_enable == `TRUE)
begin
// Handle wcsr write
if (csr == `LM32_CSR_IE)
if ((csr == `LM32_CSR_IE) || (csr == `LM32_CSR_PSW))
begin
ie <= csr_write_data[0];
eie <= csr_write_data[1];
50 changes: 38 additions & 12 deletions cores/lm32/rtl/lm32_load_store_unit.v
Original file line number Diff line number Diff line change
@@ -99,10 +99,13 @@ module lm32_load_store_unit (
`ifdef CFG_IROM_ENABLED
irom_data_m,
`endif
`ifdef CFG_MMU_ENABLED
csr,
csr_write_data,
csr_write_enable,
eret_q_x,
csr_psw,
`endif
// From Wishbone
d_dat_i,
d_ack_i,
@@ -124,8 +127,10 @@ module lm32_load_store_unit (
`endif
load_data_w,
stall_wb_load,
`ifdef CFG_MMU_ENABLED
dtlb_miss,
csr_read_data,
`endif
// To Wishbone
d_dat_o,
d_adr_o,
@@ -169,11 +174,12 @@ input exception_m; // An exception occured
input exception_x; // An exception occured in the X stage
input eret_q_x;


`ifdef CFG_MMU_ENABLED
input [`LM32_CSR_RNG] csr; // CSR read/write index
input [`LM32_WORD_RNG] csr_write_data; // Data to write to specified CSR
input csr_write_enable; // CSR write enable

input [`LM32_WORD_RNG] csr_psw;
`endif

input [`LM32_WORD_RNG] store_operand_x; // Data read from register to store
input [`LM32_WORD_RNG] load_store_address_x; // X stage load/store address
@@ -196,8 +202,10 @@ input dflush; // Flush the data cache
input [`LM32_WORD_RNG] irom_data_m; // Data from Instruction-ROM
`endif

`ifdef CFG_MMU_ENABLED
wire dtlb_miss;
output dtlb_miss;
`endif

input [`LM32_WORD_RNG] d_dat_i; // Data Wishbone interface read data
input d_ack_i; // Data Wishbone interface acknowledgement
@@ -219,8 +227,10 @@ output dcache_refilling;
wire dcache_refilling;
`endif

`ifdef CFG_MMU_ENABLED
output csr_read_data;
wire [`LM32_WORD_RNG] csr_read_data;
`endif

`ifdef CFG_IROM_ENABLED
output irom_store_data_m; // Store data to Instruction ROM
@@ -279,7 +289,6 @@ reg dcache_select_m;
wire [`LM32_WORD_RNG] dcache_data_m; // Data read from cache
wire [`LM32_WORD_RNG] dcache_refill_address; // Address to refill data cache from
reg dcache_refill_ready; // Indicates the next word of refill data is ready
reg d_adr_o_sampling;
wire [`LM32_CTYPE_RNG] first_cycle_type; // First Wishbone cycle type
wire [`LM32_CTYPE_RNG] next_cycle_type; // Next Wishbone cycle type
wire last_word; // Indicates if this is the last word in the cache line
@@ -303,8 +312,10 @@ reg wb_select_m;
reg [`LM32_WORD_RNG] wb_data_m; // Data read from Wishbone
reg wb_load_complete; // Indicates when a Wishbone load is complete

`ifdef CFG_MMU_ENABLED
wire [`LM32_WORD_RNG] physical_address;
wire kernel_mode;
`endif

/////////////////////////////////////////////////////
// Functions
@@ -424,23 +435,28 @@ lm32_dcache #(
.refill_ready (dcache_refill_ready),
.refill_data (wb_data_m),
.dflush (dflush),
`ifdef CFG_MMU_ENABLED
.csr (csr),
.csr_write_data (csr_write_data),
.csr_write_enable (csr_write_enable),
.exception_x (exception_x),
.eret_q_x (eret_q_x),
.exception_m (exception_m),
.csr_psw (csr_psw),
`endif
// ----- Outputs -----
.stall_request (dcache_stall_request),
.restart_request (dcache_restart_request),
.refill_request (dcache_refill_request),
.refill_address (dcache_refill_address),
.refilling (dcache_refilling),
.load_data (dcache_data_m),
`ifdef CFG_MMU_ENABLED
.dtlb_miss_int (dtlb_miss),
.kernel_mode (kernel_mode),
.pa (physical_address),
.csr_read_data (csr_read_data)
.csr_read_data (csr_read_data),
`endif
.load_data (dcache_data_m)
);
`endif

@@ -664,7 +680,6 @@ always @(posedge clk_i `CFG_RESET_SENSITIVITY)
begin
if (rst_i == `TRUE)
begin
d_adr_o_sampling <= `FALSE;
d_cyc_o <= `FALSE;
d_stb_o <= `FALSE;
d_dat_o <= {`LM32_WORD_WIDTH{1'b0}};
@@ -701,7 +716,6 @@ begin
else
`endif
begin
d_adr_o_sampling <= 0;
// Refill/access complete
d_cyc_o <= `FALSE;
d_stb_o <= `FALSE;
@@ -728,10 +742,11 @@ begin
if (dcache_refill_request == `TRUE)
begin
// Start cache refill
`ifdef CFG_MMU_ENABLED
`ifdef CFG_VERBOSE_DISPLAY_ENABLED
$display("Sampling address to refill 0x%08X\n", first_address);
`endif
d_adr_o_sampling <= 1;
`endif
d_adr_o <= first_address;
d_cyc_o <= `TRUE;
d_sel_o <= {`LM32_WORD_WIDTH/8{`TRUE}};
@@ -756,12 +771,17 @@ begin
)
begin
// Data cache is write through, so all stores go to memory
`ifdef CFG_MMU_ENABLED
`ifdef CFG_VERBOSE_DISPLAY_ENABLED
$display("Sampling address to write through 0x%08X\n", store_data_m);
`endif
d_adr_o_sampling <= 1;
`endif
d_dat_o <= store_data_m;
d_adr_o <= (kernel_mode == `LM32_KERNEL_MODE) ? load_store_address_m : physical_address;
d_adr_o <=
`ifdef CFG_MMU_ENABLED
(kernel_mode == `LM32_USER_MODE) ? physical_address :
`endif
load_store_address_m;
d_cyc_o <= `TRUE;
d_sel_o <= byte_enable_m;
d_stb_o <= `TRUE;
@@ -775,12 +795,18 @@ begin
)
begin
// Read requested address
`ifdef CFG_MMU_ENABLED
`ifdef CFG_VERBOSE_DISPLAY_ENABLED
$display("Sampling address to read 0x%08X\n", (kernel_mode == `LM32_KERNEL_MODE) ? load_store_address_m : physical_address);
`endif
d_adr_o_sampling <= 1;
`endif
stall_wb_load <= `FALSE;
d_adr_o <= (kernel_mode == `LM32_KERNEL_MODE) ? load_store_address_m : physical_address;
d_adr_o <=
`ifdef CFG_MMU_ENABLED
(kernel_mode == `LM32_USER_MODE) ? physical_address :
`endif
load_store_address_m;

d_cyc_o <= `TRUE;
d_sel_o <= byte_enable_m;
d_stb_o <= `TRUE;
4 changes: 2 additions & 2 deletions software/bios/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
MMDIR=../..
include $(MMDIR)/software/include.mak

OBJECTS=crt0.o isr.o main.o unlzma.o boot.o boot-helper.o splash.o dtlb_load_test.o dtlb_exception_handling_tests.o dtlb_miss_handler.o
SEGMENTS=-j .text -j .data -j .rodata
OBJECTS=crt0.o isr.o main.o unlzma.o boot.o boot-helper.o splash.o dtlb_load_test.o dtlb_exception_handling_tests.o tlb_miss_handler.o itlbtest.o
SEGMENTS=-j .text -j .data -j .rodata -j .got -j .got.plt
LIBS=$(MMDIR)/software/libhpdmc/libhpdmc.a $(MMDIR)/software/libbase/libbase-light.a \
$(MMDIR)/software/libhal/libhal.a $(MMDIR)/software/libnet/libnet.a
CFLAGS += -g
10 changes: 10 additions & 0 deletions software/bios/crt0.S
Original file line number Diff line number Diff line change
@@ -117,6 +117,16 @@ _dtlb_miss_exception_handler:
nop
nop

_itlb_miss_exception_handler:
sw (sp+0), ra
calli .save_all
calli itlb_miss_handler
bi .restore_all_and_eret
nop
nop
nop
nop

macaddress:
.byte 0x10
.byte 0xe2
8 changes: 4 additions & 4 deletions software/bios/dtlb_exception_handling_tests.c
Original file line number Diff line number Diff line change
@@ -27,18 +27,18 @@ void dtlb_exception_handling_tests() {

asm volatile("mv %0, sp" : "=r"(stack) :: );

ret = mmu_map(stack, stack);
ret = mmu_map(stack, stack, DTLB_MAPPING | MAPPING_CAN_READ | MAPPING_CAN_WRITE);
check_for_error(ret);

ret = mmu_map(stack-0x1000, stack-0x1000);
ret = mmu_map(stack-0x1000, stack-0x1000, DTLB_MAPPING | MAPPING_CAN_READ | MAPPING_CAN_WRITE);
check_for_error(ret);

printf("stack == 0x%08X\n", stack);

addr = 0x44004004;

printf("\n=> Mapping 0x%08X to 0x%08X\n", addr, addr);
ret = mmu_map(addr, addr);
ret = mmu_map(addr, addr, DTLB_MAPPING | MAPPING_CAN_READ | MAPPING_CAN_WRITE);
check_for_error(ret);

data = 42;
@@ -63,7 +63,7 @@ void dtlb_exception_handling_tests() {
printf("\n<= Reading %d from virtual address 0x%08X\n\n", data, addr);

printf("=> Mapping 0x%08X to 0%08X\n", addr, addr+0x1000);
ret = mmu_map(addr, addr+0x1000); // Map to something else
ret = mmu_map(addr, addr+0x1000, DTLB_MAPPING | MAPPING_CAN_READ | MAPPING_CAN_WRITE); // Map to something else
check_for_error(ret);

printf("=> Invalidating the mapping of virtual address 0x%08X in the TLB\n", addr);
43 changes: 0 additions & 43 deletions software/bios/dtlb_miss_handler.c

This file was deleted.

6 changes: 6 additions & 0 deletions software/bios/main.c
Original file line number Diff line number Diff line change
@@ -431,6 +431,7 @@ static void help()
puts("mmuread - reads from memory with MMU enabled");
puts("mmuwrite - writes to memory with MMU enabled");
puts("dtlbtest - runs DTLB MMU load store tests");
puts("itlbtest - runs ITLB MMU tests");
puts("detest - runs DTLB MMU exception handling tests");
}

@@ -616,6 +617,7 @@ static void do_command(char *c)
else if(strcmp(token, "rcsr") == 0) rcsr(get_token(&c));
else if(strcmp(token, "wcsr") == 0) wcsr(get_token(&c), get_token(&c));
else if(strcmp(token, "dtlbtest") == 0) dtlbtest();
else if(strcmp(token, "itlbtest") == 0) itlbtest();
else if(strcmp(token, "detest") == 0) dtlb_exception_handling_tests();
else if(strcmp(token, "dmap") == 0) dmap(get_token(&c), get_token(&c));
else if(strcmp(token, "dunmap") == 0) dunmap(get_token(&c));
@@ -794,6 +796,10 @@ int main(int i, char **c)
printf("I: Booting in rescue mode\n");

uart_set_polling_mode(1);
/*
mmu_dtlb_invalidate();
mmu_itlb_invalidate();
*/
while(1) {
putsnonl("\e[1mBIOS>\e[0m ");
readstr(buffer, 64);
91 changes: 91 additions & 0 deletions software/bios/tlb_miss_handler.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Milkymist SoC (Software)
* Copyright (C) 2012 Yann Sionneau <yann.sionneau@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <hal/mmu.h>
#include <base/mmu.h>

void dtlb_miss_handler(void)
{
unsigned int vaddr, paddr;
unsigned int EA;

// retrieve virtual address which caused the page fault
asm volatile("rcsr %0, dtlbma" : "=r"(vaddr) :: );
asm volatile("mv %0, ea": "=r"(EA) :: );

printf("Address 0x%08X caused a DTLB exception\n", EA);
printf("dtlbma == 0x%08X\n", vaddr);

/*
* check if there is an existing mapping for that virtual address
* if yes: refill the DTLB with it
* if not: we panic() !
*/
paddr = get_mmu_mapping_for(vaddr);
if (paddr == A_BAD_ADDR || !is_dtlb_mapping(vaddr))
{
puts("no such mapping !");
puts("Unrecoverable DTLB page fault !");
panic();
}

if (!is_dtlb_mapping(vaddr)) {
puts("Mapping not in DTLB");
panic();
}

printf("Refilling DTLB with mapping 0x%08X->0x%08X\n", vaddr, paddr);
mmu_dtlb_map(vaddr, paddr);

}

void itlb_miss_handler(void)
{
unsigned int vaddr, paddr;
// unsigned int vaddr;
unsigned int EA;
unsigned int PSW;
unsigned int ra;

asm volatile("mv %0, ea": "=r"(EA) :: );
asm volatile("rcsr %0, itlbma" : "=r"(vaddr) :: );
asm volatile("rcsr %0, PSW" : "=r"(PSW) :: );
asm volatile("mv %0, ra" : "=r"(ra) :: );

printf("ra == 0x%08X\n", ra);
printf("ea == 0x%08X\n", EA);
printf("Address 0x%08X caused an ITLB page fault\n", vaddr);
printf("PSW == 0x%08X\n", PSW);
// asm volatile("mv EA, %0" :: "r"(vaddr) : );

paddr = get_mmu_mapping_for(vaddr);
if (paddr == A_BAD_ADDR)
{
puts("No such mapping !");
puts("Unrecoverable ITLB page fault !");
panic();
}

if (!is_itlb_mapping(vaddr))
{
puts("Mapping not in ITLB");
panic();
}

printf("Refilling ITLB with mapping 0x%08X->0x%08X\n", vaddr, paddr);
mmu_itlb_map(vaddr, paddr);
}
4 changes: 3 additions & 1 deletion software/include/base/mmu.h
Original file line number Diff line number Diff line change
@@ -20,9 +20,11 @@

#include <hal/mmu.h>

unsigned int mmu_map(unsigned int vaddr, unsigned int paddr);
unsigned int mmu_map(unsigned int vaddr, unsigned int paddr, char metadata);
unsigned int get_mmu_mapping_for(unsigned int vaddr);
unsigned char remove_mmu_mapping_for(unsigned int vaddr);
unsigned char is_dtlb_mapping(unsigned int vaddr);
unsigned char is_itlb_mapping(unsigned int vaddr);
void panic(void);
void check_for_error(int ret);

58 changes: 55 additions & 3 deletions software/include/hal/mmu.h
Original file line number Diff line number Diff line change
@@ -19,17 +19,30 @@
#define __MMU_H__

#define PAGE_SIZE (4096)
#define MAX_MMU_SLOTS 10
#define MAX_MMU_SLOTS 30
#define NO_EMPTY_SLOT (MAX_MMU_SLOTS + 1)
#define A_BAD_ADDR 0xffffffff
#define get_pfn(x) (x & ~(PAGE_SIZE - 1))

#define ITLB_MAPPING (1)
#define DTLB_MAPPING (1 << 1)
#define MAPPING_CAN_READ (1 << 2)
#define MAPPING_CAN_WRITE (1 << 3)
#define MAPPING_COPY_ON_WRITE (1 << 4)
#define MAPPING_IS_VALID (1 << 5)

struct mmu_mapping {

unsigned int vaddr;
unsigned int paddr;
char valid;

char metadata;
// x x x x x x x x
// | | | | | |-> ITLB mapping
// | | | | |-> DTLB mapping
// | | | |-> CAN_READ
// | | |-> CAN_WRITE
// | |-> COPY_ON_WRITE: Not Implemented Yet
// |-> MAPPING_IS_VALID
};

#define enable_dtlb() do { \
@@ -48,9 +61,48 @@ struct mmu_mapping {
"xor r0, r0, r0":::"r11"); \
} while(0);

#define enable_itlb() do { \
asm volatile ("xor r11, r11, r11\n\t" \
"ori r11, r11, 0x10\n\t" \
"wcsr tlbctrl, r11\n\t" \
"xor r0, r0, r0\n\t" \
"xor r0, r0, r0\n\t" \
"xor r0, r0, r0\n\t" \
"xor r0, r0, r0\n\t":::"r11"); \
} while(0);

#define LM32_CSR_PSW_ITLBE "(0x8)"

#define disable_itlb() do { \
asm volatile ("rcsr r11, PSW\n\t" \
"mvi r10, ~(0x8)\n\t" \
"and r11, r11, r10\n\t" \
"wcsr PSW, r11\n\t" ::: "r10", "r11"); \
} while (0);

// FIXME : We MUST replace the following macro with a function which
// enables ITLB using two different methods depending on whether
// branch will be predicted as taken or non-taken

#define call_function_with_itlb_enabled(function) do { \
asm volatile ("rcsr r11, PSW\n\t" \
"ori r11, r11, 0x8\n\t" \
"wcsr psw, r11\n\t" \
"call %0\n\t" :: "r"(function) : "r11"); \
} while (0);

#define call_function_with_itlb_disabled(function) do { \
asm volatile ("rcsr r11, PSW\n\t" \
"mvi r10, ~(0x8)\n\t" \
"and r11, r11, r10\n\t" \
"wcsr psw, r11\n\t" \
"call %0\n\t" :: "r"(function) : "r10", "r11"); \
} while (0);

void mmu_dtlb_map(unsigned int vpfn, unsigned int pfn);
unsigned int read_word_with_mmu_enabled(unsigned int vaddr);
void mmu_dtlb_invalidate_line(unsigned int vaddr);
void mmu_itlb_invalidate_line(unsigned int vaddr);
void mmu_dtlb_invalidate(void);

#endif
82 changes: 72 additions & 10 deletions software/libbase/mmu.c
Original file line number Diff line number Diff line change
@@ -33,19 +33,34 @@ struct mmu_mapping mappings[MAX_MMU_SLOTS];
* If there is no empty slot anymore then we fail
*/

unsigned int mmu_map(unsigned int vaddr, unsigned int paddr) {
unsigned int mmu_map(unsigned int vaddr, unsigned int paddr, char metadata) {
int i;
int empty_slot = NO_EMPTY_SLOT;
vaddr = get_pfn(vaddr);
paddr = get_pfn(paddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
{
if (!mappings[i].valid)
if (!(mappings[i].metadata & (MAPPING_IS_VALID)))
empty_slot = i;
if ((vaddr == mappings[i].vaddr) && (paddr == mappings[i].paddr) && mappings[i].valid)
if ((vaddr == mappings[i].vaddr) && (paddr == mappings[i].paddr) && (mappings[i].metadata & MAPPING_IS_VALID))
{
puts("Already mapped !");
puts("Already mapped, updating metadata !");
mappings[i].metadata |= metadata;
// if (mappings[i].metadata & ITLB_MAPPING)
// mmu_itlb_map(vaddr, paddr);
if (mappings[i].metadata & DTLB_MAPPING)
mmu_dtlb_map(vaddr, paddr);
return 1;
} else if ((vaddr == mappings[i].vaddr) && (paddr != mappings[i].paddr) && (mappings[i].metadata & MAPPING_IS_VALID))
{
puts("Vaddr already mapped to another Paddr (0x%08X), overwritting...\n", mappings[i].paddr);
mappings[i].paddr = paddr;
mappings[i].metadata = (metadata | MAPPING_IS_VALID);
// if (mappings[i].metadata & ITLB_MAPPING)
// mmu_itlb_map(vaddr, paddr);
if (mappings[i].metadata & DTLB_MAPPING)
mmu_dtlb_map(vaddr, paddr);
return 1;
}
}
@@ -58,8 +73,14 @@ unsigned int mmu_map(unsigned int vaddr, unsigned int paddr) {

mappings[empty_slot].vaddr = vaddr;
mappings[empty_slot].paddr = paddr;
mappings[empty_slot].valid = 1;
mmu_dtlb_map(vaddr, paddr);
mappings[empty_slot].metadata = (metadata | MAPPING_IS_VALID);

// if (metadata & ITLB_MAPPING)
// mmu_itlb_map(vaddr, paddr);

if (metadata & DTLB_MAPPING)
mmu_dtlb_map(vaddr, paddr);

printf("mapping 0x%08X->0x%08X in slot %d [0x%p]\n", vaddr, paddr, empty_slot, &mappings[empty_slot]);

return 1;
@@ -70,7 +91,7 @@ unsigned int get_mmu_mapping_for(unsigned int vaddr) {
vaddr = get_pfn(vaddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
if (mappings[i].valid && (vaddr == mappings[i].vaddr))
if ((mappings[i].metadata & MAPPING_IS_VALID) && (vaddr == mappings[i].vaddr))
return mappings[i].paddr;

return A_BAD_ADDR;
@@ -82,16 +103,38 @@ unsigned char remove_mmu_mapping_for(unsigned int vaddr) {

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
{
if (mappings[i].valid && (vaddr == mappings[i].vaddr))
if ((mappings[i].metadata & MAPPING_IS_VALID) && (vaddr == mappings[i].vaddr))
{
mmu_dtlb_invalidate(vaddr);
mappings[i].valid = 0;
mmu_dtlb_invalidate_line(vaddr);
mappings[i].metadata &= ~MAPPING_IS_VALID;
return 1;
}
}
return 0;
}

unsigned char is_dtlb_mapping(unsigned int vaddr) {
unsigned int i;
vaddr = get_pfn(vaddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
if ((mappings[i].vaddr == vaddr) && (mappings[i].metadata & (MAPPING_IS_VALID)) && (mappings[i].metadata & (DTLB_MAPPING)))
return 1;

return 0;
}

unsigned char is_itlb_mapping(unsigned int vaddr) {
unsigned int i;
vaddr = get_pfn(vaddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
if ((mappings[i].vaddr == vaddr) && (mappings[i].metadata & (MAPPING_IS_VALID)) && (mappings[i].metadata & (ITLB_MAPPING)))
return 1;

return 0;
}

void panic(void) {
puts("PANIC !");
while(1)
@@ -114,3 +157,22 @@ void check_for_error(int ret) {
panic();
}
}

void mmu_map_print(void)
{
unsigned int i;

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
{
if (mappings[i].metadata & MAPPING_IS_VALID)
{
printf("[%d] 0x%08X -> 0x%08X:", i, mappings[i].vaddr, mappings[i].paddr);
if (mappings[i].metadata & ITLB_MAPPING)
printf(" ITLB ");
if (mappings[i].metadata & DTLB_MAPPING)
printf(" DTLB ");
puts("");
}
}

}
49 changes: 47 additions & 2 deletions software/libhal/mmu.c
Original file line number Diff line number Diff line change
@@ -21,6 +21,9 @@
#define DTLB_CTRL_FLUSH_CMD (0x3)
#define DTLB_CTRL_DISABLE_CMD (0x9)
#define DTLB_CTRL_ENABLE_CMD (0x11)
#define ITLB_CTRL_FLUSH_CMD (0x2)
#define ITLB_CTRL_DISABLE_CMD (0x8)
#define ITLB_CTRL_ENABLE_CMD (0x10)

/* @vpfn : virtual page frame number
* @pfn : physical page frame number
@@ -32,31 +35,60 @@ inline void mmu_dtlb_map(unsigned int vpfn, unsigned int pfn)
"wcsr tlbvaddr, %0" :: "r"(vpfn) : );

asm volatile ("ori %0, %0, 1\n\t"
"wcsr tlbpaddr, %0"::"r"(pfn):);
"wcsr tlbpaddr, %0" :: "r"(pfn) : );

asm volatile ("xor r11, r11, r11\n\t"
"ori r11, r11, 0x5\n\t"
"wcsr tlbctrl, r11" ::: "r11");

}

inline void mmu_itlb_map(unsigned int vpfn, unsigned int pfn)
{

asm volatile ("wcsr tlbvaddr, %0" :: "r"(get_pfn(vpfn)) : );

asm volatile ("wcsr tlbpaddr, %0" :: "r"(get_pfn(pfn)) : );

asm volatile ("xor r11, r11, r11\n\t"
"ori r11, r11, 0x4\n\t"
"wcsr tlbctrl, r11" ::: "r11");
}

inline void mmu_dtlb_invalidate_line(unsigned int vaddr)
{
asm volatile ("ori %0, %0, 1\n\t"
"wcsr tlbvaddr, %0"::"r"(vaddr):);
"wcsr tlbvaddr, %0" :: "r"(vaddr) : );

asm volatile ("xor r11, r11, r11\n\t"
"ori r11, r11, 0x21\n\t"
"wcsr tlbctrl, r11" ::: "r11");
}

inline void mmu_itlb_invalidate_line(unsigned int vaddr)
{
asm volatile ("ori %0, %0, 0\n\t"
"wcsr tlbvaddr, %0" :: "r"(vaddr) : );

asm volatile ("xor r11, r11, r11\n\t"
"ori r11, r11, 0x20\n\t"
"wcsr tlbctrl, r11" ::: "r11");
}

inline void mmu_dtlb_invalidate(void)
{
register unsigned int cmd = DTLB_CTRL_FLUSH_CMD;
asm volatile("wcsr tlbctrl, %0" :: "r"(cmd) : );

}

inline void mmu_itlb_invalidate(void)
{
register unsigned int cmd = ITLB_CTRL_FLUSH_CMD;
asm volatile("wcsr tlbctrl, %0" :: "r"(cmd) : );

}

/* This function activates the MMU
* then reads from virtual address "vaddr"
* and store the result in temporary variable "data".
@@ -95,3 +127,16 @@ unsigned int write_word_with_mmu_enabled(register unsigned int vaddr, register u
"xor r0, r0, r0\n\t" :: "r"(vaddr), "r"(data) : "r11"
);
}
/*
inline void call_function_with_itlb_enabled(unsigned int f)
{
asm volatile(
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x10\n\t"
"wcsr tlbctrl, r11\n\t" // Activates ITLB
"call %0\n\t"
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x8\n\t"
"wcsr tlbctrl, r11" :: "r"(f) : "r11" // Disactivates ITLB
);
}*/

0 comments on commit 70b5f48

Please sign in to comment.