├── doc
├── .DS_Store
├── PTW_fsm.png
├── ptw
│ ├── PTW Spec.pdf
│ ├── ptw-FSM.drawio.png
│ ├── ptw-Página-3.drawio.png
│ └── ptw-PTW Diagram.drawio-4.png
├── tlb
│ └── TLB_Spec.pdf
└── block_diagram.png
├── CONTRIBUTORS.md
├── filelist.f
├── README.md
├── .gitlab
└── issue_templates
│ └── issue_template.md
├── rtl
├── bsc_mmu.sv
├── ptw
│ ├── ptw_arb.sv
│ └── ptw.sv
├── dcache_adapters
│ └── bsc_mmu_hpdc_adapter.sv
├── common
│ └── pseudoLRU.sv
├── tlb
│ ├── tlb_wrapper.sv
│ └── tlb.sv
└── mmu_top.sv
├── LICENSE
└── includes
└── mmu_pkg.sv
/doc/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/.DS_Store
--------------------------------------------------------------------------------
/doc/PTW_fsm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/PTW_fsm.png
--------------------------------------------------------------------------------
/doc/ptw/PTW Spec.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/ptw/PTW Spec.pdf
--------------------------------------------------------------------------------
/doc/tlb/TLB_Spec.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/tlb/TLB_Spec.pdf
--------------------------------------------------------------------------------
/doc/block_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/block_diagram.png
--------------------------------------------------------------------------------
/doc/ptw/ptw-FSM.drawio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/ptw/ptw-FSM.drawio.png
--------------------------------------------------------------------------------
/doc/ptw/ptw-Página-3.drawio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/ptw/ptw-Página-3.drawio.png
--------------------------------------------------------------------------------
/doc/ptw/ptw-PTW Diagram.drawio-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bsc-loca/mmu/HEAD/doc/ptw/ptw-PTW Diagram.drawio-4.png
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | - Arnau Bigas Soldevilla (arnau.bigas@bsc.es)
2 | - Javier Salamero (javier.salamero@bsc.es)
3 | - Xavier Carril (xavier.carril@bsc.es)
--------------------------------------------------------------------------------
/filelist.f:
--------------------------------------------------------------------------------
1 | +incdir+./includes
2 | ./includes/mmu_pkg.sv
3 | ./rtl/common/pseudoLRU.sv
4 | ./rtl/ptw/ptw_arb.sv
5 | ./rtl/ptw/ptw.sv
6 | ./rtl/tlb/tlb.sv
7 | ./rtl/bsc_mmu.sv
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Memory Management Unit (MMU)
2 |
3 | This repository includes a Translation Lookaside Buffer (TLB) and a Page Table Walker, both forming a Memory Management Unit. The modules are designed to be used in a RISC-V processor, following the page-based 39-bit Virtual-Memory System specification (SV39).
4 |
5 |
6 |
7 |
8 |
9 | #### - TLB characteristics:
10 | - Page-based SV39 mode by default
11 | - Virtual Address (39-bit) to Physical Address (56-bit) translation
12 | - TLB entries are parametrizable (8 by default) in includes/mmu_pkg.sv
13 | - Checks Access Permissions
14 | - Raises Page Fault Exceptions
15 |
16 | #### - PTW characteristics:
17 | - Support for kilo (4KB), mega (2MB) and giga (1GB) page-size
18 | - 3 level tree hierarchy
19 | - Mealy FSM following the steps mentioned in the "Virtual Address Translation Process" chapter (4.3.2) of the [RISC-V privileged specs](https://riscv.org/technical/specifications/):
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.gitlab/issue_templates/issue_template.md:
--------------------------------------------------------------------------------
1 | # Issue Report Template
2 |
3 | ### **Description**
4 | - **Summary:**
5 | Describe what the issue is about and the consequences of delaying its resolution.
6 |
7 | ### **Environment**
8 | - **Details:**
9 | Provide a detailed description of all the circumstances present when the issue was found. Include tools used, data, and history, ensuring to be exhaustive.
10 |
11 | ### **Conditions for Reproduction**
12 | - **Steps to Reproduce:**
13 | Explain all the necessary steps to execute and reproduce the issue. If it does not always happen, specify under which circumstances it occurs, or at least the ones with higher probability of happening.
14 |
15 | ### **Hypothesis / Comments**
16 | - **Additional Information:**
17 | Use this field to add any additional information, guesses about the cause, or comments that might help in resolving the issue. This can be edited by anyone involved in the issue.
18 |
19 | ### **As a creator of the issue make sure**
20 | - Add a severity label to the issue
21 | - Set a due date for the issue to be fixed
22 | - Name a verificator (can not be the same as the fixer) here : @xxxx
23 | - Assign the issue to a fixer ( can be yourself as long as you are not the verifier)
--------------------------------------------------------------------------------
/rtl/bsc_mmu.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | module bsc_mmu
22 | import mmu_pkg::*;
23 | #(
24 | )(
25 | input logic clk_i,
26 | input logic rstn_i,
27 |
28 | // iTLB Interface
29 | input cache_tlb_comm_t icache_itlb_comm_i,
30 | output tlb_cache_comm_t itlb_icache_comm_o,
31 |
32 | // dTLB Interface
33 | input cache_tlb_comm_t core_dtlb_comm_i,
34 | output tlb_cache_comm_t dtlb_core_comm_o,
35 |
36 | // CSR Interface
37 | input csr_ptw_comm_t csr_ptw_comm_i,
38 |
39 | // PTW - Memory Interface
40 | output ptw_dmem_comm_t ptw_dmem_comm_o,
41 | input dmem_ptw_comm_t dmem_ptw_comm_i,
42 |
43 | // PMU Events
44 | output logic itlb_access_o,
45 | output logic itlb_miss_o,
46 | output logic dtlb_access_o,
47 | output logic dtlb_miss_o,
48 | output logic pmu_ptw_hit_o,
49 | output logic pmu_ptw_miss_o
50 | );
51 |
52 | // Page Table Walker - iTLB/dTLB Connections
53 | tlb_ptw_comm_t itlb_ptw_comm, dtlb_ptw_comm;
54 | ptw_tlb_comm_t ptw_itlb_comm, ptw_dtlb_comm;
55 |
56 | tlb itlb (
57 | .clk_i(clk_i),
58 | .rstn_i(rstn_i),
59 | .cache_tlb_comm_i(icache_itlb_comm_i),
60 | .tlb_cache_comm_o(itlb_icache_comm_o),
61 | .ptw_tlb_comm_i(ptw_itlb_comm),
62 | .tlb_ptw_comm_o(itlb_ptw_comm),
63 | .pmu_tlb_access_o(itlb_access_o),
64 | .pmu_tlb_miss_o(itlb_miss_o)
65 | );
66 |
67 | tlb dtlb (
68 | .clk_i(clk_i),
69 | .rstn_i(rstn_i),
70 | .cache_tlb_comm_i(core_dtlb_comm_i),
71 | .tlb_cache_comm_o(dtlb_core_comm_o),
72 | .ptw_tlb_comm_i(ptw_dtlb_comm),
73 | .tlb_ptw_comm_o(dtlb_ptw_comm),
74 | .pmu_tlb_access_o(dtlb_access_o),
75 | .pmu_tlb_miss_o(dtlb_miss_o )
76 | );
77 |
78 | ptw ptw_inst (
79 | .clk_i(clk_i),
80 | .rstn_i(rstn_i),
81 |
82 | // iTLB request-response
83 | .itlb_ptw_comm_i(itlb_ptw_comm),
84 | .ptw_itlb_comm_o(ptw_itlb_comm),
85 |
86 | // dTLB request-response
87 | .dtlb_ptw_comm_i(dtlb_ptw_comm),
88 | .ptw_dtlb_comm_o(ptw_dtlb_comm),
89 |
90 | // dmem request-response
91 | .dmem_ptw_comm_i(dmem_ptw_comm_i),
92 | .ptw_dmem_comm_o(ptw_dmem_comm_o),
93 |
94 | // csr interface
95 | .csr_ptw_comm_i(csr_ptw_comm_i),
96 |
97 | // pmu interface
98 | .pmu_ptw_hit_o(pmu_ptw_hit_o),
99 | .pmu_ptw_miss_o(pmu_ptw_miss_o)
100 | );
101 |
102 | endmodule
103 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Solderpad Hardware License v2.1
2 |
3 | This license operates as a wraparound license to the Apache License
4 | Version 2.0 (the “Apache License”) and incorporates the terms and
5 | conditions of the Apache License (which can be found here:
6 | http://apache.org/licenses/LICENSE-2.0), with the following additions and
7 | modifications. It must be read in conjunction with the Apache License.
8 | Section 1 below modifies definitions and terminology in the Apache
9 | License and Section 2 below replaces Section 2 of the Apache License.
10 | The Appendix replaces the Appendix in the Apache License. You may, at
11 | your option, choose to treat any Work released under this license as
12 | released under the Apache License (thus ignoring all sections written
13 | below entirely).
14 |
15 | 1. Terminology in the Apache License is supplemented or modified as
16 | follows:
17 |
18 | “Authorship”: any reference to ‘authorship’ shall be taken to read
19 | “authorship or design”.
20 |
21 | “Copyright owner”: any reference to ‘copyright owner’ shall be taken to
22 | read “Rights owner”.
23 |
24 | “Copyright statement”: the reference to ‘copyright statement’ shall be
25 | taken to read ‘copyright or other statement pertaining to Rights’.
26 |
27 | The following new definition shall be added to the Definitions section of
28 | the Apache License:
29 |
30 | “Rights” means copyright and any similar right including design right
31 | (whether registered or unregistered), rights in semiconductor
32 | topographies (mask works) and database rights (but excluding Patents and
33 | Trademarks).
34 |
35 | The following definitions shall replace the corresponding definitions in
36 | the Apache License:
37 |
38 | “License” shall mean this Solderpad Hardware License version 2.1, being
39 | the terms and conditions for use, manufacture, instantiation, adaptation,
40 | reproduction, and distribution as defined by Sections 1 through 9 of this
41 | document.
42 |
43 | “Licensor” shall mean the owner of the Rights or entity authorized by the
44 | owner of the Rights that is granting the License.
45 |
46 | “Derivative Works” shall mean any work, whether in Source or Object form,
47 | that is based on (or derived from) the Work and for which the editorial
48 | revisions, annotations, elaborations, or other modifications represent,
49 | as a whole, an original work of authorship or design. For the purposes of
50 | this License, Derivative Works shall not include works that remain
51 | reversibly separable from, or merely link (or bind by name) or physically
52 | connect to or interoperate with the Work and Derivative Works thereof.
53 |
54 | “Object” form shall mean any form resulting from mechanical
55 | transformation or translation of a Source form or the application of a
56 | Source form to physical material, including but not limited to compiled
57 | object code, generated documentation, the instantiation of a hardware
58 | design or physical object or material and conversions to other media
59 | types, including intermediate forms such as bytecodes, FPGA bitstreams,
60 | moulds, artwork and semiconductor topographies (mask works).
61 |
62 | “Source” form shall mean the preferred form for making modifications,
63 | including but not limited to source code, net lists, board layouts, CAD
64 | files, documentation source, and configuration files.
65 |
66 | “Work” shall mean the work of authorship or design, whether in Source or
67 | Object form, made available under the License, as indicated by a notice
68 | relating to Rights that is included in or attached to the work (an
69 | example is provided in the Appendix below).
70 |
71 | 2. Grant of License. Subject to the terms and conditions of this License,
72 | each Contributor hereby grants to You a perpetual, worldwide,
73 | non-exclusive, no-charge, royalty-free, irrevocable license under the
74 | Rights to reproduce, prepare Derivative Works of, make, adapt, repair,
75 | publicly display, publicly perform, sublicense, and distribute the Work
76 | and such Derivative Works in Source or Object form and do anything in
77 | relation to the Work as if the Rights did not exist.
78 |
79 | APPENDIX
80 |
81 | Copyright 2023 BSC*
82 | *Barcelona Supercomputing Center (BSC)
83 |
84 | SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
85 |
86 | Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
87 | may not use this file except in compliance with the License, or, at your
88 | option, the Apache License version 2.0. You may obtain a copy of the
89 | License at
90 |
91 | https://solderpad.org/licenses/SHL-2.1/
92 |
93 | Unless required by applicable law or agreed to in writing, any work
94 | distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
95 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
96 | License for the specific language governing permissions and limitations
97 | under the License.
--------------------------------------------------------------------------------
/rtl/ptw/ptw_arb.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | module ptw_arb
22 | import mmu_pkg::*;
23 | #(
24 | )(
25 | input logic clk_i,
26 | input logic rstn_i,
27 |
28 | // iTLB & dTLB interfaces
29 | input tlb_ptw_comm_t itlb_ptw_comm_i,
30 | input tlb_ptw_comm_t dtlb_ptw_comm_i,
31 | output ptw_tlb_comm_t ptw_itlb_comm_o,
32 | output ptw_tlb_comm_t ptw_dtlb_comm_o,
33 |
34 | // PTW interface
35 | input ptw_tlb_comm_t ptw_tlb_comm_i,
36 | output tlb_ptw_comm_t tlb_ptw_comm_o
37 | );
38 |
39 | logic is_req_waiting_d, is_req_waiting_q;
40 | tlb_ptw_comm_t itlb_req_waiting_d, itlb_req_waiting_q;
41 |
42 | typedef enum logic [1:0] {
43 | IDLE,
44 | SERVING_iTLB,
45 | SERVING_dTLB
46 | } arbptw_state;
47 |
48 | arbptw_state current_state, next_state;
49 | always_ff @(posedge clk_i, negedge rstn_i) begin
50 | if (!rstn_i) begin
51 | current_state <= IDLE;
52 | is_req_waiting_q <= 1'b0;
53 | itlb_req_waiting_q <= '0;
54 | end
55 | else begin
56 | current_state <= next_state;
57 | is_req_waiting_q <= is_req_waiting_d;
58 | itlb_req_waiting_q <= itlb_req_waiting_d;
59 | end
60 | end
61 |
62 | always_comb begin
63 | is_req_waiting_d = is_req_waiting_q;
64 | itlb_req_waiting_d = itlb_req_waiting_q;
65 | tlb_ptw_comm_o = '0;
66 | next_state = current_state;
67 |
68 | ptw_itlb_comm_o.resp.valid = 1'b0;
69 | ptw_itlb_comm_o.resp.error = 1'b0;
70 | ptw_itlb_comm_o.resp.pte = '0;
71 | ptw_itlb_comm_o.resp.level = '0;
72 | ptw_dtlb_comm_o.resp.valid = 1'b0;
73 | ptw_dtlb_comm_o.resp.error = 1'b0;
74 | ptw_dtlb_comm_o.resp.pte = '0;
75 | ptw_dtlb_comm_o.resp.level = '0;
76 |
77 | ptw_itlb_comm_o.ptw_ready = 1'b0;
78 | ptw_dtlb_comm_o.ptw_ready = 1'b0;
79 |
80 | ptw_itlb_comm_o.ptw_status = ptw_tlb_comm_i.ptw_status;
81 | ptw_dtlb_comm_o.ptw_status = ptw_tlb_comm_i.ptw_status;
82 |
83 | ptw_itlb_comm_o.invalidate_tlb = ptw_tlb_comm_i.invalidate_tlb;
84 | ptw_dtlb_comm_o.invalidate_tlb = ptw_tlb_comm_i.invalidate_tlb;
85 |
86 | case (current_state)
87 | IDLE : begin
88 | ptw_itlb_comm_o.resp = '0;
89 | ptw_dtlb_comm_o.resp = '0;
90 | ptw_itlb_comm_o.ptw_ready = 1'b1;
91 | ptw_dtlb_comm_o.ptw_ready = 1'b1;
92 | if (dtlb_ptw_comm_i.req.valid && itlb_ptw_comm_i.req.valid) begin
93 | is_req_waiting_d = 1'b1;
94 | itlb_req_waiting_d = itlb_ptw_comm_i;
95 | tlb_ptw_comm_o = dtlb_ptw_comm_i;
96 | next_state = SERVING_dTLB;
97 | end else if (is_req_waiting_q) begin
98 | ptw_itlb_comm_o.ptw_ready = 1'b0;
99 | ptw_dtlb_comm_o.ptw_ready = 1'b0;
100 | next_state = SERVING_iTLB;
101 | end else if (dtlb_ptw_comm_i.req.valid) begin
102 | tlb_ptw_comm_o = dtlb_ptw_comm_i;
103 | next_state = SERVING_dTLB;
104 | end else if (itlb_ptw_comm_i.req.valid) begin
105 | tlb_ptw_comm_o = itlb_ptw_comm_i;
106 | next_state = SERVING_iTLB;
107 | end else begin
108 | tlb_ptw_comm_o = '0;
109 | next_state = IDLE;
110 | end
111 | end
112 | SERVING_iTLB : begin
113 | ptw_itlb_comm_o.resp = ptw_tlb_comm_i.resp;
114 | ptw_dtlb_comm_o.resp = '0;
115 | tlb_ptw_comm_o = (is_req_waiting_q) ? itlb_req_waiting_q : itlb_ptw_comm_i;
116 | if (!ptw_tlb_comm_i.resp.valid) begin
117 | next_state = SERVING_iTLB;
118 | end else begin
119 | next_state = IDLE;
120 | is_req_waiting_d = 1'b0;
121 | end
122 | end
123 | SERVING_dTLB : begin
124 | ptw_itlb_comm_o.resp = '0;
125 | ptw_dtlb_comm_o.resp = ptw_tlb_comm_i.resp;
126 | tlb_ptw_comm_o = dtlb_ptw_comm_i;
127 | if (!ptw_tlb_comm_i.resp.valid) begin
128 | next_state = SERVING_dTLB;
129 | end else begin
130 | next_state = IDLE;
131 | end
132 | end
133 | endcase
134 | end
135 |
136 | endmodule
137 |
--------------------------------------------------------------------------------
/rtl/dcache_adapters/bsc_mmu_hpdc_adapter.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | module bsc_mmu_hpdc_adapter
22 | import mmu_pkg::*;
23 | #(
24 | parameter int unsigned SID,
25 |
26 | parameter type hpdcache_req_t = logic,
27 | parameter type hpdcache_tag_t = logic,
28 | parameter type hpdcache_rsp_t = logic
29 | )(
30 | // PTW interface
31 | output dmem_ptw_comm_t dmem_ptw_comm_o,
32 | input ptw_dmem_comm_t ptw_dmem_comm_i,
33 |
34 | // dCache Interface
35 | input logic req_dcache_ready_i,
36 | output logic req_dcache_valid_o,
37 | output hpdcache_req_t req_dcache_o,
38 | output logic req_dcache_abort_o,
39 | output hpdcache_tag_t req_dcache_tag_o,
40 | output hpdcache_pkg::hpdcache_pma_t req_dcache_pma_o,
41 | input logic rsp_dcache_valid_i,
42 | input hpdcache_rsp_t rsp_dcache_i
43 | );
44 |
45 | // Number of bits used in the request's offset address
46 | localparam int unsigned ADDR_OFFSET_BITS = $bits(req_dcache_o.addr_offset);
47 |
48 | // Number of words in the request
49 | localparam int unsigned REQ_WORDS = $size(req_dcache_o.wdata);
50 |
51 | // Word size
52 | localparam int unsigned WORD_SIZE = $bits(req_dcache_o.wdata[0]);
53 |
54 | // Number of bits used to index the bytes in a word
55 | localparam int unsigned WORD_BYTE_IDX_SIZE = $clog2(WORD_SIZE/8);
56 |
57 | // Number of bits needed to index a byte within the request
58 | localparam int unsigned REQ_INDEX_BITS = $clog2(REQ_WORDS)+WORD_BYTE_IDX_SIZE;
59 |
60 | // MMU CMD that identifies an atomic OR
61 | localparam logic[4:0] MMU_AMO_OR = 5'b01010;
62 |
63 | // PTW to HPDC
64 | always_comb begin
65 | req_dcache_valid_o = ptw_dmem_comm_i.req.valid;
66 |
67 | req_dcache_o.addr_tag = ptw_dmem_comm_i.req.addr[SIZE_VADDR:ADDR_OFFSET_BITS];
68 | req_dcache_o.addr_offset = ptw_dmem_comm_i.req.addr[ADDR_OFFSET_BITS-1:0];
69 | req_dcache_o.op = ((ptw_dmem_comm_i.req.cmd == MMU_AMO_OR) ? hpdcache_pkg::HPDCACHE_REQ_AMO_OR : hpdcache_pkg::HPDCACHE_REQ_LOAD);
70 | req_dcache_o.size = ptw_dmem_comm_i.req.typ[2:0];
71 |
72 | // Put the data in the corresponding word within the request
73 | for (int unsigned i = 0; i < REQ_WORDS; ++i) begin
74 | if (i == ptw_dmem_comm_i.req.addr[REQ_INDEX_BITS-1:WORD_BYTE_IDX_SIZE]) begin
75 | req_dcache_o.wdata[i] = ptw_dmem_comm_i.req.data;
76 | req_dcache_o.be[i] = (ptw_dmem_comm_i.req.cmd == MMU_AMO_OR) ? 8'hff : 8'h00;
77 | end else begin
78 | req_dcache_o.wdata[i] = '0;
79 | req_dcache_o.be[i] = 8'h00;
80 | end
81 | end
82 |
83 | req_dcache_o.sid = SID;
84 | req_dcache_o.tid = '0;
85 | req_dcache_o.need_rsp = 1'b1;
86 | req_dcache_o.phys_indexed = 1'b1;
87 | req_dcache_o.pma.io = 1'b0;
88 | req_dcache_o.pma.uncacheable = 1'b0;
89 | req_dcache_o.pma.wr_policy_hint = hpdcache_pkg::HPDCACHE_WR_POLICY_AUTO;
90 |
91 | req_dcache_abort_o = 1'b0;
92 | req_dcache_tag_o = '0;
93 | req_dcache_pma_o = '0;
94 | end
95 |
96 | // HPDC to PTW
97 | always_comb begin
98 | dmem_ptw_comm_o.dmem_ready = req_dcache_ready_i;
99 | dmem_ptw_comm_o.resp.valid = rsp_dcache_valid_i;
100 | dmem_ptw_comm_o.resp.nack = '0;
101 | dmem_ptw_comm_o.resp.addr = '0;
102 | dmem_ptw_comm_o.resp.tag_addr = '0;
103 | dmem_ptw_comm_o.resp.cmd = '0;
104 | dmem_ptw_comm_o.resp.typ = '0;
105 | dmem_ptw_comm_o.resp.replay = '0;
106 | dmem_ptw_comm_o.resp.has_data = '0;
107 | dmem_ptw_comm_o.resp.data_subw = '0;
108 | dmem_ptw_comm_o.resp.store_data = '0;
109 | dmem_ptw_comm_o.resp.rnvalid = '0;
110 | dmem_ptw_comm_o.resp.rnext = '0;
111 | dmem_ptw_comm_o.resp.xcpt_ma_ld = '0;
112 | dmem_ptw_comm_o.resp.xcpt_ma_st = '0;
113 | dmem_ptw_comm_o.resp.xcpt_pf_ld = '0;
114 | dmem_ptw_comm_o.resp.xcpt_pf_st = '0;
115 | dmem_ptw_comm_o.resp.ordered = '0;
116 | dmem_ptw_comm_o.resp.data =
117 | rsp_dcache_i.rdata[ptw_dmem_comm_i.req.addr[REQ_INDEX_BITS-1:WORD_BYTE_IDX_SIZE]];
118 | end
119 |
120 | endmodule
121 |
--------------------------------------------------------------------------------
/rtl/common/pseudoLRU.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | module pseudoLRU #(
22 | parameter int unsigned ENTRIES = 8
23 | )(
24 | input logic clk_i,
25 | input logic rstn_i,
26 |
27 | input logic access_hit_i, // only update the PLRU when there is a hit in the storage mechanism
28 | input logic [$clog2(ENTRIES)-1:0] access_idx_i,
29 | output logic [$clog2(ENTRIES)-1:0] replacement_idx_o
30 | );
31 |
32 | // Decode access_idx
33 | logic [ENTRIES-1:0] access_array;
34 | logic found;
35 | logic found2;
36 |
37 | logic [ENTRIES-1:0] replace_en;
38 |
39 | function logic [$clog2(ENTRIES)-1:0] cast_integer(input [31:0] iter);
40 | cast_integer = iter[$clog2(ENTRIES)-1:0];
41 | endfunction
42 |
43 | always_comb begin
44 | access_array = '0; // don't care if no 'in' bits set
45 | found = 0;
46 | for (int unsigned i = 0; (i < ENTRIES) && (!found); i++) begin
47 | if (i == access_idx_i) begin
48 | access_array[i] = 1'b1;
49 | found = 1;
50 | end
51 | end
52 | end
53 |
54 |
55 | // -----------------------------------------------
56 | // PLRU - Pseudo Least Recently Used Replacement
57 | // -----------------------------------------------
58 | logic [2*(ENTRIES-1)-1:0] plru_tree_q, plru_tree_d;
59 | always_comb begin : plru_replacement
60 | plru_tree_d = plru_tree_q;
61 | // The PLRU-tree indexing:
62 | // lvl0 0
63 | // / \
64 | // / \
65 | // lvl1 1 2
66 | // / \ / \
67 | // lvl2 3 4 5 6
68 | // / \ /\/\ /\
69 | // ... ... ... ...
70 | // Just predefine which nodes will be set/cleared
71 | // E.g. for a TLB with 8 entries, the for-loop is semantically
72 | // equivalent to the following pseudo-code:
73 | // unique case (1'b1)
74 | // lu_hit[7]: plru_tree_d[0, 2, 6] = {1, 1, 1};
75 | // lu_hit[6]: plru_tree_d[0, 2, 6] = {1, 1, 0};
76 | // lu_hit[5]: plru_tree_d[0, 2, 5] = {1, 0, 1};
77 | // lu_hit[4]: plru_tree_d[0, 2, 5] = {1, 0, 0};
78 | // lu_hit[3]: plru_tree_d[0, 1, 4] = {0, 1, 1};
79 | // lu_hit[2]: plru_tree_d[0, 1, 4] = {0, 1, 0};
80 | // lu_hit[1]: plru_tree_d[0, 1, 3] = {0, 0, 1};
81 | // lu_hit[0]: plru_tree_d[0, 1, 3] = {0, 0, 0};
82 | // default: begin /* No hit */ end
83 | // endcase
84 | for (int unsigned i = 0; i < ENTRIES; i++) begin
85 | automatic int unsigned idx_base = 0;
86 | automatic int unsigned shift = 0;
87 | automatic logic [31:0] new_index = '0;
88 | // we got a hit so update the pointer as it was least recently used
89 | if (access_array[i] & access_hit_i) begin
90 | // Set the nodes to the values we would expect
91 | for (int unsigned lvl = 0; lvl < $unsigned($clog2(ENTRIES)); lvl++) begin
92 | idx_base = $unsigned((2**lvl)-1);
93 | // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
94 | shift = $unsigned($clog2(ENTRIES)) - lvl;
95 | // to circumvent the 32 bit integer arithmetic assignment
96 | new_index = ~((i >> (shift-1)) & 32'b1);
97 | plru_tree_d[idx_base + (i >> shift)] = new_index[0];
98 | end
99 | end
100 | end
101 | // Decode tree to write enable signals
102 | // Next for-loop basically creates the following logic for e.g. an 8 entry
103 | // TLB (note: pseudo-code obviously):
104 | // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1}
105 | // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0}
106 | // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1}
107 | // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0}
108 | // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1}
109 | // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0}
110 | // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1}
111 | // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0}
112 | // For each entry traverse the tree. If every tree-node matches,
113 | // the corresponding bit of the entry's index, this is
114 | // the next entry to replace.
115 | for (int unsigned i = 0; i < ENTRIES; i += 1) begin
116 | automatic logic en = 1'b1;
117 | automatic logic [31:0] new_index2 = '0;
118 | automatic int unsigned idx_base, shift;
119 | for (int unsigned lvl = 0; lvl < $unsigned($clog2(ENTRIES)); lvl++) begin
120 | idx_base = $unsigned((2**lvl)-1);
121 | // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
122 | shift = $unsigned($clog2(ENTRIES)) - lvl;
123 |
124 | // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
125 | new_index2 = (i >> (shift-1)) & 32'b1;
126 | if (new_index2[0]) begin
127 | en &= plru_tree_q[idx_base + (i>>shift)];
128 | end else begin
129 | en &= ~plru_tree_q[idx_base + (i>>shift)];
130 | end
131 | end
132 | replace_en[i] = en;
133 | end
134 | end
135 |
136 | // sequential process
137 | always_ff @(posedge clk_i or negedge rstn_i) begin
138 | if (!rstn_i) begin
139 | plru_tree_q <= '0;
140 | end else begin
141 | plru_tree_q <= plru_tree_d;
142 | end
143 | end
144 |
145 | // Encode replace_en
146 | always_comb begin
147 | replacement_idx_o = '0; // don't care if no 'in' bits set
148 | found2 = 1'b0;
149 | for (int unsigned iter = 0; (iter < $unsigned(ENTRIES)) && (!found2); iter++) begin
150 | if (replace_en[iter] == 1'b1) begin
151 | replacement_idx_o = cast_integer(iter);
152 | found2 = 1'b1;
153 | end
154 | end
155 | end
156 |
157 |
158 | endmodule
--------------------------------------------------------------------------------
/rtl/tlb/tlb_wrapper.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | module tlb_wrapper
22 | import mmu_pkg::*;
23 | #(
24 | )(
25 | input logic clk_i,
26 | input logic rstn_i,
27 | // cache interface
28 | input logic mem_treq_i_valid,
29 | input logic [6:0] mem_treq_i_asid,
30 | input logic [27:0] mem_treq_i_vpn,
31 | input logic mem_treq_i_passthrough,
32 | input logic mem_treq_i_instruction,
33 | input logic mem_treq_i_store,
34 | output logic mem_req_ready_o,
35 | output logic tlb_tresp_o_miss,
36 | output logic [19:0] tlb_tresp_o_ppn,
37 | output logic tlb_tresp_o_xcpt__if,
38 | output logic tlb_tresp_o_xcpt_ld,
39 | output logic tlb_tresp_o_xcpt_st,
40 | output logic [7:0] tlb_tresp_o_hit_idx,
41 | // ptw interface
42 | input logic ptw_req_ready_i,
43 | input logic ptw_invalidate_i,
44 | input logic ptw_resp_i_valid,
45 | input logic ptw_resp_i_error,
46 | input logic [19:0] ptw_resp_i_pte_ppn,
47 | input logic [1:0] ptw_resp_i_pte_rfs,
48 | input logic ptw_resp_i_pte_d,
49 | input logic ptw_resp_i_pte_a,
50 | input logic ptw_resp_i_pte_g,
51 | input logic ptw_resp_i_pte_u,
52 | input logic ptw_resp_i_pte_x,
53 | input logic ptw_resp_i_pte_w,
54 | input logic ptw_resp_i_pte_r,
55 | input logic ptw_resp_i_pte_v,
56 | input logic [1:0] ptw_resp_i_level,
57 | input logic ptw_status_i_sd,
58 | input logic [26:0] ptw_status_i_zero5,
59 | input logic [1:0] ptw_status_i_sxl,
60 | input logic [1:0] ptw_status_i_uxl,
61 | input logic [8:0] ptw_status_i_zero4,
62 | input logic ptw_status_i_tsr,
63 | input logic ptw_status_i_tw,
64 | input logic ptw_status_i_tvm,
65 | input logic ptw_status_i_mxr,
66 | input logic ptw_status_i_sum,
67 | input logic ptw_status_i_mprv,
68 | input logic [1:0] ptw_status_i_xs,
69 | input logic [1:0] ptw_status_i_fs,
70 | input logic [1:0] ptw_status_i_mpp,
71 | input logic [1:0] ptw_status_i_zero3,
72 | input logic ptw_status_i_spp,
73 | input logic ptw_status_i_mpie,
74 | input logic ptw_status_i_zero2,
75 | input logic ptw_status_i_spie,
76 | input logic ptw_status_i_upie,
77 | input logic ptw_status_i_mie,
78 | input logic ptw_status_i_zero1,
79 | input logic ptw_status_i_sie,
80 | input logic ptw_status_i_uie,
81 | output logic ptw_req_o_valid,
82 | output logic [26:0] ptw_req_o_addr,
83 | output logic [1:0] ptw_req_o_prv,
84 | output logic ptw_req_o_store,
85 | output logic ptw_req_o_fetch,
86 | // csr interface
87 | input logic [1:0] csr_priv_lvl_i,
88 | input logic csr_en_translation_i,
89 | // pmu interface
90 | output logic pmu_tlb_access_o,
91 | output logic pmu_tlb_miss_o
92 | );
93 |
94 | cache_tlb_comm_t cache_tlb_comm;
95 | tlb_cache_comm_t tlb_cache_comm;
96 | tlb_ptw_comm_t tlb_ptw_comm;
97 | ptw_tlb_comm_t ptw_tlb_comm;
98 |
99 | // INPUTS
100 | // cache_tlb_comm assignaments
101 | assign cache_tlb_comm.req.valid = mem_treq_i_valid;
102 | assign cache_tlb_comm.req.asid = mem_treq_i_asid;
103 | assign cache_tlb_comm.req.vpn = mem_treq_i_vpn;
104 | assign cache_tlb_comm.req.passthrough = mem_treq_i_passthrough;
105 | assign cache_tlb_comm.req.instruction = mem_treq_i_instruction;
106 | assign cache_tlb_comm.req.store = mem_treq_i_store;
107 | assign cache_tlb_comm.priv_lvl = csr_priv_lvl_i;
108 | assign cache_tlb_comm.vm_enable = csr_en_translation_i;
109 |
110 | // ptw_tlb_comm assignaments
111 | assign ptw_tlb_comm.resp.valid = ptw_resp_i_valid;
112 | assign ptw_tlb_comm.resp.error = ptw_resp_i_error;
113 | assign ptw_tlb_comm.resp.pte.ppn = ptw_resp_i_pte_ppn;
114 | assign ptw_tlb_comm.resp.pte.rfs = ptw_resp_i_pte_rfs;
115 | assign ptw_tlb_comm.resp.pte.d = ptw_resp_i_pte_d;
116 | assign ptw_tlb_comm.resp.pte.a = ptw_resp_i_pte_a;
117 | assign ptw_tlb_comm.resp.pte.g = ptw_resp_i_pte_g;
118 | assign ptw_tlb_comm.resp.pte.u = ptw_resp_i_pte_u;
119 | assign ptw_tlb_comm.resp.pte.x = ptw_resp_i_pte_x;
120 | assign ptw_tlb_comm.resp.pte.w = ptw_resp_i_pte_w;
121 | assign ptw_tlb_comm.resp.pte.r = ptw_resp_i_pte_r;
122 | assign ptw_tlb_comm.resp.pte.v = ptw_resp_i_pte_v;
123 | assign ptw_tlb_comm.resp.level = ptw_resp_i_level;
124 | assign ptw_tlb_comm.ptw_ready = ptw_req_ready_i;
125 | assign ptw_tlb_comm.ptw_status.sd = ptw_status_i_sd;
126 | assign ptw_tlb_comm.ptw_status.zero5 = ptw_status_i_zero5;
127 | assign ptw_tlb_comm.ptw_status.sxl = ptw_status_i_sxl;
128 | assign ptw_tlb_comm.ptw_status.uxl = ptw_status_i_uxl;
129 | assign ptw_tlb_comm.ptw_status.zero4 = ptw_status_i_zero4;
130 | assign ptw_tlb_comm.ptw_status.tsr = ptw_status_i_tsr;
131 | assign ptw_tlb_comm.ptw_status.tw = ptw_status_i_tw;
132 | assign ptw_tlb_comm.ptw_status.tvm = ptw_status_i_tvm;
133 | assign ptw_tlb_comm.ptw_status.mxr = ptw_status_i_mxr;
134 | assign ptw_tlb_comm.ptw_status.sum = ptw_status_i_sum;
135 | assign ptw_tlb_comm.ptw_status.mprv = ptw_status_i_mprv;
136 | assign ptw_tlb_comm.ptw_status.xs = ptw_status_i_xs;
137 | assign ptw_tlb_comm.ptw_status.fs = ptw_status_i_fs;
138 | assign ptw_tlb_comm.ptw_status.mpp = ptw_status_i_mpp;
139 | assign ptw_tlb_comm.ptw_status.zero3 = ptw_status_i_zero3;
140 | assign ptw_tlb_comm.ptw_status.spp = ptw_status_i_spp;
141 | assign ptw_tlb_comm.ptw_status.mpie = ptw_status_i_mpie;
142 | assign ptw_tlb_comm.ptw_status.zero2 = ptw_status_i_zero2;
143 | assign ptw_tlb_comm.ptw_status.spie = ptw_status_i_spie;
144 | assign ptw_tlb_comm.ptw_status.upie = ptw_status_i_upie;
145 | assign ptw_tlb_comm.ptw_status.mie = ptw_status_i_mie;
146 | assign ptw_tlb_comm.ptw_status.zero1 = ptw_status_i_zero1;
147 | assign ptw_tlb_comm.ptw_status.sie = ptw_status_i_sie;
148 | assign ptw_tlb_comm.ptw_status.uie = ptw_status_i_uie;
149 | assign ptw_tlb_comm.invalidate_tlb = ptw_invalidate_i;
150 |
151 | tlb tlb_inst (
152 | .clk_i(clk_i),
153 | .rstn_i(rstn_i),
154 | .cache_tlb_comm_i(cache_tlb_comm),
155 | .tlb_cache_comm_o(tlb_cache_comm),
156 | .ptw_tlb_comm_i(ptw_tlb_comm),
157 | .tlb_ptw_comm_o(tlb_ptw_comm),
158 | .pmu_tlb_access_o(pmu_tlb_access_o),
159 | .pmu_tlb_miss_o(pmu_tlb_miss_o)
160 | );
161 |
162 | // OUTPUTS
163 | assign mem_req_ready_o = tlb_cache_comm.tlb_ready;
164 | assign tlb_tresp_o_miss = tlb_cache_comm.resp.miss;
165 | assign tlb_tresp_o_ppn = tlb_cache_comm.resp.ppn;
166 | assign tlb_tresp_o_xcpt_ld = tlb_cache_comm.resp.xcpt.load;
167 | assign tlb_tresp_o_xcpt_st = tlb_cache_comm.resp.xcpt.store;
168 | assign tlb_tresp_o_xcpt__if = tlb_cache_comm.resp.xcpt.fetch;
169 | assign tlb_tresp_o_hit_idx = '0;
170 | assign ptw_req_o_valid = tlb_ptw_comm.req.valid;
171 | assign ptw_req_o_addr = tlb_ptw_comm.req.vpn;
172 | assign ptw_req_o_prv = tlb_ptw_comm.req.prv;
173 | assign ptw_req_o_store = tlb_ptw_comm.req.store;
174 | assign ptw_req_o_fetch = tlb_ptw_comm.req.fetch;
175 |
176 | endmodule
177 |
--------------------------------------------------------------------------------
/includes/mmu_pkg.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | package mmu_pkg;
22 |
23 | // SV39 Parameters
24 | parameter VPN_SIZE = 27;
25 | parameter PPN_SIZE = 44;
26 | parameter SIZE_VADDR = 39;
27 | parameter ASID_SIZE = 7;
28 | parameter LEVELS = 3;
29 | parameter PAGE_LVL_BITS = 9;
30 | parameter PTESIZE = 8;
31 |
32 | parameter TLB_ENTRIES = 8;
33 | parameter TLB_IDX_SIZE = $clog2(TLB_ENTRIES);
34 |
35 | parameter PTW_CACHE_SIZE = $clog2(LEVELS*2);
36 |
37 | parameter [1:0] GIGA_PAGE = 2'b00; // 1 GiB Page
38 | parameter [1:0] MEGA_PAGE = 2'b01; // 2 MiB Page
39 | parameter [1:0] KILO_PAGE = 2'b10; // 4 KiB Page
40 |
41 | typedef struct packed {
42 | logic [PPN_SIZE-1:0] ppn;
43 | logic [1:0] rfs;
44 | logic d;
45 | logic a;
46 | logic g;
47 | logic u;
48 | logic x;
49 | logic w;
50 | logic r;
51 | logic v;
52 | } pte_t;
53 |
54 | typedef struct packed {
55 | logic sd ;
56 | logic [26:0] zero5 ;
57 | logic [1:0] sxl ;
58 | logic [1:0] uxl ;
59 | logic [8:0] zero4 ;
60 | logic tsr ;
61 | logic tw ;
62 | logic tvm ;
63 | logic mxr ;
64 | logic sum ;
65 | logic mprv ;
66 | logic [1:0] xs ;
67 | logic [1:0] fs ;
68 | logic [1:0] mpp ;
69 | logic [1:0] zero3 ;
70 | logic spp ;
71 | logic mpie ;
72 | logic zero2 ;
73 | logic spie ;
74 | logic upie ;
75 | logic mie ;
76 | logic zero1 ;
77 | logic sie ;
78 | logic uie ;
79 | } csr_mstatus_t;
80 |
81 | ////////////////////////////////
82 | //
83 | // Cache-TLB communication
84 | //
85 | ///////////////////////////////
86 |
87 | // Cache-TLB request
88 | typedef struct packed {
89 | logic valid; // Translation request valid.
90 | logic [ASID_SIZE-1:0] asid; // Address space identifier.
91 | logic [VPN_SIZE:0] vpn; // Virtual page number.
92 | logic passthrough; // Virtual address directly corresponds to physical address, for direct assignment between a virtual machine and the physical device.
93 | logic instruction; // The translation request is for a instruction fetch address.
94 | logic store; // The translation request is for a store address.
95 | } cache_tlb_req_t; // Translation request.
96 |
97 | typedef struct packed {
98 | cache_tlb_req_t req; // Translation request.
99 | logic [1:0] priv_lvl; // Privilege level of the translation: 2'b00 (User), 2'b01 (Supervisor), 2'b11 (Machine).
100 | logic vm_enable; // Memory virtualization is active.
101 | } cache_tlb_comm_t; // Communication from translation requester to TLB.
102 |
103 | typedef struct packed {
104 | logic load; // Load operation.
105 | logic store; // Store operation.
106 | logic fetch; // Fetch operation.
107 | } tlb_ex_t; // Exception origin.
108 |
109 | // TLB-Cache response
110 | typedef struct packed {
111 | logic miss; // If the translation request missed set to 1 Otherwise, the rest of the signals have valid information of the response.
112 | logic [PPN_SIZE-1:0] ppn; // Physical page number.
113 | tlb_ex_t xcpt; // Exceptions produced by the requests.
114 | logic [7:0] hit_idx; // CAM hit index of the translation request.
115 | } tlb_cache_resp_t; // Translation response.
116 |
117 | typedef struct packed {
118 | logic tlb_ready; // The tlb is ready to accept a translation request. If 0 it shouldn't receive any translation request.
119 | tlb_cache_resp_t resp; // Translation response.
120 | } tlb_cache_comm_t; // Communication from TLB to translation requester.
121 |
122 | ////////////////////////////////
123 | //
124 | // TLB-PTW communication
125 | //
126 | ///////////////////////////////
127 |
128 | // TLB-PTW request
129 | typedef struct packed {
130 | logic valid; // Translation request valid.
131 | logic [VPN_SIZE-1:0] vpn; // Virtual page number.
132 | logic [ASID_SIZE-1:0] asid; // Address space identifier.
133 | logic [1:0] prv; // Privilege level of the translation: 2'b00 (User), 2'b01 (Supervisor), 2'b11 (Machine).
134 | logic store; // Store operation.
135 | logic fetch; // Fetch operation.
136 | } tlb_ptw_req_t; // Translation request of the TLB to the PTW.
137 |
138 | typedef struct packed {
139 | tlb_ptw_req_t req; // Translation request of the TLB to the PTW.
140 | } tlb_ptw_comm_t; // Communication from TLB to PTW.
141 |
142 | // PTW-TLB response
143 | typedef struct packed {
144 | logic valid; // Translation response valid.
145 | logic error; // An error has ocurred with the translation request. Only check if the response is valid.
146 | pte_t pte; // Page table entry.
147 | logic [$clog2(LEVELS)-1:0] level; // Page entry size: 2'b00 (1 GiB Page), 2'b01 (2 MiB Page), 2'b10 (4 KiB Page).
148 | } ptw_tlb_resp_t; // PTW response to TLB translation request.
149 |
150 | typedef struct packed {
151 | ptw_tlb_resp_t resp; // PTW response to TLB translation request.
152 | logic ptw_ready; // PTW is ready to receive a translation request.
153 | csr_mstatus_t ptw_status; // mstatus csr register value, sent through the ptw.
154 | logic invalidate_tlb; // Signal to flush all entries in TLB and don't allocate in-progress transactions with the PTW.
155 | } ptw_tlb_comm_t; // Communication from to PTW to TLB.
156 |
157 | ////////////////////////////////
158 | //
159 | // TLB
160 | //
161 | ///////////////////////////////
162 |
163 | typedef struct packed {
164 | logic ur; // User read permission.
165 | logic uw; // User write permission.
166 | logic ux; // User execute permission.
167 | logic sr; // Supervisor read permission.
168 | logic sw; // Supervisor write permission.
169 | logic sx; // Supervisor execute permission.
170 | } tlb_entry_permissions_t; // TLB page entry permissions.
171 |
172 | typedef struct packed {
173 | logic [VPN_SIZE-1:0] vpn; // Virtual page number.
174 | logic [ASID_SIZE-1:0] asid; // Address space identifier.
175 | logic [PPN_SIZE-1:0] ppn; // Physical page number.
176 | logic [1:0] level; // Page entry size: 2'b00 (1 GiB Page), 2'b01 (2 MiB Page), 2'b10 (4 KiB Page).
177 | logic dirty; // The page entry is set as dirty.
178 | logic access; // The page entry has been accessed.
179 | tlb_entry_permissions_t perms; // TLB page entry permissions
180 | logic valid; // The tlb entry is valid, set to 1 when the PTW sends a translation response without errors.
181 | logic nempty; // The tlb entry is not empty, when the PTW sends a translation response that we don't have to ignore, it is set to 1.
182 | } tlb_entry_t; // TLB page entry.
183 |
184 | typedef struct packed {
185 | logic [VPN_SIZE-1:0] vpn; // Virtual page number.
186 | logic [ASID_SIZE-1:0] asid; // Address space identifier.
187 | logic store; // Store operation.
188 | logic fetch; // Fetch operation.
189 | logic [TLB_IDX_SIZE-1:0] write_idx; // Index where the page requested to the PTW will be stored in the TLB's CAM.
190 | } tlb_req_tmp_storage_t; // Stored information of the translation request saved on a miss.
191 |
192 | ////////////////////////////////
193 | //
194 | // PTW
195 | //
196 | ///////////////////////////////
197 | //
198 | typedef struct packed {
199 | logic valid;
200 | logic [SIZE_VADDR:0] tags;
201 | logic [PPN_SIZE-1:0] data;
202 | } ptw_ptecache_entry_t;
203 |
204 | ////////////////////////////////
205 | //
206 | // PTW Communications
207 | //
208 | ///////////////////////////////
209 |
210 | // PTW-DMEM request
211 | typedef struct packed {
212 | logic valid ;
213 | logic [SIZE_VADDR:0] addr ;
214 | logic [4:0] cmd ;
215 | logic [3:0] typ ;
216 | logic kill ;
217 | logic phys ;
218 | logic [63:0] data ;
219 | } ptw_dmem_req_t;
220 |
221 | typedef struct packed {
222 | ptw_dmem_req_t req;
223 | } ptw_dmem_comm_t;
224 |
225 | // PTW-DMEM response
226 | typedef struct packed {
227 | logic valid ;
228 | logic [SIZE_VADDR:0] addr ;
229 | logic [7:0] tag_addr ;
230 | logic [4:0] cmd ;
231 | logic [3:0] typ ;
232 | logic [63:0] data ;
233 | logic nack ;
234 | logic replay ;
235 | logic has_data ;
236 | logic [63:0] data_subw ;
237 | logic [63:0] store_data ;
238 | logic rnvalid ;
239 | logic [7:0] rnext ;
240 | logic xcpt_ma_ld ;
241 | logic xcpt_ma_st ;
242 | logic xcpt_pf_ld ;
243 | logic xcpt_pf_st ;
244 | logic ordered ;
245 | } dmem_ptw_resp_t;
246 |
247 | typedef struct packed {
248 | logic dmem_ready;
249 | dmem_ptw_resp_t resp;
250 | } dmem_ptw_comm_t;
251 |
252 | // CSR interface
253 |
254 | typedef struct packed {
255 | logic [63:0] satp;
256 | logic flush;
257 | csr_mstatus_t mstatus;
258 | } csr_ptw_comm_t;
259 |
260 | endpackage
261 |
--------------------------------------------------------------------------------
/rtl/mmu_top.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | module mmu_top
22 | import mmu_pkg::*;
23 | import sargantana_icache_pkg::*;
24 | import lagarto_pkg::*;
25 | #(
26 | )(
27 | input logic clk_i,
28 | input logic rstn_i,
29 |
30 | //iCache interface
31 | input treq_o_t icache_treq_i ,
32 | output tresp_i_t icache_tresp_o ,
33 |
34 | //dTLB interface
35 | // dTLB requests
36 | input logic [26:0] io_dtlb_ptw_req_bits_addr ,
37 | input logic [1:0] io_dtlb_ptw_req_bits_prv ,
38 | input logic io_dtlb_ptw_req_bits_store ,
39 | input logic io_dtlb_ptw_req_valid ,
40 | output logic io_dtlb_ptw_req_ready ,
41 | // dTLB responses
42 | output logic io_dtlb_ptw_resp_bits_error ,
43 | output logic [26:0] io_dtlb_ptw_resp_bits_pte_ppn ,
44 | output logic io_dtlb_ptw_resp_bits_pte_d ,
45 | output logic io_dtlb_ptw_resp_bits_pte_u ,
46 | output logic io_dtlb_ptw_resp_bits_pte_x ,
47 | output logic io_dtlb_ptw_resp_bits_pte_w ,
48 | output logic io_dtlb_ptw_resp_bits_pte_r ,
49 | output logic io_dtlb_ptw_resp_bits_pte_v ,
50 | output logic [1:0] io_dtlb_ptw_resp_bits_level ,
51 | output logic io_dtlb_ptw_resp_valid ,
52 |
53 | output logic io_dtlb_ptw_status_mxr ,
54 | output logic io_dtlb_ptw_status_sum ,
55 | output logic io_dtlb_ptw_invalidate ,
56 |
57 | //dCache interface
58 | // dCache requests
59 | output logic io_ptw_mem_req_valid ,
60 | output logic [39:0] io_ptw_mem_req_bits_addr ,
61 | output logic [4:0] io_ptw_mem_req_bits_cmd ,
62 | output logic [63:0] io_ptw_mem_req_bits_data ,
63 | output logic io_ptw_mem_req_bits_phys ,
64 | output logic [3:0] io_ptw_mem_req_bits_typ ,
65 | output logic io_ptw_mem_req_bits_kill ,
66 | input logic io_ptw_mem_req_ready ,
67 | // dCache response
68 | input logic io_ptw_mem_resp_valid ,
69 | input logic [63:0] io_ptw_mem_resp_bits_data ,
70 | input logic io_ptw_mem_resp_bits_nack ,
71 |
72 | //PTW interface
73 | output logic io_ptw_invalidate ,
74 |
75 | // CSRs TLB
76 | input logic [1:0] csr_priv_lvl_i ,
77 | input logic csr_en_translation_i ,
78 |
79 | // CSRs PTW
80 | input logic [31:0] csr_satp_i ,
81 | input logic csr_flush_i ,
82 | input logic [63:0] csr_status_i ,
83 |
84 | //PMU
85 | output logic io_core_pmu_itlb_access ,
86 | output logic io_core_pmu_itlb_miss ,
87 | output logic io_core_pmu_itlb_miss_cycle ,
88 | output logic io_core_pmu_ptw_hit ,
89 | output logic io_core_pmu_ptw_miss ,
90 | output logic io_core_pmu_ptw_mem_req
91 | );
92 |
93 | /**************************************************/
94 | /* -- iTLB -- */
95 | /**************************************************/
96 |
97 | //iCache
98 | cache_tlb_comm_t icache_itlb_comm;
99 | tlb_cache_comm_t itlb_icache_comm;
100 | logic [26:0] itlb_icache_resp_ppn_27bits; // 27 bit ppn truncation
101 |
102 | assign icache_itlb_comm.req.valid = icache_treq_i.valid;
103 | assign icache_itlb_comm.req.asid = '0;
104 | assign icache_itlb_comm.req.vpn = icache_treq_i.vpn;
105 | assign icache_itlb_comm.req.passthrough = 1'b0;
106 | assign icache_itlb_comm.req.instruction = 1'b1;
107 | assign icache_itlb_comm.req.store = '0;
108 |
109 | assign icache_tresp_o.miss = itlb_icache_comm.resp.miss;
110 | assign icache_tresp_o.ppn = itlb_icache_resp_ppn_27bits;
111 | assign icache_tresp_o.xcpt = itlb_icache_comm.resp.xcpt;
112 |
113 | //PTW
114 | tlb_ptw_comm_t itlb_ptw_comm;
115 | ptw_tlb_comm_t ptw_itlb_comm;
116 |
117 | //PMU
118 | assign io_core_pmu_itlb_miss_cycle = itlb_icache_comm.resp.miss && !itlb_icache_comm.tlb_ready;
119 |
120 | tlb_wrapper __itlb__(
121 | .clk_i (clk_i),
122 | .rstn_i (rstn_i),
123 | // iCache
124 | // Input
125 | .mem_treq_i_valid (icache_itlb_comm.req.valid),
126 | .mem_treq_i_asid (icache_itlb_comm.req.asid),
127 | .mem_treq_i_vpn (icache_itlb_comm.req.vpn),
128 | .mem_treq_i_passthrough (icache_itlb_comm.req.passthrough),
129 | .mem_treq_i_instruction (icache_itlb_comm.req.instruction),
130 | .mem_treq_i_store (icache_itlb_comm.req.store),
131 | // Output
132 | .mem_req_ready_o (itlb_icache_comm.tlb_ready),
133 | .tlb_tresp_o_miss (itlb_icache_comm.resp.miss),
134 | .tlb_tresp_o_ppn (itlb_icache_resp_ppn_27bits),
135 | .tlb_tresp_o_xcpt__if (itlb_icache_comm.resp.xcpt.fetch),
136 | .tlb_tresp_o_xcpt_ld (itlb_icache_comm.resp.xcpt.load),
137 | .tlb_tresp_o_xcpt_st (itlb_icache_comm.resp.xcpt.store),
138 | .tlb_tresp_o_hit_idx (itlb_icache_comm.resp.hit_idx),
139 | // PTW connection
140 | // Input
141 | .ptw_req_ready_i (ptw_itlb_comm.ptw_ready),
142 | .ptw_invalidate_i (ptw_itlb_comm.invalidate_tlb),
143 | .ptw_resp_i_valid (ptw_itlb_comm.resp.valid),
144 | .ptw_resp_i_error (ptw_itlb_comm.resp.error),
145 | .ptw_resp_i_pte_ppn (ptw_itlb_comm.resp.pte.ppn),
146 | .ptw_resp_i_pte_rfs (ptw_itlb_comm.resp.pte.rfs),
147 | .ptw_resp_i_pte_d (ptw_itlb_comm.resp.pte.d),
148 | .ptw_resp_i_pte_a (ptw_itlb_comm.resp.pte.a),
149 | .ptw_resp_i_pte_g (ptw_itlb_comm.resp.pte.g),
150 | .ptw_resp_i_pte_u (ptw_itlb_comm.resp.pte.u),
151 | .ptw_resp_i_pte_x (ptw_itlb_comm.resp.pte.x),
152 | .ptw_resp_i_pte_w (ptw_itlb_comm.resp.pte.w),
153 | .ptw_resp_i_pte_r (ptw_itlb_comm.resp.pte.r),
154 | .ptw_resp_i_pte_v (ptw_itlb_comm.resp.pte.v),
155 | .ptw_resp_i_level (ptw_itlb_comm.resp.level),
156 | .ptw_status_i_sd (ptw_itlb_comm.ptw_status.sd),
157 | .ptw_status_i_zero5 (ptw_itlb_comm.ptw_status.zero5),
158 | .ptw_status_i_sxl (ptw_itlb_comm.ptw_status.sxl),
159 | .ptw_status_i_uxl (ptw_itlb_comm.ptw_status.uxl),
160 | .ptw_status_i_zero4 (ptw_itlb_comm.ptw_status.zero4),
161 | .ptw_status_i_tsr (ptw_itlb_comm.ptw_status.tsr),
162 | .ptw_status_i_tw (ptw_itlb_comm.ptw_status.tw),
163 | .ptw_status_i_tvm (ptw_itlb_comm.ptw_status.tvm),
164 | .ptw_status_i_mxr (ptw_itlb_comm.ptw_status.mxr),
165 | .ptw_status_i_sum (ptw_itlb_comm.ptw_status.sum),
166 | .ptw_status_i_mprv (ptw_itlb_comm.ptw_status.mprv),
167 | .ptw_status_i_xs (ptw_itlb_comm.ptw_status.xs),
168 | .ptw_status_i_fs (ptw_itlb_comm.ptw_status.fs),
169 | .ptw_status_i_mpp (ptw_itlb_comm.ptw_status.mpp),
170 | .ptw_status_i_zero3 (ptw_itlb_comm.ptw_status.zero3),
171 | .ptw_status_i_spp (ptw_itlb_comm.ptw_status.spp),
172 | .ptw_status_i_mpie (ptw_itlb_comm.ptw_status.mpie),
173 | .ptw_status_i_zero2 (ptw_itlb_comm.ptw_status.zero2),
174 | .ptw_status_i_spie (ptw_itlb_comm.ptw_status.spie),
175 | .ptw_status_i_upie (ptw_itlb_comm.ptw_status.upie),
176 | .ptw_status_i_mie (ptw_itlb_comm.ptw_status.mie),
177 | .ptw_status_i_zero1 (ptw_itlb_comm.ptw_status.zero1),
178 | .ptw_status_i_sie (ptw_itlb_comm.ptw_status.sie),
179 | .ptw_status_i_uie (ptw_itlb_comm.ptw_status.uie),
180 | // Output
181 | .ptw_req_o_valid (itlb_ptw_comm.req.valid),
182 | .ptw_req_o_addr (itlb_ptw_comm.req.vpn),
183 | .ptw_req_o_prv (itlb_ptw_comm.req.prv),
184 | .ptw_req_o_store (itlb_ptw_comm.req.store),
185 | .ptw_req_o_fetch (itlb_ptw_comm.req.fetch),
186 | // CSRs
187 | // Input
188 | .csr_priv_lvl_i (csr_priv_lvl_i),
189 | .csr_en_translation_i (csr_en_translation_i),
190 | // PMU
191 | // Output
192 | .pmu_tlb_access_o (io_core_pmu_itlb_access),
193 | .pmu_tlb_miss_o (io_core_pmu_itlb_miss)
194 | );
195 |
196 | /**************************************************/
197 | /* -- PTW -- */
198 | /**************************************************/
199 |
200 | // dTLB-PTW signals
201 | tlb_ptw_comm_t dtlb_ptw_comm;
202 | assign dtlb_ptw_comm.req.valid = io_dtlb_ptw_req_valid;
203 | assign dtlb_ptw_comm.req.vpn = io_dtlb_ptw_req_bits_addr;
204 | assign dtlb_ptw_comm.req.prv = io_dtlb_ptw_req_bits_prv;
205 | assign dtlb_ptw_comm.req.store = io_dtlb_ptw_req_bits_store;
206 | assign dtlb_ptw_comm.req.fetch = '0;
207 |
208 | ptw_tlb_comm_t ptw_dtlb_comm;
209 | assign io_dtlb_ptw_req_ready = ptw_dtlb_comm.ptw_ready;
210 | assign io_dtlb_ptw_resp_valid = ptw_dtlb_comm.resp.valid;
211 | assign io_dtlb_ptw_resp_bits_error = ptw_dtlb_comm.resp.error;
212 | assign io_dtlb_ptw_resp_bits_pte_ppn = ptw_dtlb_comm.resp.pte.ppn;
213 | assign io_dtlb_ptw_resp_bits_pte_u = ptw_dtlb_comm.resp.pte.u;
214 | assign io_dtlb_ptw_resp_bits_pte_x = ptw_dtlb_comm.resp.pte.x;
215 | assign io_dtlb_ptw_resp_bits_pte_w = ptw_dtlb_comm.resp.pte.w;
216 | assign io_dtlb_ptw_resp_bits_pte_r = ptw_dtlb_comm.resp.pte.r;
217 | assign io_dtlb_ptw_resp_bits_pte_v = ptw_dtlb_comm.resp.pte.v;
218 | assign io_dtlb_ptw_resp_bits_pte_d = ptw_dtlb_comm.resp.pte.d;
219 | assign io_dtlb_ptw_resp_bits_level = ptw_dtlb_comm.resp.level;
220 | assign io_dtlb_ptw_status_mxr = ptw_dtlb_comm.ptw_status.mxr;
221 | assign io_dtlb_ptw_status_sum = ptw_dtlb_comm.ptw_status.sum;
222 | assign io_dtlb_ptw_invalidate = ptw_dtlb_comm.invalidate_tlb;
223 |
224 | // PTW-dMEM signals
225 | ptw_dmem_comm_t ptw_dmem_comm;
226 | assign io_ptw_mem_req_valid = ptw_dmem_comm.req.valid;
227 | assign io_ptw_mem_req_bits_addr = ptw_dmem_comm.req.addr;
228 | assign io_ptw_mem_req_bits_cmd = ptw_dmem_comm.req.cmd;
229 | assign io_ptw_mem_req_bits_data = ptw_dmem_comm.req.data;
230 | assign io_ptw_mem_req_bits_kill = ptw_dmem_comm.req.kill;
231 | assign io_ptw_mem_req_bits_phys = 1'b1;
232 | assign io_ptw_mem_req_bits_typ = 4'b0011;
233 |
234 | assign io_core_pmu_ptw_mem_req = ptw_dmem_comm.req.valid && io_ptw_mem_req_ready;
235 |
236 | dmem_ptw_comm_t dmem_ptw_comm;
237 | assign dmem_ptw_comm.dmem_ready = io_ptw_mem_req_ready;
238 | assign dmem_ptw_comm.resp.valid = io_ptw_mem_resp_valid;
239 | assign dmem_ptw_comm.resp.addr = '0;
240 | assign dmem_ptw_comm.resp.tag_addr = '0;
241 | assign dmem_ptw_comm.resp.cmd = '0;
242 | assign dmem_ptw_comm.resp.typ = '0;
243 | assign dmem_ptw_comm.resp.data = io_ptw_mem_resp_bits_data;
244 | assign dmem_ptw_comm.resp.nack = io_ptw_mem_resp_bits_nack;
245 | assign dmem_ptw_comm.resp.replay = '0;
246 | assign dmem_ptw_comm.resp.has_data = '0;
247 | assign dmem_ptw_comm.resp.data_subw = '0;
248 | assign dmem_ptw_comm.resp.store_data = '0;
249 | assign dmem_ptw_comm.resp.rnvalid = '0;
250 | assign dmem_ptw_comm.resp.rnext = '0;
251 | assign dmem_ptw_comm.resp.xcpt_ma_ld = '0;
252 | assign dmem_ptw_comm.resp.xcpt_ma_st = '0;
253 | assign dmem_ptw_comm.resp.xcpt_pf_ld = '0;
254 | assign dmem_ptw_comm.resp.xcpt_pf_st = '0;
255 | assign dmem_ptw_comm.resp.ordered = '0;
256 |
257 | //CSR
258 | csr_ptw_comm_t csr_ptw_comm;
259 | assign csr_ptw_comm.satp = {32'b0, csr_satp_i}; //The PTW obtains ALWAYS a satp of 64 bits
260 | assign csr_ptw_comm.flush = csr_flush_i;
261 | assign csr_ptw_comm.mstatus = csr_status_i;
262 |
263 | assign io_ptw_invalidate = ptw_itlb_comm.invalidate_tlb;
264 |
265 | //iCache
266 | assign icache_tresp_o.ptw_v = ptw_itlb_comm.resp.valid;
267 |
268 | ptw __ptw__ (
269 | .clk_i(clk_i),
270 | .rstn_i(rstn_i),
271 |
272 | // iTLB request-response
273 | .itlb_ptw_comm_i(itlb_ptw_comm),
274 | .ptw_itlb_comm_o(ptw_itlb_comm),
275 |
276 | // dTLB request-response
277 | .dtlb_ptw_comm_i(dtlb_ptw_comm),
278 | .ptw_dtlb_comm_o(ptw_dtlb_comm),
279 |
280 | // dmem request-response
281 | .dmem_ptw_comm_i(dmem_ptw_comm),
282 | .ptw_dmem_comm_o(ptw_dmem_comm),
283 |
284 | // csr interface
285 | .csr_ptw_comm_i(csr_ptw_comm),
286 |
287 | // pmu interface
288 | .pmu_ptw_hit_o(io_core_pmu_ptw_hit),
289 | .pmu_ptw_miss_o(io_core_pmu_ptw_miss)
290 | );
291 |
292 | endmodule
293 |
--------------------------------------------------------------------------------
/rtl/ptw/ptw.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | module ptw
22 | import mmu_pkg::*;
23 | #(
24 | )(
25 | input logic clk_i,
26 | input logic rstn_i,
27 |
28 | // iTLB request-response
29 | input tlb_ptw_comm_t itlb_ptw_comm_i,
30 | output ptw_tlb_comm_t ptw_itlb_comm_o,
31 |
32 | // dTLB request-response
33 | input tlb_ptw_comm_t dtlb_ptw_comm_i,
34 | output ptw_tlb_comm_t ptw_dtlb_comm_o,
35 |
36 | // dmem request-response
37 | input dmem_ptw_comm_t dmem_ptw_comm_i,
38 | output ptw_dmem_comm_t ptw_dmem_comm_o,
39 |
40 | // csr interface
41 | input csr_ptw_comm_t csr_ptw_comm_i,
42 |
43 | // pmu interface
44 | output logic pmu_ptw_hit_o,
45 | output logic pmu_ptw_miss_o
46 | );
47 | //Mem commands
48 | //localparam [4:0] M_XA_OR = 5'b01010;
49 | localparam [4:0] M_XRD = 5'b00000;
50 | localparam [3:0] MT_D = 4'b0011;
51 |
52 | // Page-Table Walker FSM States
53 | typedef enum logic [2:0] {
54 | S_READY,
55 | S_REQ,
56 | S_WAIT,
57 | S_DONE,
58 | S_ERROR
59 | } ptw_state;
60 |
61 | // Signal declaration
62 | logic unsigned [$clog2(LEVELS)-1:0] count_d, count_q;
63 | logic unsigned [$clog2(LEVELS):0] count;
64 |
65 | ptw_tlb_comm_t ptw_tlb_comm;
66 | tlb_ptw_comm_t tlb_ptw_comm;
67 |
68 | ptw_state current_state, next_state;
69 |
70 | logic ptw_ready;
71 | tlb_ptw_req_t r_req;
72 | pte_t r_pte;
73 | pte_t pte;
74 | pte_t pte_wdata;
75 |
76 | logic [PAGE_LVL_BITS-1:0] vpn_req [LEVELS-1:0];
77 | logic [PAGE_LVL_BITS-1:0] vpn_idx;
78 | logic [SIZE_VADDR:0] pte_addr;
79 | logic invalid_pte;
80 | logic valid_pte_lvl [LEVELS-1:0];
81 | logic is_pte_leaf, is_pte_table;
82 | logic is_pte_ur, is_pte_uw, is_pte_ux;
83 | logic is_pte_sr, is_pte_sw, is_pte_sx;
84 |
85 | logic [1:0] prv_req;
86 | logic perm_ok;
87 |
88 | logic resp_err, resp_val;
89 | logic [63:0] r_resp_ppn;
90 | logic [PPN_SIZE-1:0] resp_ppn_lvl [LEVELS-1:0];
91 | logic [PPN_SIZE-1:0] resp_ppn;
92 |
93 | logic pte_cache_hit;
94 | logic [PPN_SIZE-1:0] pte_cache_data;
95 |
96 | // Truncate function
97 | function [$clog2(PTW_CACHE_SIZE)-1:0] trunc_ptw_cache_size(input [31:0] val_in);
98 | trunc_ptw_cache_size = val_in[$clog2(PTW_CACHE_SIZE)-1:0];
99 | endfunction
100 |
101 | // PTW Arbiter
102 | ptw_arb ptw_arb_inst(
103 | .clk_i(clk_i),
104 | .rstn_i(rstn_i),
105 | .itlb_ptw_comm_i(itlb_ptw_comm_i),
106 | .dtlb_ptw_comm_i(dtlb_ptw_comm_i),
107 | .ptw_itlb_comm_o(ptw_itlb_comm_o),
108 | .ptw_dtlb_comm_o(ptw_dtlb_comm_o),
109 | .ptw_tlb_comm_i(ptw_tlb_comm),
110 | .tlb_ptw_comm_o(tlb_ptw_comm)
111 | );
112 |
113 | // VPN indexation depending on the page level
114 | genvar lvl;
115 | generate
116 | for (lvl = 0; lvl < LEVELS; lvl++) begin
117 | logic [VPN_SIZE-1:0] aux_vpn_req;
118 | assign aux_vpn_req = (r_req.vpn >> ((LEVELS-lvl-1)*PAGE_LVL_BITS));
119 | assign vpn_req[lvl] = aux_vpn_req[PAGE_LVL_BITS-1:0];
120 | end
121 | endgenerate
122 | assign vpn_idx = vpn_req[count_q];
123 |
124 | // Formatting pte data from dmem
125 | assign pte.ppn = dmem_ptw_comm_i.resp.data[10+:(PPN_SIZE)];
126 | assign pte.rfs = dmem_ptw_comm_i.resp.data[9:8];
127 | assign pte.d = dmem_ptw_comm_i.resp.data[7];
128 | assign pte.a = dmem_ptw_comm_i.resp.data[6];
129 | assign pte.g = dmem_ptw_comm_i.resp.data[5];
130 | assign pte.u = dmem_ptw_comm_i.resp.data[4];
131 | assign pte.x = dmem_ptw_comm_i.resp.data[3];
132 | assign pte.w = dmem_ptw_comm_i.resp.data[2];
133 | assign pte.r = dmem_ptw_comm_i.resp.data[1];
134 |
135 | genvar c;
136 | generate
137 | for (c = 0; c < (LEVELS-1); c++) begin
138 | always_comb begin
139 | if (pte.r || pte.w || pte.x) begin
140 | valid_pte_lvl[c] = (pte.ppn[((LEVELS-c-1)*PAGE_LVL_BITS)-1:0] == '0) ? dmem_ptw_comm_i.resp.data[0] : 1'b0; //Make sure PPN LSB are 0
141 | end else begin
142 | valid_pte_lvl[c] = dmem_ptw_comm_i.resp.data[0];
143 | end
144 | end
145 | end
146 | endgenerate
147 | assign valid_pte_lvl[LEVELS-1] = dmem_ptw_comm_i.resp.data[0];
148 | assign pte.v = valid_pte_lvl[count_q];
149 |
150 | assign invalid_pte = (((dmem_ptw_comm_i.resp.data >> (PPN_SIZE+10)) != '0) ||
151 | ((is_pte_table & pte.v & (pte.d || pte.a || pte.u)))) ? 1'b1 : 1'b0; //Make sure that N, PBMT and Reserved are 0
152 |
153 | assign is_pte_table = pte.v && !pte.x && !pte.w && !pte.r;
154 | assign is_pte_leaf = pte.v && (pte.x || pte.w || pte.r);
155 |
156 | assign is_pte_ur = is_pte_leaf && pte.u && pte.r;
157 | assign is_pte_uw = is_pte_ur && pte.w;
158 | assign is_pte_ux = pte.v && pte.x && pte.u;
159 | assign is_pte_sr = is_pte_leaf && pte.r && !pte.u;
160 | assign is_pte_sw = is_pte_sr && pte.w;
161 | assign is_pte_sx = pte.v && pte.x && !pte.u;
162 |
163 | // Page Table Entry pointer
164 | logic [63:0] aux_pte_addr;
165 | assign aux_pte_addr = {{(64-(PPN_SIZE+PAGE_LVL_BITS+$clog2(riscv_pkg::XLEN/8))){1'b0}}, {r_pte.ppn, vpn_idx, {{($clog2(riscv_pkg::XLEN/8))}{1'b0}}}};
166 | assign pte_addr = aux_pte_addr[SIZE_VADDR:0] ; // For Sv39: (r_pte.ppn << 12) + (vpn_idx << 3)
167 |
168 | // PTW Ready
169 | assign ptw_ready = (current_state == S_READY);
170 |
171 | // Catch Request from TLB(Arb) & PTE response from dmem
172 | always_ff @(posedge clk_i, negedge rstn_i) begin
173 | if (!rstn_i) begin
174 | r_req <= '0;
175 | r_pte <= '0;
176 | end else begin
177 | if ((current_state == S_WAIT) && dmem_ptw_comm_i.resp.valid) begin
178 | r_pte <= pte;
179 | end else if ((current_state == S_REQ) && pte_cache_hit && (count_q < $unsigned(LEVELS-1))) begin
180 | r_pte.ppn <= pte_cache_data;
181 | end else if (ptw_ready & tlb_ptw_comm.req.valid) begin
182 | r_req <= tlb_ptw_comm.req;
183 | r_pte.ppn <= csr_ptw_comm_i.satp[PPN_SIZE-1:0];
184 | end
185 | end
186 | end
187 | `ifndef NO_PTW_CACHE
188 | ///////////////////////
189 | // PTE cache
190 | ///////////////////////
191 | ptw_ptecache_entry_t [PTW_CACHE_SIZE-1:0] ptecache_entry;
192 | logic access_hit;
193 | logic full_cache;
194 | logic unsigned [$clog2(PTW_CACHE_SIZE)-1:0] hit_idx;
195 | logic unsigned [$clog2(PTW_CACHE_SIZE)-1:0] plru_eviction_idx;
196 | logic unsigned [$clog2(PTW_CACHE_SIZE)-1:0] priorityEncoder_idx;
197 | logic [PTW_CACHE_SIZE-1:0] valid_vector;
198 | logic [PTW_CACHE_SIZE-1:0] hit_vector;
199 |
200 | pseudoLRU #(
201 | .ENTRIES(PTW_CACHE_SIZE)
202 | ) ptw_PLRU (
203 | .clk_i(clk_i),
204 | .rstn_i(rstn_i),
205 | .access_hit_i(access_hit),
206 | .access_idx_i(hit_idx),
207 | .replacement_idx_o(plru_eviction_idx)
208 | );
209 |
210 | always_ff @(posedge clk_i, negedge rstn_i) begin
211 | if (!rstn_i) begin
212 | for (int i = 0; i < PTW_CACHE_SIZE; i++) begin
213 | ptecache_entry[i] <= '0;
214 | access_hit <= 1'b0;
215 | end
216 | end else begin
217 | access_hit <= 1'b0;
218 | if (dmem_ptw_comm_i.resp.valid && is_pte_table && !pte_cache_hit) begin
219 | if (full_cache) begin
220 | ptecache_entry[plru_eviction_idx].valid <= 1'b1;
221 | ptecache_entry[plru_eviction_idx].tags <= pte_addr;
222 | ptecache_entry[plru_eviction_idx].data <= pte.ppn;
223 | end else begin
224 | ptecache_entry[priorityEncoder_idx].valid <= 1'b1;
225 | ptecache_entry[priorityEncoder_idx].tags <= pte_addr;
226 | ptecache_entry[priorityEncoder_idx].data <= pte.ppn;
227 | end
228 | end
229 | else if (pte_cache_hit && (current_state == S_REQ)) begin
230 | access_hit <= 1'b1;
231 | end
232 | else if (csr_ptw_comm_i.flush) begin
233 | for (int i = 0; i < PTW_CACHE_SIZE; i++) begin
234 | ptecache_entry[i].valid <= 1'b0;
235 | end
236 | end
237 | end
238 | end
239 |
240 | always_comb begin
241 | for (int i = 0; i < PTW_CACHE_SIZE; i++) begin
242 | hit_vector[i] = ((ptecache_entry[i].tags == pte_addr) && ptecache_entry[i].valid) ? 1'b1 : 1'b0;
243 | valid_vector[i] = ptecache_entry[i].valid;
244 | end
245 | end
246 | assign full_cache =& valid_vector; //And Reduction
247 | assign pte_cache_hit =| hit_vector; //Or Reduction
248 |
249 | //Find hit index and data of the data[hit_idx]
250 | logic found;
251 | always_comb begin
252 | hit_idx = '0;
253 | pte_cache_data = '0;
254 | found = 1'b0; // Control variable
255 | for (int i = 0; (i < PTW_CACHE_SIZE) && (!found); i++) begin
256 | if (hit_vector[i]) begin
257 | hit_idx = $unsigned(trunc_ptw_cache_size($unsigned(i)));
258 | pte_cache_data = ptecache_entry[i].data;
259 | found = 1'b1;
260 | end
261 | end
262 | end
263 |
264 | //Priority Encoder
265 | logic found2;
266 | always_comb begin
267 | priorityEncoder_idx = '0;
268 | found2 = 1'b0;
269 | for (int i = 0; (i < PTW_CACHE_SIZE) && (!found2); i++) begin
270 | if (!valid_vector[i]) begin
271 | priorityEncoder_idx = trunc_ptw_cache_size($unsigned(i));
272 | found2 = 1'b1;
273 | end
274 | end
275 | end
276 | /////////////////////////////////
277 | `else
278 | assign pte_cache_hit = 1'b0; //Temporal
279 | assign pte_cache_data = '0; //Temporal
280 | `endif
281 |
282 | // Check permissons for set_dirty
283 | assign prv_req = r_req.prv;
284 |
285 | always_comb begin
286 | if (prv_req[0]) begin //Supervisor
287 | if (csr_ptw_comm_i.mstatus.sum) begin
288 | if (r_req.fetch) perm_ok = is_pte_sx || is_pte_ux;
289 | else begin
290 | if (r_req.store) perm_ok = is_pte_sw || is_pte_uw;
291 | else if (csr_ptw_comm_i.mstatus.mxr) perm_ok = is_pte_sr || is_pte_ur || is_pte_ux || is_pte_sx;
292 | else perm_ok = is_pte_sr || is_pte_ur;
293 | end
294 | end else begin
295 | if (r_req.fetch) perm_ok = is_pte_sx;
296 | else begin
297 | if (r_req.store) perm_ok = is_pte_sw;
298 | else if (csr_ptw_comm_i.mstatus.mxr) perm_ok = is_pte_sr || is_pte_sx;
299 | else perm_ok = is_pte_sr;
300 | end
301 | end
302 | end else begin //User
303 | if (r_req.fetch) perm_ok = is_pte_ux;
304 | else begin
305 | if (r_req.store) perm_ok = is_pte_uw;
306 | else if (csr_ptw_comm_i.mstatus.mxr) perm_ok = is_pte_ur || is_pte_ux;
307 | else perm_ok = is_pte_ur;
308 | end
309 | end
310 | end
311 |
312 | // dmem Request
313 | always_comb begin
314 | pte_wdata = '0;
315 | pte_wdata.a = 1'b1;
316 | end
317 |
318 | assign ptw_dmem_comm_o.req.phys = 1'b1;
319 | assign ptw_dmem_comm_o.req.cmd = M_XRD;
320 | assign ptw_dmem_comm_o.req.typ = MT_D;
321 | assign ptw_dmem_comm_o.req.addr = pte_addr;
322 | assign ptw_dmem_comm_o.req.kill = 1'b0;
323 | assign ptw_dmem_comm_o.req.data = { {(64-$bits(pte_wdata)){1'b0}},
324 | pte_wdata.ppn,
325 | pte_wdata.rfs,
326 | pte_wdata.d,
327 | pte_wdata.a,
328 | pte_wdata.g,
329 | pte_wdata.u,
330 | pte_wdata.x,
331 | pte_wdata.w,
332 | pte_wdata.r,
333 | pte_wdata.v
334 | };
335 |
336 | // TLB Response
337 | assign resp_err = (current_state == S_ERROR);
338 | assign resp_val = (current_state == S_DONE) || resp_err;
339 |
340 | assign r_resp_ppn = {{(64-(SIZE_VADDR-11)){1'b0}}, pte_addr[SIZE_VADDR:12]}; // pte_addr >> 12
341 | genvar j;
342 | generate
343 | for (j = 0; j < (LEVELS-1); j++) begin
344 | logic [63:0] aux_resp_ppn_lvl;
345 | assign aux_resp_ppn_lvl = {{(64-$bits(r_resp_ppn[63:(LEVELS-j-1)*PAGE_LVL_BITS])-$bits(r_req.vpn[PAGE_LVL_BITS*(LEVELS-j-1)-1:0])){1'b0}},
346 | r_resp_ppn[63:(LEVELS-j-1)*PAGE_LVL_BITS],
347 | r_req.vpn[PAGE_LVL_BITS*(LEVELS-j-1)-1:0]};
348 | assign resp_ppn_lvl[j] = aux_resp_ppn_lvl[PPN_SIZE-1:0];
349 | end
350 | endgenerate
351 | assign resp_ppn_lvl[LEVELS-1] = r_resp_ppn[PPN_SIZE-1:0];
352 | assign resp_ppn = resp_ppn_lvl[count_q];
353 |
354 | // Send TLB Response to Arb
355 | assign ptw_tlb_comm.resp.valid = resp_val;
356 | assign ptw_tlb_comm.resp.error = resp_err;
357 | assign ptw_tlb_comm.resp.level = count_q;
358 | assign ptw_tlb_comm.resp.pte.ppn = resp_ppn;
359 | assign ptw_tlb_comm.resp.pte.rfs = r_pte.rfs;
360 | assign ptw_tlb_comm.resp.pte.d = r_pte.d;
361 | assign ptw_tlb_comm.resp.pte.a = r_pte.a;
362 | assign ptw_tlb_comm.resp.pte.g = r_pte.g;
363 | assign ptw_tlb_comm.resp.pte.u = r_pte.u;
364 | assign ptw_tlb_comm.resp.pte.x = r_pte.x;
365 | assign ptw_tlb_comm.resp.pte.w = r_pte.w;
366 | assign ptw_tlb_comm.resp.pte.r = r_pte.r;
367 | assign ptw_tlb_comm.resp.pte.v = r_pte.v;
368 | assign ptw_tlb_comm.ptw_ready = ptw_ready;
369 | assign ptw_tlb_comm.ptw_status = csr_ptw_comm_i.mstatus;
370 | assign ptw_tlb_comm.invalidate_tlb = csr_ptw_comm_i.flush;
371 |
372 | // Page-Table Walker FSM
373 | always_ff @(posedge clk_i, negedge rstn_i) begin
374 | if (!rstn_i) begin
375 | current_state <= S_READY;
376 | count_q <= '0;
377 | end
378 | else begin
379 | current_state <= next_state;
380 | count_q <= count_d;
381 | end
382 | end
383 |
384 | always_comb begin
385 | count_d = count_q;
386 | count = count_q + 1'b1;
387 | pmu_ptw_hit_o = 1'b0;
388 | pmu_ptw_miss_o = 1'b0;
389 | ptw_dmem_comm_o.req.valid = 1'b0;
390 | next_state = current_state;
391 | case (current_state)
392 | S_READY : begin
393 | count_d = '0;
394 | if (tlb_ptw_comm.req.valid) next_state = S_REQ;
395 | else next_state = S_READY;
396 | end
397 | S_REQ : begin
398 | ptw_dmem_comm_o.req.valid = 1'b1;
399 | if (pte_cache_hit && (count_q < $unsigned(LEVELS-1))) begin
400 | ptw_dmem_comm_o.req.valid = 1'b0;
401 | pmu_ptw_hit_o = 1'b1;
402 | count_d = count[1:0];
403 | next_state = S_REQ;
404 | end else if (dmem_ptw_comm_i.dmem_ready) begin
405 | next_state = S_WAIT;
406 | end else begin
407 | next_state = S_REQ;
408 | end
409 | end
410 | S_WAIT : begin
411 | if (dmem_ptw_comm_i.resp.nack) begin
412 | next_state = S_REQ;
413 | end else if (dmem_ptw_comm_i.resp.valid) begin
414 | if (invalid_pte) begin
415 | next_state = S_ERROR;
416 | end else if (is_pte_table && (count_q < $unsigned(LEVELS-1))) begin
417 | count_d = count[1:0];
418 | pmu_ptw_miss_o = 1'b1;
419 | next_state = S_REQ;
420 | end else if (is_pte_leaf) begin
421 | next_state = S_DONE;
422 | end
423 | else begin
424 | next_state = S_ERROR;
425 | end
426 | end
427 | else begin
428 | next_state = S_WAIT;
429 | end
430 | end
431 | S_DONE : begin
432 | next_state = S_READY;
433 | end
434 | S_ERROR : begin
435 | next_state = S_READY;
436 | end
437 | endcase
438 | end
439 |
440 | endmodule
441 |
--------------------------------------------------------------------------------
/rtl/tlb/tlb.sv:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 BSC*
3 | * *Barcelona Supercomputing Center (BSC)
4 | *
5 | * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
6 | *
7 | * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
8 | * may not use this file except in compliance with the License, or, at your
9 | * option, the Apache License version 2.0. You may obtain a copy of the
10 | * License at
11 | *
12 | * https://solderpad.org/licenses/SHL-2.1/
13 | *
14 | * Unless required by applicable law or agreed to in writing, any work
15 | * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
16 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 | * License for the specific language governing permissions and limitations
18 | * under the License.
19 | */
20 |
21 | /* TLB follows SV39 specification*/
22 |
23 | module tlb
24 | import mmu_pkg::*;
25 | #(
26 | )(
27 | input logic clk_i, // System clock signal.
28 | input logic rstn_i, // System reset signal (active low).
29 |
30 | // TLB request-response
31 | input cache_tlb_comm_t cache_tlb_comm_i, // Communication from translation requester to TLB.
32 | output tlb_cache_comm_t tlb_cache_comm_o, // Communication from TLB to translation requester.
33 |
34 | // PTW request-response
35 | input ptw_tlb_comm_t ptw_tlb_comm_i, // Communication from TLB to PTW.
36 | output tlb_ptw_comm_t tlb_ptw_comm_o, // Communication from to PTW to TLB.
37 |
38 | // PMU counter events
39 | output logic pmu_tlb_access_o, // Accepted Translation request to TLB.
40 | output logic pmu_tlb_miss_o // Accepted Translation request to TLB misses.
41 | );
42 |
43 | tlb_entry_t [TLB_ENTRIES-1:0] tlb_entries;
44 |
45 | // Truncate function
46 | function [TLB_IDX_SIZE-1:0] trunc_tlb_idx_size(input [31:0] val_in);
47 | trunc_tlb_idx_size = val_in[TLB_IDX_SIZE-1:0];
48 | endfunction
49 |
50 | function [TLB_IDX_SIZE-1:0] trunc_tlb_idx_size_4in(input [3:0] val_in);
51 | trunc_tlb_idx_size_4in = val_in[TLB_IDX_SIZE-1:0];
52 | endfunction
53 |
54 | // TLB WRITE LOGIC
55 | ///////////////////////////////
56 |
57 | logic clear_tlb, write_tlb;
58 | logic [TLB_ENTRIES-1:0] clear_mask;
59 | logic [TLB_IDX_SIZE-1:0] write_idx;
60 |
61 | assign write_idx = tlb_req_tmp.write_idx; // stored write_idx with the eviction idx (calculated in the first cycle of the req)
62 | always_ff @(posedge clk_i, negedge rstn_i) begin
63 | if(~rstn_i) begin
64 | for (int i=0; i supervisor
315 |
316 | // Read permission
317 | always_comb begin
318 | if (sv_priv_lvl) begin
319 | if (ptw_tlb_comm_i.ptw_status.sum) begin // if SUM bit is set, in SV we can read in readable user pages
320 | if (ptw_tlb_comm_i.ptw_status.mxr) begin // if MXR bit is set, executable pages can be also readed
321 | read_ok = tlb_entries[hit_idx].perms.sr | tlb_entries[hit_idx].perms.ur | tlb_entries[hit_idx].perms.sx | tlb_entries[hit_idx].perms.ux;
322 | end else begin
323 | read_ok = tlb_entries[hit_idx].perms.sr | tlb_entries[hit_idx].perms.ur;
324 | end
325 | end else begin
326 | if (ptw_tlb_comm_i.ptw_status.mxr) begin // if MXR bit is set, executable pages can be also readed
327 | read_ok = tlb_entries[hit_idx].perms.sr | tlb_entries[hit_idx].perms.sx;
328 | end else begin
329 | read_ok = tlb_entries[hit_idx].perms.sr;
330 | end
331 | end
332 | end else begin // User mode
333 | if (ptw_tlb_comm_i.ptw_status.mxr) begin // if MXR bit is set, executable pages can be also readed
334 | read_ok = tlb_entries[hit_idx].perms.ur | tlb_entries[hit_idx].perms.ux;
335 | end else begin
336 | read_ok = tlb_entries[hit_idx].perms.ur;
337 | end
338 | end
339 | end
340 |
341 | // Write permission
342 | always_comb begin
343 | if(sv_priv_lvl) begin
344 | if (ptw_tlb_comm_i.ptw_status.sum) begin // if SUM bit is set, in SV we can write in writable user pages
345 | write_ok = tlb_entries[hit_idx].perms.sw | tlb_entries[hit_idx].perms.uw;
346 | end else begin
347 | write_ok = tlb_entries[hit_idx].perms.sw;
348 | end
349 | end else begin
350 | write_ok = tlb_entries[hit_idx].perms.uw;
351 | end
352 | end
353 |
354 | // Execution permission
355 | assign exec_ok = (sv_priv_lvl) ? tlb_entries[hit_idx].perms.sx : tlb_entries[hit_idx].perms.ux;
356 |
357 | logic xcpt_if, xcpt_st, xcpt_ld;
358 | assign xcpt_if = (vm_enable && ((tlb_hit && !exec_ok) || tlb_entry_access_is_zero)) ? 1'b1 : 1'b0;
359 | assign xcpt_st = (vm_enable && ((tlb_hit && !write_ok) || tlb_entry_access_is_zero || tlb_entry_dirty_is_zero)) ? 1'b1 : 1'b0;
360 | assign xcpt_ld = (vm_enable && ((tlb_hit && !read_ok) || tlb_entry_access_is_zero)) ? 1'b1 : 1'b0;
361 |
362 | // PPN ASSIGNAMENT
363 | ///////////////////////////////
364 |
365 | logic [PPN_SIZE-1:0] ppn_k, ppn_m, ppn_g;
366 |
367 | // We receive superpages from PTW as if they were 4KB pages (implementation decision in LowRisc)
368 | // For example 2MB ppn 1 is encoded as 100000000b -> 512 in 4KB pages
369 | // therefore, it is required exctracting the page number in its real size and fill the 0s with the
370 | // corresponding bits of the vpn to get the correct translation
371 |
372 | assign ppn_k = tlb_entries[hit_idx].ppn;
373 | assign ppn_m = {tlb_entries[hit_idx].ppn >> PAGE_LVL_BITS, cache_vpn[PAGE_LVL_BITS-1:0]};
374 | assign ppn_g = {tlb_entries[hit_idx].ppn >> (PAGE_LVL_BITS * 2), cache_vpn[PAGE_LVL_BITS*2-1:0]};
375 |
376 | // TLB RESPONSE
377 | ///////////////////////////////
378 |
379 | assign tlb_cache_comm_o.tlb_ready = tlb_ready;
380 | assign tlb_cache_comm_o.resp.miss = tlb_miss;
381 | assign tlb_cache_comm_o.resp.ppn =
382 | ((ppn_k & {PPN_SIZE{hit_k & vm_enable & ~passthrough}}) | (ppn_m & {PPN_SIZE{hit_m & vm_enable & ~passthrough}})) |
383 | ((ppn_g & {PPN_SIZE{hit_g & vm_enable & ~passthrough}}) | {{(PPN_SIZE-VPN_SIZE-1){1'b0}}, cache_vpn & {(VPN_SIZE+1){~(vm_enable & ~passthrough)}}});
384 | assign tlb_cache_comm_o.resp.xcpt.load = xcpt_ld;
385 | assign tlb_cache_comm_o.resp.xcpt.store = xcpt_st;
386 | assign tlb_cache_comm_o.resp.xcpt.fetch = xcpt_if;
387 |
388 | assign tlb_cache_comm_o.resp.hit_idx = 'h0;
389 |
390 | // PMU EVENTS
391 | ///////////////////////////////
392 | assign pmu_tlb_access_o = pmu_tlb_access;
393 | assign pmu_tlb_miss_o = pmu_tlb_miss;
394 |
395 | endmodule
396 |
--------------------------------------------------------------------------------