这是SPI桥的叉骨,将叉骨交易从主机转换为SPI-MEMORY交易。它可用于将SPI闪光灯,EEPROM或RAM映射到处理器的地址空间中。桥梁透明地运行,因此它的行为就像是内存映射的读/写内存,因为它隐藏了SPI协议 - 以额外的延迟为代价。因此,桥梁支持实地执行(XIP),允许处理器直接访问(并执行)指令和位于SPI内存中的数据。
这是NEORV32 RISC-V处理器的另一个“分拆”项目。有一天,可以将桥作为XIP模块添加到处理器中。
关键功能
戒酒 /想法 /帮助
该桥基于单个VHDL文件rtl/wb_spi_bridge.vhd 。顶级实体是wb_spi_bridge ,可以直接对其进行实例化,而无需任何特殊库。
entity wb_spi_bridge is
generic (
SPI_CLK_DIV : positive ; -- clock divider: f(spi_clk_o) = f(clk_i) / 2*SPI_CLK_DIV, min 2
SPI_ABYTES : positive ; -- number of address bytes in SPI protocol, 1..4
SPI_CPHA : std_ulogic ; -- clock phase
SPI_CPOL : std_ulogic ; -- idle polarity
WB_ADDR_BASE : std_ulogic_vector ( 31 downto 0 ); -- module base address, size-aligned
WB_ADDR_SIZE : positive -- module address space in bytes
);
port (
-- wishbone host interface --
wb_clk_i : in std_ulogic ; -- clock
wb_rstn_i : in std_ulogic ; -- reset, async, low-active
wb_srstn_i : in std_ulogic ; -- reset, SYNC, low-active
wb_adr_i : in std_ulogic_vector ( 31 downto 0 ); -- address
wb_dat_i : in std_ulogic_vector ( 31 downto 0 ); -- read data
wb_dat_o : out std_ulogic_vector ( 31 downto 0 ); -- write data
wb_we_i : in std_ulogic ; -- read/write
wb_sel_i : in std_ulogic_vector ( 03 downto 0 ); -- byte enable
wb_stb_i : in std_ulogic ; -- strobe
wb_cyc_i : in std_ulogic ; -- valid cycle
wb_ack_o : out std_ulogic ; -- transfer acknowledge
wb_err_o : out std_ulogic ; -- transfer error
-- SPI device interface --
spi_csn_o : out std_ulogic ; -- chip-select, low-active
spi_clk_o : out std_ulogic ; -- serial clock
spi_data_i : in std_ulogic ; -- device data output
spi_data_o : out std_ulogic -- controller data output
);
end wb_spi_bridge ;顶级实体实例化了两个内文件实体:
wb_spi_bridge_link处理SPI内存协议(读,写,写启用)wb_spi_bridge_phy处理实际的串行外围接口协议wb_rstn_i )和一个同步一个( wb_srstn_i ),它们都是低活动的。需要异步复位将桥梁带入定义的状态。同步重置是可选的,可用于从应用程序逻辑重置桥梁。如果不使用该重置,将此重置绑在1中。
Wishbone-to-Spi桥的应用特定配置是使用顶部实体的通用物进行的。
SPI时钟频率是使用SPI_CLK_DIV通用的配置。它定义了一个适用于时钟输入clk_i缩放器以获得实际的SPI时钟。 SPI时钟由f_spi = f_main / (2 * SPI_CLK_DIV)定义。时钟缩放器的最小值为2,导致输入时钟速度的最大SPI时钟速度为1/4。
实际时钟模式是通过SPI_CPHA和SPI_CPOL配置的。这两个通用物的组合允许配置四种标准SPI时钟模式中的任何一个。有关时钟模式的更多信息,请参见Spi Wikipedia文章。
SPI_ABYTES GENERIC配置了SPI内存的地址大小,该大小由其容量定义。允许的值是1、2、3和4。大多数SPI EEPROM使用的地址大小为16位,导致SPI_ABYTES = 2 。许多标准的SPI闪存存储器使用24位用于SPI_ABYTES = 3的地址。
桥仅使用三个标准内存命令。如果特定SPI内存的命令不同,则可以在VHDL源文件中调整它们:
-- spi memory opcodes -----------------------------------------------------------
constant cmd_write_c : std_ulogic_vector ( 7 downto 0 ) := x"02" ; -- write data
constant cmd_read_c : std_ulogic_vector ( 7 downto 0 ) := x"03" ; -- read data
constant cmd_wren_c : std_ulogic_vector ( 7 downto 0 ) := x"06" ; -- write enable
-- ------------------------------------------------------------------------------桥的32位基础地址由WB_ADDR_BASE通用定义。定义SPI内存的容量的被占用地址空间的大小由WB_ADDR_SIZE GENEL配置。地址空间大小必须是两个功率,基本地址必须自然与此尺寸保持一致。
示例:具有64KB的SPI Flash(64*1024字节):
WB_ADDR_BASE => 0 x"FFFF0000" ,
WB_ADDR_SIZE => 64 * 1024在这种情况下, 0xFFFF8000的基础地址将是无效的,因为它与64kb边界无天然。
主机接口基于Wishbone B4规范。它支持经典和管道的单一访问转移。爆发转移尚未支持。
| 经典模式阅读访问示例 | 管道模式写入访问示例 |
|---|---|
stb和cyc都保持主张,直到转移完成为止。cyc保持主张,直到传输完成为止。在转移开始时,仅在一个周期中主张stb 。 |设置确认信号ack (一个周期为活动)时,将完成转移。这表明成功终止。设置了误差信号err (一个周期为活动)时,也可以完成转移。仅当设置了不支持的字节启用掩码时,桥梁才会引起误差信号(见下文)。
桥支撑32位,16位和8位写入访问。读取访问始终返回完整的32位单词。请注意,桥梁可访问小型字节订单中的内存数据。 sel字节蒙版信号定义了根据访问的数据数量。支持以下字节蒙版:
sel | r/w | 数据数量 |
|---|---|---|
1111 | 读 | 完整的32位单词 |
1111 | 写 | 完整的32位单词 |
1100 | 写 | 高16位半词 |
0011 | 写 | 较低的16位半词 |
0001 | 写 | 字节0 |
0010 | 写 | 字节1 |
0100 | 写 | 字节2 |
1000 | 写 | 字节3 |
所有剩余的字节蒙版组合都会引起传输误差( err一个周期设置为一个周期而不是ack )。
桥总是将完整的命令序列发送到SPI内存的任何类型的叉骨转移(尚未支持爆发)。这意味着内存操作码,地址位和实际数据都是为每个事务传输的。下表显示了每种类型的交易中通过SPI传输的位数。每个位都需要SPI时钟的一个时钟。
| r/w | 数据大小 | CMD位 | 地址位 | 数据位 | 总数 |
|---|---|---|---|---|---|
| 读 | 单词(32位) | 8 | 8*SPI_ABYTES | 32 | 8 + 8*SPI_ABYTES + 32 |
| 写 | 单词(32位) | 8 + 8 | 8*SPI_ABYTES | 32 | 8 + 8 + 8*SPI_ABYTES + 32 |
| 写 | 半词(16位) | 8 + 8 | 8*SPI_ABYTES | 16 | 8 + 8 + 8*SPI_ABYTES + 16 |
| 写 | 字节(8位) | 8 + 8 | 8*SPI_ABYTES | 8 | 8 + 8 + 8*SPI_ABYTES + 8 |
例如,单词宽的写入访问使用24位地址的SPI闪存需要8 + 8 + 8*3 + 32 = 72 SPI时钟周期。一个SPI时钟周期等于2 * SPI_CLK_DIV循环clk_i 。
任何类型的写入访问都需要设置内存的写入启用闩锁,该闩锁已发送单个8位命令。
这些项目提供了一个简单的TestBench sim/wb_spi_bridge_tb.vhd ,可以通过ghdl通过提供脚本来模拟该项目:
wb_spi_bridge/sim$ sh ghdl.sh模拟将使用100MHz时钟以1ms的速度运行。在此期间,触发所有支持的(和一个非法的)叉骨访问。请注意,此测试台旨在用于手动波形检查 - 这不是自我检查!波形数据存储到sim/wb_spi_bridge.vcd ,可以使用gtkwave查看:
wb_spi_bridge/sim$ gtkwave wb_spi_bridge.vcd该桥已被FPGA提供。通过将其直接连接到NEORV32 RISC-V处理器(无wishbone互连)连接到25LC512 SPI EEPROM的NEORV32 RISC-V处理器的叉骨接口端口来测试。
NEORV32软件示例提供了一个“总线资源管理器”程序,该程序允许通过UART终端执行任意叉骨访问。该桥可以成功处理所有读写操作。此外,NEORV32可以成功地从SPI模块执行程序。
以下配置的示例映射结果:
SPI_CLK_DIV => 32 ,
SPI_ABYTES => 2 , -- 16-bit addressing
SPI_CPHA => '0' ,
SPI_CPOL => '0' , -- clock mode 0
WB_ADDR_BASE => x"F0000000" ,
WB_ADDR_SIZE => 64 * 1024 EP4CE22F17C6N @100MHz Hierarchy Logic Cells Logic Registers
----------------------------------------------------------------------------
wb_spi_bridge:wb_spi_bridge_inst 233 (115) 174 (88)
wb_spi_bridge_link:wb_spi_bridge_link_inst 118 (16) 86 (8)
wb_spi_bridge_phy:wb_spi_bridge_phy_inst 102 (102) 78 (78)