+ assert SET_SIZE_BITS <= TLB_LG_PGSZ report "Set indexed by virtual address" severity FAILURE;
+
+ -- Latch the request in r0.req as long as we're not stalling
+ stage_0 : process(clk)
+ variable r : reg_stage_0_t;
+ begin
+ if rising_edge(clk) then
+ assert (d_in.valid and m_in.valid) = '0' report
+ "request collision loadstore vs MMU";
+ if m_in.valid = '1' then
+ r.req.valid := '1';
+ r.req.load := not (m_in.tlbie or m_in.tlbld);
+ r.req.dcbz := '0';
+ r.req.nc := '0';
+ r.req.reserve := '0';
+ r.req.virt_mode := '0';
+ r.req.priv_mode := '1';
+ r.req.addr := m_in.addr;
+ r.req.data := m_in.pte;
+ r.req.byte_sel := (others => '1');
+ r.tlbie := m_in.tlbie;
+ r.doall := m_in.doall;
+ r.tlbld := m_in.tlbld;
+ r.mmu_req := '1';
+ else
+ r.req := d_in;
+ r.req.data := (others => '0');
+ r.tlbie := '0';
+ r.doall := '0';
+ r.tlbld := '0';
+ r.mmu_req := '0';
+ end if;
+ r.d_valid := '0';
+ if rst = '1' then
+ r0_full <= '0';
+ elsif (r1.full = '0' and d_in.hold = '0') or r0_full = '0' then
+ r0 <= r;
+ r0_full <= r.req.valid;
+ end if;
+ -- Sample data the cycle after a request comes in from loadstore1.
+ -- If another request has come in already then the data will get
+ -- put directly into req.data below.
+ if r0.req.valid = '1' and r.req.valid = '0' and r0.d_valid = '0' and
+ r0.mmu_req = '0' then
+ r0.req.data <= d_in.data;
+ r0.d_valid <= '1';
+ end if;
+ end if;
+ end process;
+
+ -- we don't yet handle collisions between loadstore1 requests and MMU requests
+ m_out.stall <= '0';
+
+ -- Hold off the request in r0 when r1 has an uncompleted request
+ r0_stall <= r0_full and (r1.full or d_in.hold);
+ r0_valid <= r0_full and not r1.full and not d_in.hold;
+ stall_out <= r0_stall;
+
+ -- TLB
+ -- Operates in the second cycle on the request latched in r0.req.
+ -- TLB updates write the entry at the end of the second cycle.
+ tlb_read : process(clk)
+ variable index : tlb_index_t;
+ variable addrbits : std_ulogic_vector(TLB_SET_BITS - 1 downto 0);
+ begin
+ if rising_edge(clk) then
+ if m_in.valid = '1' then
+ addrbits := m_in.addr(TLB_LG_PGSZ + TLB_SET_BITS - 1 downto TLB_LG_PGSZ);
+ else
+ addrbits := d_in.addr(TLB_LG_PGSZ + TLB_SET_BITS - 1 downto TLB_LG_PGSZ);
+ end if;
+ index := to_integer(unsigned(addrbits));
+ -- If we have any op and the previous op isn't finished,
+ -- then keep the same output for next cycle.
+ if r0_stall = '0' then
+ tlb_valid_way <= dtlb_valids(index);
+ tlb_tag_way <= dtlb_tags(index);
+ tlb_pte_way <= dtlb_ptes(index);
+ end if;
+ end if;
+ end process;
+
+ -- Generate TLB PLRUs
+ maybe_tlb_plrus: if TLB_NUM_WAYS > 1 generate
+ begin
+ tlb_plrus: for i in 0 to TLB_SET_SIZE - 1 generate
+ -- TLB PLRU interface
+ signal tlb_plru_acc : std_ulogic_vector(TLB_WAY_BITS-1 downto 0);
+ signal tlb_plru_acc_en : std_ulogic;
+ signal tlb_plru_out : std_ulogic_vector(TLB_WAY_BITS-1 downto 0);
+ begin
+ tlb_plru : entity work.plru
+ generic map (
+ BITS => TLB_WAY_BITS
+ )
+ port map (
+ clk => clk,
+ rst => rst,
+ acc => tlb_plru_acc,
+ acc_en => tlb_plru_acc_en,
+ lru => tlb_plru_out
+ );
+
+ process(all)
+ begin
+ -- PLRU interface
+ if r1.tlb_hit_index = i then
+ tlb_plru_acc_en <= r1.tlb_hit;
+ else
+ tlb_plru_acc_en <= '0';
+ end if;
+ tlb_plru_acc <= std_ulogic_vector(to_unsigned(r1.tlb_hit_way, TLB_WAY_BITS));
+ tlb_plru_victim(i) <= tlb_plru_out;
+ end process;
+ end generate;
+ end generate;
+
+ tlb_search : process(all)
+ variable hitway : tlb_way_t;
+ variable hit : std_ulogic;
+ variable eatag : tlb_tag_t;
+ begin
+ tlb_req_index <= to_integer(unsigned(r0.req.addr(TLB_LG_PGSZ + TLB_SET_BITS - 1
+ downto TLB_LG_PGSZ)));
+ hitway := 0;
+ hit := '0';
+ eatag := r0.req.addr(63 downto TLB_LG_PGSZ + TLB_SET_BITS);
+ for i in tlb_way_t loop
+ if tlb_valid_way(i) = '1' and
+ read_tlb_tag(i, tlb_tag_way) = eatag then
+ hitway := i;
+ hit := '1';
+ end if;
+ end loop;
+ tlb_hit <= hit and r0_valid;
+ tlb_hit_way <= hitway;
+ if tlb_hit = '1' then
+ pte <= read_tlb_pte(hitway, tlb_pte_way);
+ else
+ pte <= (others => '0');
+ end if;
+ valid_ra <= tlb_hit or not r0.req.virt_mode;
+ if r0.req.virt_mode = '1' then
+ ra <= pte(REAL_ADDR_BITS - 1 downto TLB_LG_PGSZ) &
+ r0.req.addr(TLB_LG_PGSZ - 1 downto ROW_OFF_BITS) &
+ (ROW_OFF_BITS-1 downto 0 => '0');
+ perm_attr <= extract_perm_attr(pte);
+ else
+ ra <= r0.req.addr(REAL_ADDR_BITS - 1 downto ROW_OFF_BITS) &
+ (ROW_OFF_BITS-1 downto 0 => '0');
+ perm_attr <= real_mode_perm_attr;
+ end if;
+ end process;
+
+ tlb_update : process(clk)
+ variable tlbie : std_ulogic;
+ variable tlbwe : std_ulogic;
+ variable repl_way : tlb_way_t;
+ variable eatag : tlb_tag_t;
+ variable tagset : tlb_way_tags_t;
+ variable pteset : tlb_way_ptes_t;
+ begin
+ if rising_edge(clk) then
+ tlbie := r0_valid and r0.tlbie;
+ tlbwe := r0_valid and r0.tlbld;
+ if rst = '1' or (tlbie = '1' and r0.doall = '1') then
+ -- clear all valid bits at once
+ for i in tlb_index_t loop
+ dtlb_valids(i) <= (others => '0');
+ end loop;
+ elsif tlbie = '1' then
+ if tlb_hit = '1' then
+ dtlb_valids(tlb_req_index)(tlb_hit_way) <= '0';
+ end if;
+ elsif tlbwe = '1' then
+ if tlb_hit = '1' then
+ repl_way := tlb_hit_way;
+ else
+ repl_way := to_integer(unsigned(tlb_plru_victim(tlb_req_index)));
+ end if;
+ eatag := r0.req.addr(63 downto TLB_LG_PGSZ + TLB_SET_BITS);
+ tagset := tlb_tag_way;
+ write_tlb_tag(repl_way, tagset, eatag);
+ dtlb_tags(tlb_req_index) <= tagset;
+ pteset := tlb_pte_way;
+ write_tlb_pte(repl_way, pteset, r0.req.data);
+ dtlb_ptes(tlb_req_index) <= pteset;
+ dtlb_valids(tlb_req_index)(repl_way) <= '1';
+ end if;
+ end if;
+ end process;
+