diff --git a/hdl/ip/vhd/espi/sims/espi_tb.vhd b/hdl/ip/vhd/espi/sims/espi_tb.vhd index 63642318..cd17daa4 100644 --- a/hdl/ip/vhd/espi/sims/espi_tb.vhd +++ b/hdl/ip/vhd/espi/sims/espi_tb.vhd @@ -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 ( @@ -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. diff --git a/hdl/ip/vhd/espi/sys_regs/espi_regs.rdl b/hdl/ip/vhd/espi/sys_regs/espi_regs.rdl index 9c992255..73d93d05 100644 --- a/hdl/ip/vhd/espi/sys_regs/espi_regs.rdl +++ b/hdl/ip/vhd/espi/sys_regs/espi_regs.rdl @@ -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"; diff --git a/hdl/ip/vhd/espi/sys_regs/espi_regs.vhd b/hdl/ip/vhd/espi/sys_regs/espi_regs.vhd index 323c5344..5593c9ef 100644 --- a/hdl/ip/vhd/espi/sys_regs/espi_regs.vhd +++ b/hdl/ip/vhd/espi/sys_regs/espi_regs.vhd @@ -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; @@ -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); @@ -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; @@ -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; @@ -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 => diff --git a/hdl/ip/vhd/espi/sys_regs/sp5_post_code_pkg.vhd b/hdl/ip/vhd/espi/sys_regs/sp5_post_code_pkg.vhd new file mode 100644 index 00000000..cd42ac2b --- /dev/null +++ b/hdl/ip/vhd/espi/sys_regs/sp5_post_code_pkg.vhd @@ -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;