Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ YOSYS ?= yosys
VERILATOR ?= verilator
BUILD_DIR ?= build

CPU_RTL = hw/rtl/cpu/alu.sv hw/rtl/cpu/regfile.sv hw/rtl/cpu/rvc_decompress.sv hw/rtl/cpu/cpu_core.sv
BUS_RTL = hw/rtl/bus/wb_error_slave.sv hw/rtl/bus/wb_address_decoder.sv hw/rtl/bus/wb_arbiter.sv hw/rtl/bus/wb_interconnect.sv hw/rtl/bus/wb_cpu_adapter.sv
CPU_RTL = hw/rtl/cpu/alu.sv hw/rtl/cpu/regfile.sv hw/rtl/cpu/rvc_decompress.sv hw/rtl/security/seal_pmp.sv hw/rtl/security/pmp_checker.sv hw/rtl/cpu/cpu_core.sv
BUS_RTL = hw/rtl/bus/wb_error_slave.sv hw/rtl/bus/wb_address_decoder.sv hw/rtl/bus/wb_arbiter.sv hw/rtl/security/wb_firewall.sv hw/rtl/bus/wb_interconnect.sv hw/rtl/bus/wb_cpu_adapter.sv
PERIPHERAL_RTL = hw/rtl/peripherals/wb_uart.sv hw/rtl/peripherals/wb_gpio.sv hw/rtl/peripherals/wb_spi.sv hw/rtl/peripherals/wb_i2c.sv hw/rtl/peripherals/wb_timer.sv hw/rtl/peripherals/wb_watchdog.sv hw/rtl/peripherals/interrupt_controller.sv hw/rtl/peripherals/wb_debug_perf.sv hw/rtl/peripherals/wb_power_ctrl.sv hw/rtl/peripherals/peripheral_subsystem.sv
SECURITY_RTL = hw/rtl/security/seal_pmp.sv hw/rtl/security/pmp_checker.sv hw/rtl/security/wb_firewall.sv

.PHONY: all sim lint synth test-cocotb clean
.PHONY: all sim lint synth test-cocotb security-test cpu-pmp-test clean

all: sim test-cocotb lint synth

Expand All @@ -32,6 +33,18 @@ sim: | $(BUILD_DIR)
$(VVP) $(BUILD_DIR)/peripheral_subsystem_tb
$(IVERILOG) -g2012 -Wall -s peripheral_cpu_irq_tb -o $(BUILD_DIR)/peripheral_cpu_irq_tb $(CPU_RTL) hw/rtl/bus/wb_cpu_adapter.sv hw/rtl/memory/wb_sram.sv hw/rtl/memory/boot_rom.sv hw/rtl/memory/memory_subsystem.sv $(PERIPHERAL_RTL) hw/tb/peripheral_cpu_irq_tb.sv
$(VVP) $(BUILD_DIR)/peripheral_cpu_irq_tb
$(IVERILOG) -g2012 -Wall -s security_tb -o $(BUILD_DIR)/security_tb $(SECURITY_RTL) hw/tb/security_tb.sv
$(VVP) $(BUILD_DIR)/security_tb
$(IVERILOG) -g2012 -Wall -s cpu_pmp_tb -o $(BUILD_DIR)/cpu_pmp_tb $(CPU_RTL) hw/tb/cpu_pmp_tb.sv
$(VVP) $(BUILD_DIR)/cpu_pmp_tb

security-test: | $(BUILD_DIR)
$(IVERILOG) -g2012 -Wall -s security_tb -o $(BUILD_DIR)/security_tb $(SECURITY_RTL) hw/tb/security_tb.sv
$(VVP) $(BUILD_DIR)/security_tb

cpu-pmp-test: | $(BUILD_DIR)
$(IVERILOG) -g2012 -Wall -s cpu_pmp_tb -o $(BUILD_DIR)/cpu_pmp_tb $(CPU_RTL) hw/tb/cpu_pmp_tb.sv
$(VVP) $(BUILD_DIR)/cpu_pmp_tb

