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
66 changes: 66 additions & 0 deletions hdl/ip/vhd/espi/sims/espi_tb.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use work.espi_spec_regs_pkg.all;
use work.espi_regs_pkg;
use work.espi_dbg_vc_pkg.all;
use work.espi_tb_pkg.all;
use work.sp5_post_code_pkg.all;

entity espi_tb is
generic (
Expand Down Expand Up @@ -311,6 +312,71 @@ begin
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_COUNT_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"00000000"), "Post code count register did not reset after espi reset");

elsif run("post_code_monitor_check") then
enable_debug_mode(net);
-- Send each tracked post code in turn, verifying the corresponding
-- sticky bit accumulates in the monitor register.
dbg_send_post_code(net, POST_CODE_BL_SUCCESS_C_MAIN);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"00000001"), "bl_success_c_main bit should be set");

dbg_send_post_code(net, POST_CODE_TP_PROC_MEM_AFTER_MEM_DATA_INIT);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"00000003"), "tp_proc_mem_after_mem_data_init bit should be set");

dbg_send_post_code(net, POST_CODE_TP_ABL7_RESUME_INITIALIZATION);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"00000007"), "tp_abl7_resume_initialization bit should be set");

dbg_send_post_code(net, POST_CODE_TP_ABL_MEMORY_DDR_TRAINING_START);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"0000000f"), "tp_abl_memory_ddr_training_start bit should be set");

dbg_send_post_code(net, POST_CODE_TP_PROC_CPU_OPTIMIZED_BOOT_START);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"0000001f"), "tp_proc_cpu_optimized_boot_start bit should be set");

dbg_send_post_code(net, POST_CODE_TP_ABL4_APOB);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"0000003f"), "tp_abl4_apob bit should be set");

dbg_send_post_code(net, POST_CODE_BL_SUCCESS_BIOS_LOAD_COMPLETE);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"0000007f"), "bl_success_bios_load_complete bit should be set");

dbg_send_post_code(net, POST_CODE_PHBLHELLO);
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"000000ff"), "phbl_hello bit should be set, all bits should now be set");

-- A non-matching post code must not change the monitor register.
dbg_send_post_code(net, x"DEADBEEF");
dbg_wait_for_done(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"000000ff"), "non-matching post code must not alter monitor register");

-- espi_reset must clear all monitor bits.
dbg_espi_reset(net);
wait for 1 us;
read_bus(net, bus_handle, To_StdLogicVector(espi_regs_pkg.POST_CODE_MONITOR_OFFSET, bus_handle.p_address_length), data_32);
check_equal(data_32, std_logic_vector'(x"00000000"), "monitor register must clear on espi reset");

-- ============================================================
-- Verify that pc_free correctly deasserts when the RX FIFO
-- approaches capacity, and reasserts after draining.
Expand Down
54 changes: 54 additions & 0 deletions hdl/ip/vhd/espi/sys_regs/espi_regs.rdl
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,60 @@ addrmap espi_regs {
} count[31:0] = 0;
} post_code_count;

reg {
name = "Post Code Monitor";
desc = "Monitor and set flags based on certain incoming post codes. This is useful for testing and debug to trigger certain actions based on post codes seen from the host.";

field {
default sw = r;
default hw = w;
desc = "0x1de90001 - PHBLhello. Sticky: set when post code is seen, cleared by hardware on new boot.";
} phbl_hello[7:7] = 0;

field {
default sw = r;
default hw = w;
desc = "0xee1000bb - BL_SUCCESS_BIOS_LOAD_COMPLETE. Sticky: set when post code is seen, cleared by hardware on new boot.";
} bl_success_bios_load_complete[6:6] = 0;

field {
default sw = r;
default hw = w;
desc = "0xea00e0c9 - TpAbl4Apob. Sticky: set when post code is seen, cleared by hardware on new boot.";
} tp_abl4_apob[5:5] = 0;

field {
default sw = r;
default hw = w;
desc = "0xea00e055 - TpProcCpuOptimizedBootStart. Sticky: set when post code is seen, cleared by hardware on new boot.";
} tp_proc_cpu_optimized_boot_start[4:4] = 0;

field {
default sw = r;
default hw = w;
desc = "0xea00e340 - TpAblMemoryDdrTrainingStart - seen instead of TpAbl7ResumeInitialization when no eMCR. Sticky: set when post code is seen, cleared by hardware on new boot.";
} tp_abl_memory_ddr_training_start[3:3] = 0;

field {
default sw = r;
default hw = w;
desc = "0xea00e101 - TpAbl7ResumeInitialization - eMCR starting (not seen if no eMCR). Sticky: set when post code is seen, cleared by hardware on new boot.";
} tp_abl7_resume_initialization[2:2] = 0;

field {
default sw = r;
default hw = w;
desc = "0xea00e046 - TpProcMemAfterMemDataInit - memory data initialised. Sticky: set when post code is seen, cleared by hardware on new boot.";
} tp_proc_mem_after_mem_data_init[1:1] = 0;

field {
default sw = r;
default hw = w;
desc = "0xee1000a0 - BL_SUCCESS_C_MAIN - Successfully entered Main. Sticky: set when post code is seen, cleared by hardware on new boot.";
} bl_success_c_main[0:0] = 0;

} post_code_monitor;

