return clsdecl
def appendComments(self,data):
- lines = data.split("\n")
- for line in lines:
- self.printpy("#"+line)
+ self.outputfile.write(data)
+ #lines = data.split("\n")
+ #for line in lines:
+ # self.printpy("#"+line)
# combinatorical assign
def cont_assign_1(self,p):
--- /dev/null
+// 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.
+//
+// Author: Florian Zaruba, ETH Zurich
+// Date: 19.04.2017
+// Description: Load Store Unit, handles address calculation and memory interface signals
+
+//import ariane_pkg::*;
+
+module load_store_unit #(
+ parameter int ASID_WIDTH = 1
+ // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig
+)(
+ input logic clk_i,
+ input logic rst_ni,
+ input logic flush_i,
+ output logic no_st_pending_o,
+ input logic amo_valid_commit_i,
+
+
+ //input fu_data_t fu_data_i,
+ output logic lsu_ready_o, // FU is ready e.g. not busy
+ input logic lsu_valid_i, // Input is valid
+
+ output logic [TRANS_ID_BITS-1:0] load_trans_id_o, // ID of scoreboard entry at which to write back
+ output logic [63:0] load_result_o,
+ output logic load_valid_o,
+ //output exception_t load_exception_o, // to WB, signal exception status LD exception
+
+ output logic [TRANS_ID_BITS-1:0] store_trans_id_o, // ID of scoreboard entry at which to write back
+ output logic [63:0] store_result_o,
+ output logic store_valid_o,
+ //output exception_t store_exception_o, // to WB, signal exception status ST exception
+
+ input logic commit_i, // commit the pending store
+ output logic commit_ready_o, // commit queue is ready to accept another commit request
+
+ input logic enable_translation_i, // enable virtual memory translation
+ input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores
+
+ // icache translation requests
+ //input icache_areq_o_t icache_areq_i,
+ //output icache_areq_i_t icache_areq_o,
+
+ //input riscv::priv_lvl_t priv_lvl_i, // From CSR register file
+ //input riscv::priv_lvl_t ld_st_priv_lvl_i, // From CSR register file
+ input logic sum_i, // From CSR register file
+ input logic mxr_i, // From CSR register file
+ input logic [43:0] satp_ppn_i, // From CSR register file
+ input logic [ASID_WIDTH-1:0] asid_i, // From CSR register file
+ input logic flush_tlb_i,
+ // Performance counters
+ output logic itlb_miss_o,
+ output logic dtlb_miss_o
+
+ // interface to dcache
+ //input dcache_req_o_t [2:0] dcache_req_ports_i,
+ //output dcache_req_i_t [2:0] dcache_req_ports_o,
+ // AMO interface
+ //output amo_req_t amo_req_o,
+ //input amo_resp_t amo_resp_i
+);
+ #docstring_begin
+ // data is misaligned
+ logic data_misaligned;
+ // --------------------------------------
+ // 1st register stage - (stall registers)
+ // --------------------------------------
+ // those are the signals which are always correct
+ // e.g.: they keep the value in the stall case
+ lsu_ctrl_t lsu_ctrl;
+
+ logic pop_st;
+ logic pop_ld;
+
+ // ------------------------------
+ // Address Generation Unit (AGU)
+ // ------------------------------
+ // virtual address as calculated by the AGU in the first cycle
+ logic [63:0] vaddr_i;
+ logic [7:0] be_i;
+
+ assign vaddr_i = $unsigned($signed(fu_data_i.imm) + $signed(fu_data_i.operand_a));
+
+ logic st_valid_i;
+ logic ld_valid_i;
+ logic ld_translation_req;
+ logic st_translation_req;
+ logic [63:0] ld_vaddr;
+ logic [63:0] st_vaddr;
+ logic translation_req;
+ logic translation_valid;
+ logic [63:0] mmu_vaddr;
+ logic [63:0] mmu_paddr;
+ exception_t mmu_exception;
+ logic dtlb_hit;
+
+ logic ld_valid;
+ logic [TRANS_ID_BITS-1:0] ld_trans_id;
+ logic [63:0] ld_result;
+ logic st_valid;
+ logic [TRANS_ID_BITS-1:0] st_trans_id;
+ logic [63:0] st_result;
+
+ logic [11:0] page_offset;
+ logic page_offset_matches;
+
+ exception_t misaligned_exception;
+ exception_t ld_ex;
+ exception_t st_ex;
+
+ // -------------------
+ // MMU e.g.: TLBs/PTW
+ // -------------------
+ mmu #(
+ .INSTR_TLB_ENTRIES ( 16 ),
+ .DATA_TLB_ENTRIES ( 16 ),
+ .ASID_WIDTH ( ASID_WIDTH ),
+ .ArianeCfg ( ArianeCfg )
+ ) i_mmu (
+ // misaligned bypass
+ .misaligned_ex_i ( misaligned_exception ),
+ .lsu_is_store_i ( st_translation_req ),
+ .lsu_req_i ( translation_req ),
+ .lsu_vaddr_i ( mmu_vaddr ),
+ .lsu_valid_o ( translation_valid ),
+ .lsu_paddr_o ( mmu_paddr ),
+ .lsu_exception_o ( mmu_exception ),
+ .lsu_dtlb_hit_o ( dtlb_hit ), // send in the same cycle as the request
+ // connecting PTW to D$ IF
+ .req_port_i ( dcache_req_ports_i [0] ),
+ .req_port_o ( dcache_req_ports_o [0] ),
+ // icache address translation requests
+ .icache_areq_i ( icache_areq_i ),
+ .icache_areq_o ( icache_areq_o ),
+ .*
+ );
+ // ------------------
+ // Store Unit
+ // ------------------
+ store_unit i_store_unit (
+ .clk_i,
+ .rst_ni,
+ .flush_i,
+ .no_st_pending_o,
+
+ .valid_i ( st_valid_i ),
+ .lsu_ctrl_i ( lsu_ctrl ),
+ .pop_st_o ( pop_st ),
+ .commit_i,
+ .commit_ready_o,
+ .amo_valid_commit_i,
+
+ .valid_o ( st_valid ),
+ .trans_id_o ( st_trans_id ),
+ .result_o ( st_result ),
+ .ex_o ( st_ex ),
+ // MMU port
+ .translation_req_o ( st_translation_req ),
+ .vaddr_o ( st_vaddr ),
+ .paddr_i ( mmu_paddr ),
+ .ex_i ( mmu_exception ),
+ .dtlb_hit_i ( dtlb_hit ),
+ // Load Unit
+ .page_offset_i ( page_offset ),
+ .page_offset_matches_o ( page_offset_matches ),
+ // AMOs
+ .amo_req_o,
+ .amo_resp_i,
+ // to memory arbiter
+ .req_port_i ( dcache_req_ports_i [2] ),
+ .req_port_o ( dcache_req_ports_o [2] )
+ );
+
+ // ------------------
+ // Load Unit
+ // ------------------
+ load_unit i_load_unit (
+ .valid_i ( ld_valid_i ),
+ .lsu_ctrl_i ( lsu_ctrl ),
+ .pop_ld_o ( pop_ld ),
+
+ .valid_o ( ld_valid ),
+ .trans_id_o ( ld_trans_id ),
+ .result_o ( ld_result ),
+ .ex_o ( ld_ex ),
+ // MMU port
+ .translation_req_o ( ld_translation_req ),
+ .vaddr_o ( ld_vaddr ),
+ .paddr_i ( mmu_paddr ),
+ .ex_i ( mmu_exception ),
+ .dtlb_hit_i ( dtlb_hit ),
+ // to store unit
+ .page_offset_o ( page_offset ),
+ .page_offset_matches_i ( page_offset_matches ),
+ // to memory arbiter
+ .req_port_i ( dcache_req_ports_i [1] ),
+ .req_port_o ( dcache_req_ports_o [1] ),
+ .*
+ );
+
+ // ----------------------------
+ // Output Pipeline Register
+ // ----------------------------
+ shift_reg #(
+ .dtype ( logic[$bits(ld_valid) + $bits(ld_trans_id) + $bits(ld_result) + $bits(ld_ex) - 1: 0]),
+ .Depth ( NR_LOAD_PIPE_REGS )
+ ) i_pipe_reg_load (
+ .clk_i,
+ .rst_ni,
+ .d_i ( {ld_valid, ld_trans_id, ld_result, ld_ex} ),
+ .d_o ( {load_valid_o, load_trans_id_o, load_result_o, load_exception_o} )
+ );
+
+ shift_reg #(
+ .dtype ( logic[$bits(st_valid) + $bits(st_trans_id) + $bits(st_result) + $bits(st_ex) - 1: 0]),
+ .Depth ( NR_STORE_PIPE_REGS )
+ ) i_pipe_reg_store (
+ .clk_i,
+ .rst_ni,
+ .d_i ( {st_valid, st_trans_id, st_result, st_ex} ),
+ .d_o ( {store_valid_o, store_trans_id_o, store_result_o, store_exception_o} )
+ );
+
+ // determine whether this is a load or store
+ always_comb begin : which_op
+
+ ld_valid_i = 1'b0;
+ st_valid_i = 1'b0;
+
+ translation_req = 1'b0;
+ mmu_vaddr = 64'b0;
+
+ // check the operator to activate the right functional unit accordingly
+ unique case (lsu_ctrl.fu)
+ // all loads go here
+ LOAD: begin
+ ld_valid_i = lsu_ctrl.valid;
+ translation_req = ld_translation_req;
+ mmu_vaddr = ld_vaddr;
+ end
+ // all stores go here
+ STORE: begin
+ st_valid_i = lsu_ctrl.valid;
+ translation_req = st_translation_req;
+ mmu_vaddr = st_vaddr;
+ end
+ // not relevant for the LSU
+ default: ;
+ endcase
+ end
+
+
+ // ---------------
+ // Byte Enable
+ // ---------------
+ // we can generate the byte enable from the virtual address since the last
+ // 12 bit are the same anyway
+ // and we can always generate the byte enable from the address at hand
+ assign be_i = be_gen(vaddr_i[2:0], extract_transfer_size(fu_data_i.operator));
+
+ // ------------------------
+ // Misaligned Exception
+ // ------------------------
+ // we can detect a misaligned exception immediately
+ // the misaligned exception is passed to the functional unit via the MMU, which in case
+ // can augment the exception if other memory related exceptions like a page fault or access errors
+ always_comb begin : data_misaligned_detection
+
+ misaligned_exception = {
+ 64'b0,
+ 64'b0,
+ 1'b0
+ };
+
+ data_misaligned = 1'b0;
+
+ if (lsu_ctrl.valid) begin
+ case (lsu_ctrl.operator)
+ // double word
+ LD, SD, FLD, FSD,
+ AMO_LRD, AMO_SCD,
+ AMO_SWAPD, AMO_ADDD, AMO_ANDD, AMO_ORD,
+ AMO_XORD, AMO_MAXD, AMO_MAXDU, AMO_MIND,
+ AMO_MINDU: begin
+ if (lsu_ctrl.vaddr[2:0] != 3'b000) begin
+ data_misaligned = 1'b1;
+ end
+ end
+ // word
+ LW, LWU, SW, FLW, FSW,
+ AMO_LRW, AMO_SCW,
+ AMO_SWAPW, AMO_ADDW, AMO_ANDW, AMO_ORW,
+ AMO_XORW, AMO_MAXW, AMO_MAXWU, AMO_MINW,
+ AMO_MINWU: begin
+ if (lsu_ctrl.vaddr[1:0] != 2'b00) begin
+ data_misaligned = 1'b1;
+ end
+ end
+ // half word
+ LH, LHU, SH, FLH, FSH: begin
+ if (lsu_ctrl.vaddr[0] != 1'b0) begin
+ data_misaligned = 1'b1;
+ end
+ end
+ // byte -> is always aligned
+ default:;
+ endcase
+ end
+
+ if (data_misaligned) begin
+
+ if (lsu_ctrl.fu == LOAD) begin
+ misaligned_exception = {
+ riscv::LD_ADDR_MISALIGNED,
+ lsu_ctrl.vaddr,
+ 1'b1
+ };
+
+ end else if (lsu_ctrl.fu == STORE) begin
+ misaligned_exception = {
+ riscv::ST_ADDR_MISALIGNED,
+ lsu_ctrl.vaddr,
+ 1'b1
+ };
+ end
+ end
+
+ // we work with SV39, so if VM is enabled, check that all bits [63:38] are equal
+ if (en_ld_st_translation_i && !((&lsu_ctrl.vaddr[63:38]) == 1'b1 || (|lsu_ctrl.vaddr[63:38]) == 1'b0)) begin
+
+ if (lsu_ctrl.fu == LOAD) begin
+ misaligned_exception = {
+ riscv::LD_ACCESS_FAULT,
+ lsu_ctrl.vaddr,
+ 1'b1
+ };
+
+ end else if (lsu_ctrl.fu == STORE) begin
+ misaligned_exception = {
+ riscv::ST_ACCESS_FAULT,
+ lsu_ctrl.vaddr,
+ 1'b1
+ };
+ end
+ end
+ end
+
+ // ------------------
+ // LSU Control
+ // ------------------
+ // new data arrives here
+ lsu_ctrl_t lsu_req_i;
+
+ assign lsu_req_i = {lsu_valid_i, vaddr_i, fu_data_i.operand_b, be_i, fu_data_i.fu, fu_data_i.operator, fu_data_i.trans_id};
+
+ lsu_bypass lsu_bypass_i (
+ .lsu_req_i ( lsu_req_i ),
+ .lus_req_valid_i ( lsu_valid_i ),
+ .pop_ld_i ( pop_ld ),
+ .pop_st_i ( pop_st ),
+
+ .lsu_ctrl_o ( lsu_ctrl ),
+ .ready_o ( lsu_ready_o ),
+ .*
+ );
+#docstring_end
+endmodule
+
+#docstring_begin
+// ------------------
+// LSU Control
+// ------------------
+// The LSU consists of two independent block which share a common address translation block.
+// The one block is the load unit, the other one is the store unit. They will signal their readiness
+// with separate signals. If they are not ready the LSU control should keep the last applied signals stable.
+// Furthermore it can be the case that another request for one of the two store units arrives in which case
+// the LSU control should sample it and store it for later application to the units. It does so, by storing it in a
+// two element FIFO. This is necessary as we only know very late in the cycle whether the load/store will succeed (address check,
+// TLB hit mainly). So we better unconditionally allow another request to arrive and store this request in case we need to.
+module lsu_bypass (
+ input logic clk_i,
+ input logic rst_ni,
+ input logic flush_i,
+
+ input lsu_ctrl_t lsu_req_i,
+ input logic lus_req_valid_i,
+ input logic pop_ld_i,
+ input logic pop_st_i,
+
+ output lsu_ctrl_t lsu_ctrl_o,
+ output logic ready_o
+ );
+
+ lsu_ctrl_t [1:0] mem_n, mem_q;
+ logic read_pointer_n, read_pointer_q;
+ logic write_pointer_n, write_pointer_q;
+ logic [1:0] status_cnt_n, status_cnt_q;
+
+ logic empty;
+ assign empty = (status_cnt_q == 0);
+ assign ready_o = empty;
+
+ always_comb begin
+ automatic logic [1:0] status_cnt;
+ automatic logic write_pointer;
+ automatic logic read_pointer;
+
+ status_cnt = status_cnt_q;
+ write_pointer = write_pointer_q;
+ read_pointer = read_pointer_q;
+
+ mem_n = mem_q;
+ // we've got a valid LSU request
+ if (lus_req_valid_i) begin
+ mem_n[write_pointer_q] = lsu_req_i;
+ write_pointer++;
+ status_cnt++;
+ end
+
+ if (pop_ld_i) begin
+ // invalidate the result
+ mem_n[read_pointer_q].valid = 1'b0;
+ read_pointer++;
+ status_cnt--;
+ end
+
+ if (pop_st_i) begin
+ // invalidate the result
+ mem_n[read_pointer_q].valid = 1'b0;
+ read_pointer++;
+ status_cnt--;
+ end
+
+ if (pop_st_i && pop_ld_i)
+ mem_n = '0;
+
+ if (flush_i) begin
+ status_cnt = '0;
+ write_pointer = '0;
+ read_pointer = '0;
+ mem_n = '0;
+ end
+ // default assignments
+ read_pointer_n = read_pointer;
+ write_pointer_n = write_pointer;
+ status_cnt_n = status_cnt;
+ end
+
+ // output assignment
+ always_comb begin : output_assignments
+ if (empty) begin
+ lsu_ctrl_o = lsu_req_i;
+ end else begin
+ lsu_ctrl_o = mem_q[read_pointer_q];
+ end
+ end
+
+ // registers
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (~rst_ni) begin
+ mem_q <= '0;
+ status_cnt_q <= '0;
+ write_pointer_q <= '0;
+ read_pointer_q <= '0;
+ end else begin
+ mem_q <= mem_n;
+ status_cnt_q <= status_cnt_n;
+ write_pointer_q <= write_pointer_n;
+ read_pointer_q <= read_pointer_n;
+ end
+ end
+endmodule
+#docstring_end
--- /dev/null
+// 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.
+//
+// Author: Florian Zaruba, ETH Zurich
+// Date: 19/04/2017
+// Description: Memory Management Unit for Ariane, contains TLB and
+// address translation unit. SV39 as defined in RISC-V
+// privilege specification 1.11-WIP
+
+//import ariane_pkg::*;
+
+module mmu #(
+ parameter int INSTR_TLB_ENTRIES = 4,
+ parameter int DATA_TLB_ENTRIES = 4,
+ parameter int ASID_WIDTH = 1
+ // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig
+) (
+ input logic clk_i,
+ input logic rst_ni,
+ input logic flush_i,
+ input logic enable_translation_i,
+ input logic en_ld_st_translation_i, // enable virtual memory translation for load/stores
+ // IF interface
+ #docstring_begin
+ input icache_areq_o_t icache_areq_i,
+ output icache_areq_i_t icache_areq_o,
+ #docstring_end
+ // LSU interface
+ // this is a more minimalistic interface because the actual addressing logic is handled
+ // in the LSU as we distinguish load and stores, what we do here is simple address translation
+ //input exception_t misaligned_ex_i,
+ input logic lsu_req_i, // request address translation
+ input logic [63:0] lsu_vaddr_i, // virtual address in
+ input logic lsu_is_store_i, // the translation is requested by a store
+ // if we need to walk the page table we can't grant in the same cycle
+ // Cycle 0
+ output logic lsu_dtlb_hit_o, // sent in the same cycle as the request if translation hits in the DTLB
+ // Cycle 1
+ output logic lsu_valid_o, // translation is valid
+ output logic [63:0] lsu_paddr_o, // translated address
+ //output exception_t lsu_exception_o, // address translation threw an exception
+ // General control signals
+ //input riscv::priv_lvl_t priv_lvl_i,
+ //input riscv::priv_lvl_t ld_st_priv_lvl_i,
+ input logic sum_i,
+ input logic mxr_i,
+ // input logic flag_mprv_i,
+ input logic [43:0] satp_ppn_i,
+ input logic [ASID_WIDTH-1:0] asid_i,
+ input logic flush_tlb_i,
+ // Performance counters
+ output logic itlb_miss_o,
+ output logic dtlb_miss_o
+ // PTW memory interface
+ //input dcache_req_o_t req_port_i,
+ //output dcache_req_i_t req_port_o
+);
+
+ #docstring_begin
+ logic iaccess_err; // insufficient privilege to access this instruction page
+ logic daccess_err; // insufficient privilege to access this data page
+ logic ptw_active; // PTW is currently walking a page table
+ logic walking_instr; // PTW is walking because of an ITLB miss
+ logic ptw_error; // PTW threw an exception
+
+ logic [38:0] update_vaddr;
+ tlb_update_t update_ptw_itlb, update_ptw_dtlb;
+
+ logic itlb_lu_access;
+ riscv::pte_t itlb_content;
+ logic itlb_is_2M;
+ logic itlb_is_1G;
+ logic itlb_lu_hit;
+
+ logic dtlb_lu_access;
+ riscv::pte_t dtlb_content;
+ logic dtlb_is_2M;
+ logic dtlb_is_1G;
+ logic dtlb_lu_hit;
+
+
+ // Assignments
+ assign itlb_lu_access = icache_areq_i.fetch_req;
+ assign dtlb_lu_access = lsu_req_i;
+
+
+ tlb #(
+ .TLB_ENTRIES ( INSTR_TLB_ENTRIES ),
+ .ASID_WIDTH ( ASID_WIDTH )
+ ) i_itlb (
+ .clk_i ( clk_i ),
+ .rst_ni ( rst_ni ),
+ .flush_i ( flush_tlb_i ),
+
+ .update_i ( update_ptw_itlb ),
+
+ .lu_access_i ( itlb_lu_access ),
+ .lu_asid_i ( asid_i ),
+ .lu_vaddr_i ( icache_areq_i.fetch_vaddr ),
+ .lu_content_o ( itlb_content ),
+
+ .lu_is_2M_o ( itlb_is_2M ),
+ .lu_is_1G_o ( itlb_is_1G ),
+ .lu_hit_o ( itlb_lu_hit )
+ );
+
+ tlb #(
+ .TLB_ENTRIES ( DATA_TLB_ENTRIES ),
+ .ASID_WIDTH ( ASID_WIDTH )
+ ) i_dtlb (
+ .clk_i ( clk_i ),
+ .rst_ni ( rst_ni ),
+ .flush_i ( flush_tlb_i ),
+
+ .update_i ( update_ptw_dtlb ),
+
+ .lu_access_i ( dtlb_lu_access ),
+ .lu_asid_i ( asid_i ),
+ .lu_vaddr_i ( lsu_vaddr_i ),
+ .lu_content_o ( dtlb_content ),
+
+ .lu_is_2M_o ( dtlb_is_2M ),
+ .lu_is_1G_o ( dtlb_is_1G ),
+ .lu_hit_o ( dtlb_lu_hit )
+ );
+
+
+ ptw #(
+ .ASID_WIDTH ( ASID_WIDTH )
+ ) i_ptw (
+ .clk_i ( clk_i ),
+ .rst_ni ( rst_ni ),
+ .ptw_active_o ( ptw_active ),
+ .walking_instr_o ( walking_instr ),
+ .ptw_error_o ( ptw_error ),
+ .enable_translation_i ( enable_translation_i ),
+
+ .update_vaddr_o ( update_vaddr ),
+ .itlb_update_o ( update_ptw_itlb ),
+ .dtlb_update_o ( update_ptw_dtlb ),
+
+ .itlb_access_i ( itlb_lu_access ),
+ .itlb_hit_i ( itlb_lu_hit ),
+ .itlb_vaddr_i ( icache_areq_i.fetch_vaddr ),
+
+ .dtlb_access_i ( dtlb_lu_access ),
+ .dtlb_hit_i ( dtlb_lu_hit ),
+ .dtlb_vaddr_i ( lsu_vaddr_i ),
+
+ .req_port_i ( req_port_i ),
+ .req_port_o ( req_port_o ),
+
+ .*
+ );
+
+ // ila_1 i_ila_1 (
+ // .clk(clk_i), // input wire clk
+ // .probe0({req_port_o.address_tag, req_port_o.address_index}),
+ // .probe1(req_port_o.data_req), // input wire [63:0] probe1
+ // .probe2(req_port_i.data_gnt), // input wire [0:0] probe2
+ // .probe3(req_port_i.data_rdata), // input wire [0:0] probe3
+ // .probe4(req_port_i.data_rvalid), // input wire [0:0] probe4
+ // .probe5(ptw_error), // input wire [1:0] probe5
+ // .probe6(update_vaddr), // input wire [0:0] probe6
+ // .probe7(update_ptw_itlb.valid), // input wire [0:0] probe7
+ // .probe8(update_ptw_dtlb.valid), // input wire [0:0] probe8
+ // .probe9(dtlb_lu_access), // input wire [0:0] probe9
+ // .probe10(lsu_vaddr_i), // input wire [0:0] probe10
+ // .probe11(dtlb_lu_hit), // input wire [0:0] probe11
+ // .probe12(itlb_lu_access), // input wire [0:0] probe12
+ // .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0] probe13
+ // .probe14(itlb_lu_hit) // input wire [0:0] probe13
+ // );
+
+ //-----------------------
+ // Instruction Interface
+ //-----------------------
+ logic match_any_execute_region;
+ // The instruction interface is a simple request response interface
+ always_comb begin : instr_interface
+ // MMU disabled: just pass through
+ icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
+ icache_areq_o.fetch_paddr = icache_areq_i.fetch_vaddr; // play through in case we disabled address translation
+ // two potential exception sources:
+ // 1. HPTW threw an exception -> signal with a page fault exception
+ // 2. We got an access error because of insufficient permissions -> throw an access exception
+ icache_areq_o.fetch_exception = '0;
+ // Check whether we are allowed to access this memory region from a fetch perspective
+ iaccess_err = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u)
+ || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u));
+
+ // MMU enabled: address from TLB, request delayed until hit. Error when TLB
+ // hit and no access right or TLB hit and translated address not valid (e.g.
+ // AXI decode error), or when PTW performs walk due to ITLB miss and raises
+ // an error.
+ if (enable_translation_i) begin
+ // we work with SV39, so if VM is enabled, check that all bits [63:38] are equal
+ if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[63:38]) == 1'b1 || (|icache_areq_i.fetch_vaddr[63:38]) == 1'b0)) begin
+ icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1};
+ end
+
+ icache_areq_o.fetch_valid = 1'b0;
+
+ // 4K page
+ icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]};
+ // Mega page
+ if (itlb_is_2M) begin
+ icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12];
+ end
+ // Giga page
+ if (itlb_is_1G) begin
+ icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12];
+ end
+
+ // ---------
+ // ITLB Hit
+ // --------
+ // if we hit the ITLB output the request signal immediately
+ if (itlb_lu_hit) begin
+ icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
+ // we got an access error
+ if (iaccess_err) begin
+ // throw a page fault
+ icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, icache_areq_i.fetch_vaddr, 1'b1};
+ end
+ end else
+ // ---------
+ // ITLB Miss
+ // ---------
+ // watch out for exceptions happening during walking the page table
+ if (ptw_active && walking_instr) begin
+ icache_areq_o.fetch_valid = ptw_error;
+ icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
+ end
+ end
+ // if it didn't match any execute region throw an `Instruction Access Fault`
+ if (!match_any_execute_region) begin
+ icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr, 1'b1};
+ end
+ end
+
+ // check for execute flag on memory
+ assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, icache_areq_o.fetch_paddr);
+
+ //-----------------------
+ // Data Interface
+ //-----------------------
+ logic [63:0] lsu_vaddr_n, lsu_vaddr_q;
+ riscv::pte_t dtlb_pte_n, dtlb_pte_q;
+ exception_t misaligned_ex_n, misaligned_ex_q;
+ logic lsu_req_n, lsu_req_q;
+ logic lsu_is_store_n, lsu_is_store_q;
+ logic dtlb_hit_n, dtlb_hit_q;
+ logic dtlb_is_2M_n, dtlb_is_2M_q;
+ logic dtlb_is_1G_n, dtlb_is_1G_q;
+
+ // check if we need to do translation or if we are always ready (e.g.: we are not translating anything)
+ assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit : 1'b1;
+
+ // The data interface is simpler and only consists of a request/response interface
+ always_comb begin : data_interface
+ // save request and DTLB response
+ lsu_vaddr_n = lsu_vaddr_i;
+ lsu_req_n = lsu_req_i;
+ misaligned_ex_n = misaligned_ex_i;
+ dtlb_pte_n = dtlb_content;
+ dtlb_hit_n = dtlb_lu_hit;
+ lsu_is_store_n = lsu_is_store_i;
+ dtlb_is_2M_n = dtlb_is_2M;
+ dtlb_is_1G_n = dtlb_is_1G;
+
+ lsu_paddr_o = lsu_vaddr_q;
+ lsu_valid_o = lsu_req_q;
+ lsu_exception_o = misaligned_ex_q;
+ // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions
+ misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i;
+
+ // Check if the User flag is set, then we may only access it in supervisor mode
+ // if SUM is enabled
+ daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode
+ (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u); // this is not a user page but we are in user mode and trying to access it
+ // translation is enabled and no misaligned exception occurred
+ if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin
+ lsu_valid_o = 1'b0;
+ // 4K page
+ lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]};
+ // Mega page
+ if (dtlb_is_2M_q) begin
+ lsu_paddr_o[20:12] = lsu_vaddr_q[20:12];
+ end
+ // Giga page
+ if (dtlb_is_1G_q) begin
+ lsu_paddr_o[29:12] = lsu_vaddr_q[29:12];
+ end
+ // ---------
+ // DTLB Hit
+ // --------
+ if (dtlb_hit_q && lsu_req_q) begin
+ lsu_valid_o = 1'b1;
+ // this is a store
+ if (lsu_is_store_q) begin
+ // check if the page is write-able and we are not violating privileges
+ // also check if the dirty flag is set
+ if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin
+ lsu_exception_o = {riscv::STORE_PAGE_FAULT, lsu_vaddr_q, 1'b1};
+ end
+
+ // this is a load, check for sufficient access privileges - throw a page fault if necessary
+ end else if (daccess_err) begin
+ lsu_exception_o = {riscv::LOAD_PAGE_FAULT, lsu_vaddr_q, 1'b1};
+ end
+ end else
+
+ // ---------
+ // DTLB Miss
+ // ---------
+ // watch out for exceptions
+ if (ptw_active && !walking_instr) begin
+ // page table walker threw an exception
+ if (ptw_error) begin
+ // an error makes the translation valid
+ lsu_valid_o = 1'b1;
+ // the page table walker can only throw page faults
+ if (lsu_is_store_q) begin
+ lsu_exception_o = {riscv::STORE_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
+ end else begin
+ lsu_exception_o = {riscv::LOAD_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
+ end
+ end
+ end
+ end
+ end
+ // ----------
+ // Registers
+ // ----------
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (~rst_ni) begin
+ lsu_vaddr_q <= '0;
+ lsu_req_q <= '0;
+ misaligned_ex_q <= '0;
+ dtlb_pte_q <= '0;
+ dtlb_hit_q <= '0;
+ lsu_is_store_q <= '0;
+ dtlb_is_2M_q <= '0;
+ dtlb_is_1G_q <= '0;
+ end else begin
+ lsu_vaddr_q <= lsu_vaddr_n;
+ lsu_req_q <= lsu_req_n;
+ misaligned_ex_q <= misaligned_ex_n;
+ dtlb_pte_q <= dtlb_pte_n;
+ dtlb_hit_q <= dtlb_hit_n;
+ lsu_is_store_q <= lsu_is_store_n;
+ dtlb_is_2M_q <= dtlb_is_2M_n;
+ dtlb_is_1G_q <= dtlb_is_1G_n;
+ end
+ end
+#docstring_end
+endmodule
--- /dev/null
+# this file has been generated by sv2nmigen
+
+from nmigen import Signal, Module, Const, Cat, Elaboratable
+
+
+
+class tlb(Elaboratable):
+
+ def __init__(self):
+ self.clk_i = Signal() # input
+ self.rst_ni = Signal() # input
+ self.flush_i = Signal() # input
+ self.lu_access_i = Signal() # input
+ self.lu_asid_i = Signal(ASID_WIDTH) # input
+ self.lu_vaddr_i = Signal(64) # input
+ self.lu_is_2M_o = Signal() # output
+ self.lu_is_1G_o = Signal() # output
+ self.lu_hit_o = Signal() # output
+ def elaborate(self, platform=None):
+ m = Module()
+ return m
+
+#// 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.
+#//
+#// Author: David Schaffenrath, TU Graz
+#// Author: Florian Zaruba, ETH Zurich
+#// Date: 21.4.2017
+#// Description: Translation Lookaside Buffer, SV39
+#// fully set-associative
+#
+#//import ariane_pkg::*;
+#
+#module tlb #(
+# parameter int TLB_ENTRIES = 4,
+# parameter int ASID_WIDTH = 1
+# )(
+# input logic clk_i, // Clock
+# input logic rst_ni, // Asynchronous reset active low
+# input logic flush_i, // Flush signal
+# // Update TLB
+# //input tlb_update_t update_i,
+# // Lookup signals
+# input logic lu_access_i,
+# input logic [ASID_WIDTH-1:0] lu_asid_i,
+# input logic [63:0] lu_vaddr_i,
+# //output riscv::pte_t lu_content_o,
+# output logic lu_is_2M_o,
+# output logic lu_is_1G_o,
+# output logic lu_hit_o
+#);
+#
+""" #docstring_begin
+ // SV39 defines three levels of page tables
+ struct packed {
+ logic [ASID_WIDTH-1:0] asid;
+ logic [8:0] vpn2;
+ logic [8:0] vpn1;
+ logic [8:0] vpn0;
+ logic is_2M;
+ logic is_1G;
+ logic valid;
+ } [TLB_ENTRIES-1:0] tags_q, tags_n;
+
+ riscv::pte_t [TLB_ENTRIES-1:0] content_q, content_n;
+ logic [8:0] vpn0, vpn1, vpn2;
+ logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic
+ logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy
+ //-------------
+ // Translation
+ //-------------
+ always_comb begin : translation
+ vpn0 = lu_vaddr_i[20:12];
+ vpn1 = lu_vaddr_i[29:21];
+ vpn2 = lu_vaddr_i[38:30];
+
+ // default assignment
+ lu_hit = '{default: 0};
+ lu_hit_o = 1'b0;
+ lu_content_o = '{default: 0};
+ lu_is_1G_o = 1'b0;
+ lu_is_2M_o = 1'b0;
+
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ // first level match, this may be a giga page, check the ASID flags as well
+ if (tags_q[i].valid && lu_asid_i == tags_q[i].asid && vpn2 == tags_q[i].vpn2) begin
+ // second level
+ if (tags_q[i].is_1G) begin
+ lu_is_1G_o = 1'b1;
+ lu_content_o = content_q[i];
+ lu_hit_o = 1'b1;
+ lu_hit[i] = 1'b1;
+ // not a giga page hit so check further
+ end else if (vpn1 == tags_q[i].vpn1) begin
+ // this could be a 2 mega page hit or a 4 kB hit
+ // output accordingly
+ if (tags_q[i].is_2M || vpn0 == tags_q[i].vpn0) begin
+ lu_is_2M_o = tags_q[i].is_2M;
+ lu_content_o = content_q[i];
+ lu_hit_o = 1'b1;
+ lu_hit[i] = 1'b1;
+ end
+ end
+ end
+ end
+ end
+
+ // ------------------
+ // Update and Flush
+ // ------------------
+ always_comb begin : update_flush
+ tags_n = tags_q;
+ content_n = content_q;
+
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ if (flush_i) begin
+ // invalidate logic
+ if (lu_asid_i == 1'b0) // flush everything if ASID is 0
+ tags_n[i].valid = 1'b0;
+ else if (lu_asid_i == tags_q[i].asid) // just flush entries from this ASID
+ tags_n[i].valid = 1'b0;
+
+ // normal replacement
+ end else if (update_i.valid & replace_en[i]) begin
+ // update tag array
+ tags_n[i] = '{
+ asid: update_i.asid,
+ vpn2: update_i.vpn [26:18],
+ vpn1: update_i.vpn [17:9],
+ vpn0: update_i.vpn [8:0],
+ is_1G: update_i.is_1G,
+ is_2M: update_i.is_2M,
+ valid: 1'b1
+ };
+ // and content as well
+ content_n[i] = update_i.content;
+ end
+ end
+ end
+
+ // -----------------------------------------------
+ // PLRU - Pseudo Least Recently Used Replacement
+ // -----------------------------------------------
+ logic[2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
+ always_comb begin : plru_replacement
+ plru_tree_n = plru_tree_q;
+ // The PLRU-tree indexing:
+ // lvl0 0
+ // / \
+ // / \
+ // lvl1 1 2
+ // / \ / \
+ // lvl2 3 4 5 6
+ // / \ /\/\ /\
+ // ... ... ... ...
+ // Just predefine which nodes will be set/cleared
+ // E.g. for a TLB with 8 entries, the for-loop is semantically
+ // equivalent to the following pseudo-code:
+ // unique case (1'b1)
+ // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1};
+ // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0};
+ // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1};
+ // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0};
+ // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1};
+ // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0};
+ // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1};
+ // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0};
+ // default: begin /* No hit */ end
+ // endcase
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ automatic int unsigned idx_base, shift, new_index;
+ // we got a hit so update the pointer as it was least recently used
+ if (lu_hit[i] & lu_access_i) begin
+ // Set the nodes to the values we would expect
+ for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
+ idx_base = $unsigned((2**lvl)-1);
+ // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
+ shift = $clog2(TLB_ENTRIES) - lvl;
+ // to circumvent the 32 bit integer arithmetic assignment
+ new_index = ~((i >> (shift-1)) & 32'b1);
+ plru_tree_n[idx_base + (i >> shift)] = new_index[0];
+ end
+ end
+ end
+ // Decode tree to write enable signals
+ // Next for-loop basically creates the following logic for e.g. an 8 entry
+ // TLB (note: pseudo-code obviously):
+ // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1}
+ // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0}
+ // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1}
+ // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0}
+ // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1}
+ // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0}
+ // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1}
+ // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0}
+ // For each entry traverse the tree. If every tree-node matches,
+ // the corresponding bit of the entry's index, this is
+ // the next entry to replace.
+ for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin
+ automatic logic en;
+ automatic int unsigned idx_base, shift, new_index;
+ en = 1'b1;
+ for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
+ idx_base = $unsigned((2**lvl)-1);
+ // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
+ shift = $clog2(TLB_ENTRIES) - lvl;
+
+ // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
+ new_index = (i >> (shift-1)) & 32'b1;
+ if (new_index[0]) begin
+ en &= plru_tree_q[idx_base + (i>>shift)];
+ end else begin
+ en &= ~plru_tree_q[idx_base + (i>>shift)];
+ end
+ end
+ replace_en[i] = en;
+ end
+ end
+
+ // sequential process
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if(~rst_ni) begin
+ tags_q <= '{default: 0};
+ content_q <= '{default: 0};
+ plru_tree_q <= '{default: 0};
+ end else begin
+ tags_q <= tags_n;
+ content_q <= content_n;
+ plru_tree_q <= plru_tree_n;
+ end
+ end
+ //--------------
+ // Sanity checks
+ //--------------
+
+ //pragma translate_off
+ `ifndef VERILATOR
+
+ initial begin : p_assertions
+ assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
+ else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end
+ assert (ASID_WIDTH >= 1)
+ else begin $error("ASID width must be at least 1"); $stop(); end
+ end
+
+ // Just for checking
+ function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
+ automatic int count = 0;
+ foreach (vector[idx]) begin
+ count += vector[idx];
+ end
+ return count;
+ endfunction
+
+ assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
+ else begin $error("More then one hit in TLB!"); $stop(); end
+ assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
+ else begin $error("More then one TLB entry selected for next replace!"); $stop(); end
+
+ `endif
+ //pragma translate_on
+"""
+#endmodule
+#
+#
--- /dev/null
+// 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.
+//
+// Author: David Schaffenrath, TU Graz
+// Author: Florian Zaruba, ETH Zurich
+// Date: 21.4.2017
+// Description: Translation Lookaside Buffer, SV39
+// fully set-associative
+
+//import ariane_pkg::*;
+
+module tlb #(
+ parameter int TLB_ENTRIES = 4,
+ parameter int ASID_WIDTH = 1
+ )(
+ input logic clk_i, // Clock
+ input logic rst_ni, // Asynchronous reset active low
+ input logic flush_i, // Flush signal
+ // Update TLB
+ //input tlb_update_t update_i,
+ // Lookup signals
+ input logic lu_access_i,
+ input logic [ASID_WIDTH-1:0] lu_asid_i,
+ input logic [63:0] lu_vaddr_i,
+ //output riscv::pte_t lu_content_o,
+ output logic lu_is_2M_o,
+ output logic lu_is_1G_o,
+ output logic lu_hit_o
+);
+
+ #docstring_begin
+ // SV39 defines three levels of page tables
+ struct packed {
+ logic [ASID_WIDTH-1:0] asid;
+ logic [8:0] vpn2;
+ logic [8:0] vpn1;
+ logic [8:0] vpn0;
+ logic is_2M;
+ logic is_1G;
+ logic valid;
+ } [TLB_ENTRIES-1:0] tags_q, tags_n;
+
+ riscv::pte_t [TLB_ENTRIES-1:0] content_q, content_n;
+ logic [8:0] vpn0, vpn1, vpn2;
+ logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic
+ logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy
+ //-------------
+ // Translation
+ //-------------
+ always_comb begin : translation
+ vpn0 = lu_vaddr_i[20:12];
+ vpn1 = lu_vaddr_i[29:21];
+ vpn2 = lu_vaddr_i[38:30];
+
+ // default assignment
+ lu_hit = '{default: 0};
+ lu_hit_o = 1'b0;
+ lu_content_o = '{default: 0};
+ lu_is_1G_o = 1'b0;
+ lu_is_2M_o = 1'b0;
+
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ // first level match, this may be a giga page, check the ASID flags as well
+ if (tags_q[i].valid && lu_asid_i == tags_q[i].asid && vpn2 == tags_q[i].vpn2) begin
+ // second level
+ if (tags_q[i].is_1G) begin
+ lu_is_1G_o = 1'b1;
+ lu_content_o = content_q[i];
+ lu_hit_o = 1'b1;
+ lu_hit[i] = 1'b1;
+ // not a giga page hit so check further
+ end else if (vpn1 == tags_q[i].vpn1) begin
+ // this could be a 2 mega page hit or a 4 kB hit
+ // output accordingly
+ if (tags_q[i].is_2M || vpn0 == tags_q[i].vpn0) begin
+ lu_is_2M_o = tags_q[i].is_2M;
+ lu_content_o = content_q[i];
+ lu_hit_o = 1'b1;
+ lu_hit[i] = 1'b1;
+ end
+ end
+ end
+ end
+ end
+
+ // ------------------
+ // Update and Flush
+ // ------------------
+ always_comb begin : update_flush
+ tags_n = tags_q;
+ content_n = content_q;
+
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ if (flush_i) begin
+ // invalidate logic
+ if (lu_asid_i == 1'b0) // flush everything if ASID is 0
+ tags_n[i].valid = 1'b0;
+ else if (lu_asid_i == tags_q[i].asid) // just flush entries from this ASID
+ tags_n[i].valid = 1'b0;
+
+ // normal replacement
+ end else if (update_i.valid & replace_en[i]) begin
+ // update tag array
+ tags_n[i] = '{
+ asid: update_i.asid,
+ vpn2: update_i.vpn [26:18],
+ vpn1: update_i.vpn [17:9],
+ vpn0: update_i.vpn [8:0],
+ is_1G: update_i.is_1G,
+ is_2M: update_i.is_2M,
+ valid: 1'b1
+ };
+ // and content as well
+ content_n[i] = update_i.content;
+ end
+ end
+ end
+
+ // -----------------------------------------------
+ // PLRU - Pseudo Least Recently Used Replacement
+ // -----------------------------------------------
+ logic[2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
+ always_comb begin : plru_replacement
+ plru_tree_n = plru_tree_q;
+ // The PLRU-tree indexing:
+ // lvl0 0
+ // / \
+ // / \
+ // lvl1 1 2
+ // / \ / \
+ // lvl2 3 4 5 6
+ // / \ /\/\ /\
+ // ... ... ... ...
+ // Just predefine which nodes will be set/cleared
+ // E.g. for a TLB with 8 entries, the for-loop is semantically
+ // equivalent to the following pseudo-code:
+ // unique case (1'b1)
+ // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1};
+ // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0};
+ // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1};
+ // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0};
+ // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1};
+ // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0};
+ // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1};
+ // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0};
+ // default: begin /* No hit */ end
+ // endcase
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ automatic int unsigned idx_base, shift, new_index;
+ // we got a hit so update the pointer as it was least recently used
+ if (lu_hit[i] & lu_access_i) begin
+ // Set the nodes to the values we would expect
+ for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
+ idx_base = $unsigned((2**lvl)-1);
+ // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
+ shift = $clog2(TLB_ENTRIES) - lvl;
+ // to circumvent the 32 bit integer arithmetic assignment
+ new_index = ~((i >> (shift-1)) & 32'b1);
+ plru_tree_n[idx_base + (i >> shift)] = new_index[0];
+ end
+ end
+ end
+ // Decode tree to write enable signals
+ // Next for-loop basically creates the following logic for e.g. an 8 entry
+ // TLB (note: pseudo-code obviously):
+ // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1}
+ // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0}
+ // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1}
+ // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0}
+ // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1}
+ // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0}
+ // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1}
+ // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0}
+ // For each entry traverse the tree. If every tree-node matches,
+ // the corresponding bit of the entry's index, this is
+ // the next entry to replace.
+ for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin
+ automatic logic en;
+ automatic int unsigned idx_base, shift, new_index;
+ en = 1'b1;
+ for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
+ idx_base = $unsigned((2**lvl)-1);
+ // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
+ shift = $clog2(TLB_ENTRIES) - lvl;
+
+ // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
+ new_index = (i >> (shift-1)) & 32'b1;
+ if (new_index[0]) begin
+ en &= plru_tree_q[idx_base + (i>>shift)];
+ end else begin
+ en &= ~plru_tree_q[idx_base + (i>>shift)];
+ end
+ end
+ replace_en[i] = en;
+ end
+ end
+
+ // sequential process
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if(~rst_ni) begin
+ tags_q <= '{default: 0};
+ content_q <= '{default: 0};
+ plru_tree_q <= '{default: 0};
+ end else begin
+ tags_q <= tags_n;
+ content_q <= content_n;
+ plru_tree_q <= plru_tree_n;
+ end
+ end
+ //--------------
+ // Sanity checks
+ //--------------
+
+ //pragma translate_off
+ `ifndef VERILATOR
+
+ initial begin : p_assertions
+ assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
+ else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end
+ assert (ASID_WIDTH >= 1)
+ else begin $error("ASID width must be at least 1"); $stop(); end
+ end
+
+ // Just for checking
+ function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
+ automatic int count = 0;
+ foreach (vector[idx]) begin
+ count += vector[idx];
+ end
+ return count;
+ endfunction
+
+ assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
+ else begin $error("More then one hit in TLB!"); $stop(); end
+ assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
+ else begin $error("More then one TLB entry selected for next replace!"); $stop(); end
+
+ `endif
+ //pragma translate_on
+#docstring_end
+endmodule
from lib2to3.pgen2 import token
from lib2to3.pygram import python_symbols as syms
-yacc1_debug = 0
-yacc2_debug = 0
-parse_debug = 0
+yacc1_debug = 1
+yacc2_debug = 1
+parse_debug = 1
from ply import yacc, lex
--- /dev/null
+quote = "\""
+import sys
+class Preprocessor:
+ def __init__(self):
+ self.docstrings = []
+ def removeComments(self,data):
+ #print(data)
+ ret = ""
+ in_docstring = False
+ docstring = ""
+ for line in data.split("\n"):
+ docstring_end = ("#docstring_end" in line)
+ if "#docstring_begin" in line:
+ ret += "//DOCSTRING_PLACEHOLDER\n"
+ in_docstring = True
+ docstring = ""
+ if(in_docstring==False):
+ ret += line + "\n"
+ else:
+ if(not docstring_end): docstring += line + "\n"
+ if(docstring_end):
+ in_docstring = False
+ self.docstrings += [docstring]
+ print(docstring)
+ return ret
+ def insertDocstrings(self,data):
+ ret =""
+ docstring_counter = 0
+ for line in data.split("\n"):
+ if("//DOCSTRING_PLACEHOLDER" in line):
+ ret += quote*3
+ ret += self.docstrings[docstring_counter]
+ ret += quote*3
+ ret += "\n"
+ docstring_counter += 1
+ else:
+ ret += "#"+line + "\n"
+ return ret
+
import lexor
import parse_sv
import absyn
+import pypreproc
from ply import *
import os
print(outputfn)
with open(fname) as f:
data = f.read()
+ preproc = pypreproc.Preprocessor()
+ data = preproc.removeComments(data)
parse_sv.absyn = absyn.Absyn(outputfn)
yacc.parse(data, debug=parse_sv.yacc2_debug)
print("No Error")
- parse_sv.absyn.appendComments(data)
+ parse_sv.absyn.appendComments(preproc.insertDocstrings(data))