lint:
$(VERILATOR) --lint-only --Wall -Wno-fatal --top-module counter hw/rtl/counter.sv
Expand All @@ -40,6 +53,9 @@ lint:
$(VERILATOR) --lint-only --Wall -Wno-fatal --top-module wb_cpu_adapter $(BUS_RTL)
$(VERILATOR) --lint-only --Wall -Wno-fatal --top-module memory_subsystem hw/rtl/memory/wb_sram.sv hw/rtl/memory/boot_rom.sv hw/rtl/memory/memory_subsystem.sv
$(VERILATOR) --lint-only --Wall -Wno-fatal --top-module peripheral_subsystem $(PERIPHERAL_RTL)
$(VERILATOR) --lint-only --Wall -Wno-fatal --top-module seal_pmp $(SECURITY_RTL)
$(VERILATOR) --lint-only --Wall -Wno-fatal --top-module pmp_checker $(SECURITY_RTL)
$(VERILATOR) --lint-only --Wall -Wno-fatal --top-module wb_firewall $(SECURITY_RTL)

synth: | $(BUILD_DIR)
$(YOSYS) -s scripts/synth.ys
Expand Down
2 changes: 1 addition & 1 deletion docs/BUS_SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ This specification defines the implemented Phase 04 subsystem. The RTL implement

## Arbitration and protection

CPU and DMA requests are arbitrated round-robin. Locking/atomic bus cycles are out of scope while the A extension is excluded. PMP and the DMA firewall are not yet connected to this interconnect.
CPU and DMA requests are arbitrated round-robin. Locking/atomic bus cycles are out of scope while the A extension is excluded. The post-arbitration bus firewall independently validates the granted master before target decode, returns `ERR` for denied requests, and logs the master and address. CPU PMP checks instruction fetches and data operations inside the CPU; it does not apply to DMA.

## CDC and timing

Expand Down
2 changes: 2 additions & 0 deletions docs/MEMORY_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ All addresses are byte addresses. Boot ROM and main SRAM are implemented by Phas
- Boot ROM writes return an error. SRAM accesses outside the 32 KiB range return an error. Unmapped addresses return an error and set an observable fault cause.
- Execute permission is denied for MMIO by default. ROM and SRAM execute permissions are governed by CPU privilege/PMP policy.
- DMA may access SRAM and explicitly permitted memory windows only. DMA access to ROM, key-vault state, debug controls, or protected peripheral registers is denied by default.
- SRAM `0x1000_7000`–`0x1000_7FFF` is the secure 4 KiB subregion and is accessible only to the CPU in machine mode through the bus firewall. DMA is limited to `0x1000_0000`–`0x1000_6FFF`.
- The eight-entry CPU PMP governs instruction fetches and data accesses independently of the bus firewall. PMP does not authorize DMA or other non-CPU masters.
- Address decode uses the complete address so high-address aliases cannot reach a valid target.

DMA, crypto, AI accelerator, TRNG, and PMP/firewall register behavior remains to be implemented. Physical clock gating, debug halt/step, and external debug transport are not part of the initial Phase 06 RTL.
4 changes: 3 additions & 1 deletion hw/rtl/bus/wb_arbiter.sv
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ module wb_arbiter #(
output logic [31:0] s_adr, s_dat_w,
output logic [3:0] s_sel,
input logic [31:0] s_dat_r,
input logic s_ack, s_err
input logic s_ack, s_err,
output logic owner_out
);
localparam integer TIMEOUT_LIMIT = (TIMEOUT_CYCLES < 1) ? 1 : TIMEOUT_CYCLES;
localparam integer COUNT_W = (TIMEOUT_LIMIT < 2) ? 1 : $clog2(TIMEOUT_LIMIT + 1);
logic busy, owner, next_owner;
logic [COUNT_W-1:0] wait_count;
logic timeout;
assign owner_out = owner;
assign s_cyc = busy && (owner ? m1_cyc : m0_cyc);
assign s_stb = busy && (owner ? m1_stb : m0_stb);
assign s_we = busy && (owner ? m1_we : m0_we);
Expand Down
30 changes: 27 additions & 3 deletions hw/rtl/bus/wb_interconnect.sv
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@ module wb_interconnect #(
input logic m1_cyc, m1_stb, m1_we,
input logic [31:0] m1_adr, m1_dat_w,
input logic [3:0] m1_sel,
input logic [1:0] m0_privilege, m1_privilege,
output logic [31:0] m1_dat_r,
output logic m1_ack, m1_err,
output logic [2:0] s_cyc, s_stb, s_we,
output logic [95:0] s_adr,
output logic [95:0] s_dat_w,
output logic [11:0] s_sel,
input logic [95:0] s_dat_r,
input logic [2:0] s_ack, s_err
input logic [2:0] s_ack, s_err,
output logic violation_valid,
output logic [31:0] violation_addr,
output logic [2:0] violation_master,
output logic [31:0] violation_count
);
logic d_cyc, d_stb, d_we;
logic [31:0] d_adr, d_dat_w, d_dat_r;
Expand All @@ -32,17 +37,36 @@ module wb_interconnect #(
logic error_ack, error_err;
logic [31:0] error_rdata;
logic target_ack, target_err;
logic arb_owner;
logic raw_cyc, raw_stb, raw_we;
logic [31:0] raw_adr, raw_dat_w, raw_dat_r;
logic [3:0] raw_sel;
logic fw_ack, fw_err, fw_cyc, fw_stb, fw_we;
logic [31:0] fw_adr, fw_dat_w;
logic [3:0] fw_sel;