reg {
name = "IPCC_SP_TO_HOST_USEDWDS";
desc = "Count of usedwds in the IPCC to host TX FIFO";
Expand Down
6 changes: 6 additions & 0 deletions hdl/ip/vhd/espi/sys_regs/espi_regs.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use work.espi_regs_pkg.all;
use work.espi_spec_regs_view_pkg.all;
use work.espi_spec_regs_pkg;
use work.qspi_link_layer_pkg.all;
use work.sp5_post_code_pkg.all;
use work.axil15x32_pkg.all;
use work.calc_pkg.log2ceil;

Expand Down Expand Up @@ -58,6 +59,7 @@ architecture rtl of espi_regs is
signal oob_free_saw_full_reg : oob_free_saw_full_type;
signal last_resp_status_reg : espi_status_type;
signal live_status_reg : espi_status_type;
signal post_code_monitor_reg : post_code_monitor_type;
constant BUFFER_ENTRIES : integer := 4096;
constant BUFFER_ADDR_WIDTH : integer := log2ceil(BUFFER_ENTRIES);
signal pc_buf_waddr : std_logic_vector(BUFFER_ADDR_WIDTH - 1 downto 0);
Expand Down Expand Up @@ -104,6 +106,7 @@ begin
control_reg <= rec_reset;
last_post_code_reg <= rec_reset;
post_code_count_reg <= rec_reset;
post_code_monitor_reg <= rec_reset;
pc_buf_waddr <= (others => '0');
stuff_count <= rec_reset;
stuff_enable <= rec_reset;
Expand All @@ -123,10 +126,12 @@ begin
if espi_reset then
last_post_code_reg <= rec_reset;
post_code_count_reg <= rec_reset;
post_code_monitor_reg <= rec_reset;
pc_buf_waddr <= (others => '0');
elsif post_code_valid then
last_post_code_reg <= unpack(post_code);
post_code_count_reg <= unpack(post_code_count_reg.count + 1);
post_code_monitor_reg <= post_code_monitor_reg or decode_post_code_monitor(post_code);
pc_buf_waddr <= pc_buf_waddr + 1;
end if;
end if;
Expand Down Expand Up @@ -177,6 +182,7 @@ begin
resp_fifo_ack <= '1';
when LAST_POST_CODE_OFFSET => rdata <= pack(last_post_code_reg);
when POST_CODE_COUNT_OFFSET => rdata <= pack(post_code_count_reg);
when POST_CODE_MONITOR_OFFSET => rdata <= pack(post_code_monitor_reg);
when IPCC_TO_HOST_USEDWDS_OFFSET =>
rdata <= resize(to_host_tx_fifo_usedwds, rdata'length);
when IPCC_TO_HOST_BYTE_CNTR_OFFSET =>
Expand Down
69 changes: 69 additions & 0 deletions hdl/ip/vhd/espi/sys_regs/sp5_post_code_pkg.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
--
-- Copyright 2026 Oxide Computer Company

-- Composite record and views for exposing eSPI spec register
-- values as a read-only interface in the sys_regs address space.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.espi_spec_regs_pkg.all;
use work.espi_regs_pkg.all;


package sp5_post_code_pkg is

-- Post codes for SP5
constant POST_CODE_BL_SUCCESS_C_MAIN : std_logic_vector(31 downto 0) := x"EE1000A0";
constant POST_CODE_TP_PROC_MEM_AFTER_MEM_DATA_INIT : std_logic_vector(31 downto 0) := x"EA00E046";
constant POST_CODE_TP_ABL7_RESUME_INITIALIZATION : std_logic_vector(31 downto 0) := x"EA00E101";
constant POST_CODE_TP_ABL_MEMORY_DDR_TRAINING_START : std_logic_vector(31 downto 0) := x"EA00E340";
constant POST_CODE_TP_PROC_CPU_OPTIMIZED_BOOT_START : std_logic_vector(31 downto 0) := x"EA00E055";
constant POST_CODE_TP_ABL4_APOB : std_logic_vector(31 downto 0) := x"EA00E0C9";
constant POST_CODE_BL_SUCCESS_BIOS_LOAD_COMPLETE : std_logic_vector(31 downto 0) := x"EE1000BB";
constant POST_CODE_PHBLHELLO : std_logic_vector(31 downto 0) := x"1DE90001";

-- Given a valid strobe and a post code, return a post_code_monitor_type with
-- the matching monitor bit set (all others '0'). Returns all zeros when valid
-- is '0' or the post code is not one of the tracked values. The caller is
-- expected to OR this into the sticky register each cycle.
function decode_post_code_monitor(
post_code : std_logic_vector(31 downto 0)
) return post_code_monitor_type;

end package sp5_post_code_pkg;

package body sp5_post_code_pkg is

function decode_post_code_monitor(
post_code : std_logic_vector(31 downto 0)
) return post_code_monitor_type is
variable ret : post_code_monitor_type := rec_reset;
begin
case post_code is
when POST_CODE_BL_SUCCESS_C_MAIN =>
ret.bl_success_c_main := '1';
when POST_CODE_TP_PROC_MEM_AFTER_MEM_DATA_INIT =>
ret.tp_proc_mem_after_mem_data_init := '1';
when POST_CODE_TP_ABL7_RESUME_INITIALIZATION =>
ret.tp_abl7_resume_initialization := '1';
when POST_CODE_TP_ABL_MEMORY_DDR_TRAINING_START =>
ret.tp_abl_memory_ddr_training_start := '1';
when POST_CODE_TP_PROC_CPU_OPTIMIZED_BOOT_START =>
ret.tp_proc_cpu_optimized_boot_start := '1';
when POST_CODE_TP_ABL4_APOB =>
ret.tp_abl4_apob := '1';
when POST_CODE_BL_SUCCESS_BIOS_LOAD_COMPLETE =>
ret.bl_success_bios_load_complete := '1';
when POST_CODE_PHBLHELLO =>
ret.phbl_hello := '1';
when others =>
null;
end case;
return ret;
end function;

end package body sp5_post_code_pkg;
Loading