wb_arbiter #(.TIMEOUT_CYCLES(TIMEOUT_CYCLES)) arbiter (
.clk, .rst_n,
.m0_cyc, .m0_stb, .m0_we, .m0_adr, .m0_dat_w, .m0_sel,
.m0_dat_r, .m0_ack, .m0_err,
.m1_cyc, .m1_stb, .m1_we, .m1_adr, .m1_dat_w, .m1_sel,
.m1_dat_r, .m1_ack, .m1_err,
.s_cyc(d_cyc), .s_stb(d_stb), .s_we(d_we), .s_adr(d_adr), .s_dat_w(d_dat_w), .s_sel(d_sel),
.s_dat_r(d_dat_r), .s_ack(d_ack), .s_err(d_err)
.s_cyc(raw_cyc), .s_stb(raw_stb), .s_we(raw_we), .s_adr(raw_adr), .s_dat_w(raw_dat_w), .s_sel(raw_sel),
.s_dat_r(raw_dat_r), .s_ack(fw_ack), .s_err(fw_err), .owner_out(arb_owner)
);

wb_firewall firewall (
.clk, .rst_n, .master_id(arb_owner ? 3'd1 : 3'd0),
.privilege(arb_owner ? m1_privilege : m0_privilege), .cpu_execute(1'b0),
.m_cyc(raw_cyc), .m_stb(raw_stb), .m_we(raw_we), .m_adr(raw_adr), .m_dat_w(raw_dat_w), .m_sel(raw_sel),
.m_dat_r(raw_dat_r), .m_ack(fw_ack), .m_err(fw_err),
.s_cyc(fw_cyc), .s_stb(fw_stb), .s_we(fw_we), .s_adr(fw_adr), .s_dat_w(fw_dat_w), .s_sel(fw_sel),
.s_dat_r(d_dat_r), .s_ack(d_ack), .s_err(d_err),
.violation_valid, .violation_addr, .violation_master, .violation_count
);
assign d_cyc=fw_cyc; assign d_stb=fw_stb; assign d_we=fw_we;
assign d_adr=fw_adr; assign d_dat_w=fw_dat_w; assign d_sel=fw_sel;

wb_address_decoder decoder (.adr(d_adr), .select(target), .unmapped);
wb_error_slave error_target (
.clk, .rst_n,
Expand Down
75 changes: 68 additions & 7 deletions hw/rtl/cpu/cpu_core.sv
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,51 @@ module cpu_core (
logic signed [63:0] mul_a_signed, mul_b_signed;
logic [63:0] mul_a_unsigned, mul_b_unsigned;
logic [31:0] muldiv_result;
logic [63:0] pmpcfg_flat;
logic [255:0] pmpaddr_flat;
logic [7:0] pmp_cfg_we;
logic pmp_addr_we, pmp_data_deny, pmp_fetch_deny, pmp_data_allow, pmp_fetch_allow;
logic [2:0] pmp_addr_index;
logic [31:0] pmp_addr_write_data;
logic [4:0] pmp_data_size, pmp_fetch_size;
logic pmp_mem_ready, pmp_mem_error;
logic [7:0] pmp_cfg_unused;
logic [31:0] pmp_addr_unused;

assign pmp_cfg_we = (state==ST_EXEC && !pmp_fetch_deny && dec_is_csr && csr_write && !dec_exception &&
(csr_addr==12'h3a0 || csr_addr==12'h3a2)) ?
((csr_addr==12'h3a0) ? 8'h0f : 8'hf0) : 8'h00;
assign pmp_addr_we = (state==ST_EXEC && !pmp_fetch_deny && dec_is_csr && csr_write && !dec_exception &&
csr_addr>=12'h3b0 && csr_addr<=12'h3b7);
assign pmp_addr_index = csr_addr[2:0];
assign pmp_addr_write_data = csr_wdata;
seal_pmp pmp_regs (
.clk, .rst_n, .cfg_we(pmp_cfg_we), .cfg_wdata({csr_wdata,csr_wdata}),
.addr_we(pmp_addr_we), .addr_index(pmp_addr_index), .addr_wdata(pmp_addr_write_data),
.read_index(csr_addr[2:0]), .cfg_rdata(pmp_cfg_unused), .addr_rdata(pmp_addr_unused),
.cfg_flat(pmpcfg_flat), .addr_flat(pmpaddr_flat)
);
always_comb begin
case (mem_funct3)
3'b000,3'b100: pmp_data_size=1;
3'b001,3'b101: pmp_data_size=2;
default: pmp_data_size=4;
endcase
pmp_fetch_size=compressed ? 2 : 4;
end
pmp_checker pmp_data_check (
.addr(mem_aligned_addr + {30'b0,mem_lane}), .size_bytes(pmp_data_size),
.access_read(!mem_is_store), .access_write(mem_is_store), .access_execute(1'b0),
.privilege(2'b11), .cfg_flat(pmpcfg_flat), .addr_flat(pmpaddr_flat),
.allow(pmp_data_allow), .matched(), .matched_index()
);
pmp_checker pmp_fetch_check (
.addr(pc), .size_bytes(pmp_fetch_size), .access_read(1'b0), .access_write(1'b0), .access_execute(1'b1),
.privilege(2'b11), .cfg_flat(pmpcfg_flat), .addr_flat(pmpaddr_flat),
.allow(pmp_fetch_allow), .matched(), .matched_index()
);
assign pmp_data_deny = !pmp_data_allow;
assign pmp_fetch_deny = !pmp_fetch_allow;

assign compressed_word = instr[15:0];
assign compressed = (compressed_word[1:0] != 2'b11);
Expand Down Expand Up @@ -131,6 +176,10 @@ module cpu_core (
12'h304: csr_rdata = mie;
12'h344: begin csr_rdata = mip_value; csr_readonly = 1'b1; end
12'h305: csr_rdata = mtvec;
12'h3a0: csr_rdata = pmpcfg_flat[31:0];
12'h3a2: csr_rdata = pmpcfg_flat[63:32];
12'h3b0,12'h3b1,12'h3b2,12'h3b3,12'h3b4,12'h3b5,12'h3b6,12'h3b7:
csr_rdata = pmpaddr_flat[csr_addr[2:0]*32 +: 32];
12'h341: csr_rdata = mepc;
12'h342: csr_rdata = mcause;
12'h343: csr_rdata = mtval;
Expand Down Expand Up @@ -313,31 +362,36 @@ module cpu_core (

always_comb begin
rf_we=0; rf_rd=rd; rf_wdata=dec_write_data;
if (state==ST_EXEC && dec_legal && dec_write_rd && !dec_exception && !dec_is_load && !dec_is_store && !dec_is_mret && !dec_is_wfi) rf_we=1;
else if (state==ST_MEM && dmem_ready && !dmem_error && !mem_is_store) begin rf_we=1; rf_rd=mem_rd; rf_wdata=load_result; end
if (state==ST_EXEC && !pmp_fetch_deny && dec_legal && dec_write_rd && !dec_exception && !dec_is_load && !dec_is_store && !dec_is_mret && !dec_is_wfi) rf_we=1;
else if (state==ST_MEM && dmem_ready && !dmem_error && !pmp_data_deny && !mem_is_store) begin rf_we=1; rf_rd=mem_rd; rf_wdata=load_result; end
end
always_comb begin
dmem_valid=(state==ST_MEM); dmem_write=mem_is_store; dmem_addr=mem_aligned_addr;
dmem_valid=(state==ST_MEM) && !pmp_data_deny; dmem_write=mem_is_store; dmem_addr=mem_aligned_addr;
dmem_wdata=mem_write_data; dmem_wstrb=mem_write_strobe;
end
// One-cycle architectural retirement pulse for observability/performance
// counters. Memory instructions retire only when their bus operation ends.
assign retire = rst_n && !trap_take &&
((state==ST_EXEC && dec_legal && !dec_exception && !dec_is_load && !dec_is_store) ||
(state==ST_MEM && dmem_ready && !dmem_error));
(state==ST_MEM && dmem_ready && !dmem_error && !pmp_data_deny));

assign pmp_mem_ready = dmem_ready || (state==ST_MEM && pmp_data_deny);
assign pmp_mem_error = dmem_error || pmp_data_deny;

always_comb begin
trap_take = 1'b0;
trap_is_interrupt = 1'b0;
trap_entry_cause = 32'd0;
trap_entry_value = 32'd0;
trap_entry_epc = pc;
if (state==ST_EXEC && interrupt_pending && mstatus[3]) begin
if (state==ST_EXEC && pmp_fetch_deny) begin
trap_take=1'b1; trap_entry_cause=32'd1; trap_entry_value=pc;
end else if (state==ST_EXEC && interrupt_pending && mstatus[3]) begin
trap_take=1'b1; trap_is_interrupt=1'b1; trap_entry_cause=interrupt_cause;
end else if (state==ST_EXEC && (!dec_legal || dec_exception)) begin
trap_take=1'b1; trap_entry_cause=dec_legal ? dec_exception_cause : 32'd2;
trap_entry_value=dec_legal ? dec_exception_value : (compressed ? {16'b0,compressed_word} : active_instr);
end else if (state==ST_MEM && dmem_ready && dmem_error) begin
end else if (state==ST_MEM && pmp_mem_ready && pmp_mem_error) begin
trap_take=1'b1; trap_entry_cause=mem_is_store ? 32'd7 : 32'd5;
trap_entry_value=mem_aligned_addr+{30'b0,mem_lane}; trap_entry_epc=mem_pc;
end else if (state==ST_SLEEP && interrupt_pending && mstatus[3]) begin
Expand Down Expand Up @@ -397,7 +451,7 @@ module cpu_core (
end
end
end
ST_MEM: if (dmem_ready) begin
ST_MEM: if (pmp_mem_ready) begin
pc<=mem_pc + (mem_pc_step==2 ? 32'd2 : 32'd4);
state<=ST_EXEC; minstret<=minstret+1'b1;
end
Expand All @@ -410,4 +464,11 @@ module cpu_core (
endcase
end
end

`ifdef FORMAL
default clocking pmp_cb @(posedge clk); endclocking
assert property (state==ST_MEM && pmp_data_deny |-> !dmem_valid);
assert property (state==ST_EXEC && pmp_fetch_deny |-> trap_take && trap_entry_cause==32'd1 && !rf_we);
assert property (state==ST_EXEC && pmp_fetch_deny |-> !(|pmp_cfg_we) && !pmp_addr_we);
`endif
endmodule
69 changes: 69 additions & 0 deletions hw/rtl/security/pmp_checker.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Byte-accurate RV32 PMP checker: OFF, TOR, NA4 and NAPOT; lowest-index
// overlap wins. A request crossing the winning region boundary is denied.
module pmp_checker (
input logic [31:0] addr,
input logic [4:0] size_bytes,
input logic access_read, access_write, access_execute,
input logic [1:0] privilege, // 3=M, 0=U; reserved values fail closed
input logic [63:0] cfg_flat,
input logic [255:0] addr_flat,
output logic allow,
output logic matched,
output logic [2:0] matched_index
);
logic found, full_match, perm_ok;
logic [32:0] req_end, lo, hi;
logic [31:0] a;
logic [7:0] c;
logic overlap, covers;
integer i;
always_comb begin
req_end = {1'b0,addr} + (size_bytes == 0 ? 33'd1 : {28'b0,size_bytes});
found=0; full_match=0; perm_ok=0;
matched=0; matched_index=0; allow=0;
lo=0; hi=0; a=0; c=0; overlap=0; covers=0;
for (i=0;i<8;i=i+1) begin
c=cfg_flat[i*8 +: 8]; a=addr_flat[i*32 +: 32];
lo=0; hi=0;
case (c[4:3])
2'b01: begin // TOR, previous address is exclusive lower bound
lo=(i==0) ? 33'b0 : {1'b0,addr_flat[(i-1)*32 +: 32],2'b00};
hi={1'b0,a,2'b00};
end
2'b10: begin lo={1'b0,a,2'b00}; hi=lo+4; end // NA4
2'b11: begin // NAPOT: trailing ones encode size, 8 bytes minimum
integer n;
logic [31:0] mask;
n=0; mask=0;
for (integer k=0;k<32;k=k+1) if ((k==n) && a[k]) n=n+1;
if (n>=29) begin lo=0; hi=33'h1_0000_0000; end
else begin
mask=(32'hffff_ffff << n) << 2;
lo={1'b0,(a & mask),2'b00};
hi=lo + (33'd1 << (n+3));
end
end
default: begin lo=0; hi=0; end
endcase
overlap=(c[4:3] != 0) && ({1'b0,addr} < hi) && (req_end > lo) && (hi>lo);
covers=({1'b0,addr} >= lo) && (req_end <= hi) && (hi>lo);
if (!found && overlap) begin
found=1; matched=1; matched_index=i[2:0]; full_match=covers;
perm_ok=(!access_read || c[0]) && (!access_write || c[1]) && (!access_execute || c[2]);
// M bypasses unlocked entries, while locked entries apply to M.
if (privilege==2'b11 && !c[7]) perm_ok=1;
end
end
if (privilege==2'b11) allow= !found || (full_match &&
(!cfg_flat[matched_index*8+7] || perm_ok));
else if (privilege==2'b00) allow= found && full_match && perm_ok;
else allow=0;
// With no entry matching, only machine mode is permitted.
end
`ifdef FORMAL
always_comb begin
assert (allow || !matched || !full_match || !perm_ok);
if (privilege==2'b00 && !matched) assert (!allow);
end
`endif
endmodule
43 changes: 43 additions & 0 deletions hw/rtl/security/seal_pmp.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Eight-entry RV32 PMP register bank. pmpcfg is supplied one byte at a time;
// pmpaddr follows the architectural 4 KiB-granularity encoding.
module seal_pmp (
input logic clk,
input logic rst_n,
input logic [7:0] cfg_we,
input logic [63:0] cfg_wdata,
input logic addr_we,
input logic [2:0] addr_index,
input logic [31:0] addr_wdata,
input logic [2:0] read_index,
output logic [7:0] cfg_rdata,
output logic [31:0] addr_rdata,
output logic [63:0] cfg_flat,
output logic [255:0] addr_flat
);
logic [7:0] cfg [0:7];
logic [31:0] paddr [0:7];
integer i;
always_comb begin
cfg_rdata = cfg[read_index];
addr_rdata = paddr[read_index];
for (integer j=0; j<8; j=j+1) begin
cfg_flat[j*8 +: 8] = cfg[j];
addr_flat[j*32 +: 32] = paddr[j];
end
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i=0; i<8; i=i+1) begin cfg[i] <= 0; paddr[i] <= 0; end
end else begin
for (integer j=0; j<8; j=j+1) if (cfg_we[j] && !cfg[j][7]) begin
cfg[j] <= {cfg_wdata[j*8+7], 2'b00, cfg_wdata[j*8+3 +: 2],
cfg_wdata[j*8+2], (cfg_wdata[j*8+1] && cfg_wdata[j*8]), cfg_wdata[j*8]};
// Reserved address mode 2 is WARL and reads back as OFF.
if (cfg_wdata[j*8 + 3 +: 2] == 2'b10) cfg[j][4:3] <= 2'b00;
end
if (addr_we && !cfg[addr_index][7] &&
!(addr_index < 7 && cfg[addr_index+1'b1][7] && cfg[addr_index+1'b1][4:3] == 2'b01))
paddr[addr_index] <= {2'b00,addr_wdata[29:0]};
end
end
endmodule
Loading
Loading