一、功能
(1)支持 SPI 模式 0 和模式 3(关于 SPI 模式的介绍网上有很多,这里不再赘述。重点注意一下:对于模式 0 和模式 3,发送端在 SPI 时钟下降沿发送数据,接收端在 SPI 时钟上升沿采样数据);
(2)支持 Standard SPI,Dual SPI,Quad SPI 模式;
(3)支持 XIP 模式(支持 W25Q、N25Q、MX25L 等系列 SPI Flash);
对于 XIP 模式的定义,每一款 Flash 器件的说法似乎有些细微的差异。本文的理解就是,在 XIP 模式下,Flash 器件可以看作一块类似于 sram 的存储设备,只需要通过地址即可读取 Flash 的存储内容。不需要像 SPI 接口读 Flash 时那样,需要先发送 flash 专用的读命令,其次发送读地址,以及可能需要发送的 dummy cycle,然后 Flash 才会返回读数据。本文设计的 SPI 主控器的 AHB path 能够将 AHB 读操作直接转换成 SPI 读 Flash 操作,因此从 AHB 接口端来看,Flash 就像一个普通的 sram 一样,通过地址即可读取 Flash 中的数据。
(4)SPI 控制器时钟频率为 50MHz 时(SPI 接口时钟 sclk 频率最高为 25MHz),AHB 接口地址连续读 W25QXX Flash 带宽最高约为 12.5MB/s,随机地址读带宽约 4.3MB/s;
(5)支持对 AHB 接口读取的数据计算 CRC32 校验码(目的:验证 Flash Device 中的数据是否被正确传输到 SPI 主控制器端);
二、架构设计
![图片:SPI 架构设计]
三、接口介绍
(1)AMBA3 AHB-Lite 接口
(2)AMBA4 APB 接口
(3)SPI 接口
| Name | IO Type | description |
|---|
| cs_n | output | SPI 片选信号 |
| sclk | output | SPI 时钟信号 |
| io_0 | inout | SPI 数据线 0,MOSI |
| io_1 | inout | SPI 数据线 1,MISO |
| io_2 | inout | SPI 数据线 2 |
| io_3 | inout | SPI 数据线 3 |
(4)中断电平信号
在以下情况时会产生中断:
i. 发送 buffer 为空;
ii. 接收 buffer 非空;
iii. 在 AHB path 检测到写操作(AHB path 为 XIP 模式,只支持读操作);
四、寄存器介绍
![图片:寄存器映射]
五、APB 接口读写 W25Q128 Flash
APB 读写 Flash 需要通过多次访问寄存器来实现。
5.1 APB 写 Flash
以 W25Q128 Flash 中写 Flash 的命令 Page Program (02h)为例,只需要通过访问寄存器依次将 1byte 命令、3byte 地址和若干待写入的数据以 byte 为单位发送出去即可。W25Q128 的 Page Program 命令的 sequence 如下图所示:
![图片:Page Program 序列]
5.1.1 寄存器访问具体流程(以向地址 24'h123456 写入 8'hA0 为例)
(1) 向寄存器地址 8'h20 写入 32'h02(即把命令 8'h02 写入 tx_buf);
(2) 向寄存器地址 8'h0 写入 32'h1(即 abort=0, last_byte=0, op_mode=0, rx_trig=0, tx_trig=1);
(3) 循环读寄存器地址 8'h10,直到读数据的 bit0 为 1(即 tx_buf_empty=1);
(4) 向寄存器地址 8'h20 写入 32'h12(即把地址的高 8bit 写入 tx_buf);
(5) 向寄存器地址 8'h0 写入 32'h1;
(6) 循环读寄存器地址 8'h10,直到读数据的 bit0 为 1;
(7) 向寄存器地址 8'h20 写入 32'h34(即把地址的中间 8bit 写入 tx_buf);
(8) 向寄存器地址 8'h0 写入 32'h1;
(9) 循环读寄存器地址 8'h10,直到读数据的 bit0 为 1;
(10) 向寄存器地址 8'h20 写入 32'h56(即把地址的低 8bit 写入 tx_buf);
(11) 向寄存器地址 8'h0 写入 32'h1;
(12) 循环读寄存器地址 8'h10,直到读数据的 bit0 为 1;
(13) 向寄存器地址 8'h20 写入 32'hA0(即把要写入 Flash 的数据写入 tx_buf);
(14) 向寄存器地址 8'h0 写入 32'h11(即 abort=0, last_byte=1, op_mode=0, rx_trig=0, tx_trig=1);
循环读寄存器地址 8'h10,直到读数据的 bit0 为 1;
5.1.2 仿真波形
![图片:APB 写 Flash 波形]
5.2 APB 读 Flash 状态寄存器
在 5.1 节中介绍了 Page Program 指令写 Flash 的流程,但是 Page Program 指令本身只是负责把要写入 Flash 的数据发送给 Flash 端,而数据真正写入 Flash 存储单元还需要一段时间,这个时间一般是需要若干毫秒。主控端可以通过定时的方式,固定延时多少毫秒后再发起下一次对 Flash 的读写访问;也可以通过循环读 Flash 中的状态寄存器,根据状态寄存器的值判断上一次的写操作或者擦除操作是否完成。本文设计的 SPI 控制器没有定时的功能,因此只能通过软件定时或者读 Flash 状态寄存器的方式判断当前 Flash 是否处于 busy 状态。
![图片:状态寄存器位定义]
W25Q128 的 BUSY 位在状态寄存器 1 的 bit0,读状态寄存器 1 的 sequence 如下图所示:
![图片:读状态寄存器序列]
5.2.1 寄存器访问具体流程(以循环读 W25Q128 状态寄存器 1,且直到 BUSY 等于 0 为例)
(1) 向寄存器地址 8'h20 写入 32'h05(即把命令 8'h05 写入 tx_buf);
(2) 向寄存器地址 8'h0 写入 32'h2(即 abort=0, last_byte=0, op_mode=0, rx_trig=1, tx_trig=0);
(3) 循环读寄存器地址 8'h10,直到读数据的 bit 1 为 1(即 rx_buf_full=1);
(4) APB 读寄存器地址 8'h24,APB read data 即为状态寄存器 1 的值;
(5) 判断状态寄存器 1 的 bit 0(BUSY)是否为 0,若为 0 则表示 flash 处于 idle 状态,向寄存器地址 8'h0 写入 32'h21/32'h22(即 abort 置 1,rx_trig 或 tx_trig 置 1,op_mode 和 last_byte 可以为任意值),将 cs_n 直接拉高,结束当前的 SPI 传输;否则返回步骤 (2);
5.2.2 仿真波形
![图片:读状态寄存器波形 1]
![图片:读状态寄存器波形 2]
六、AHB 接口读 W25Q128 Flash(即 XIP 模式)
AHB 读数据位宽为 32bit,一次 AHB 读命令固定从 Flash 中读 4byte 数据,因此建议通过 AHB path 读 Flash 时,一次读 32bit 数据,从而提高读取速率。(AHB 的 narrow transfer 也是支持的,支持 AHB 一次读 1byte、2byte、4byte 数据,只是 AHB 读操作转换成 SPI 读 Flash 操作时,都是 4 字节对齐的读 Flahs 操作)
另外,该 SPI 主控支持 AHB 读取一个地址后,在没有收到下一个 AHB 读命令的情况下,自动从 Flash 中读取下一个地址的 32bit 数据,节省 SPI 读 Flash 时的命令、地址和 dummy 的开销,从而提高 AHB 连续读 Flash 的带宽。
(1)AHB 地址非连续读 Flash 波形如下,第一个读地址为 24'h123450,第二个读地址为 24'h123458。
![图片:AHB 非连续读波形]
(2)AHB 地址连续读 Flash 波形如下,第一个读地址为 24'h123450,第二个读地址为 24'h123454。
![图片:AHB 连续读波形]
七、CRC32 功能使用方法
(1)向寄存器地址 8'h28 写入 32'h3,使能 CRC32 计算的功能,并初始化 CRC32。
(2)通过 AHB 接口从 Flash 中读数据,所有 AHB 接口读取的数据都会参与 CRC32 的计算;
(3)读取寄存器地址 8'h2C,获取 CRC32 的计算结果。
八、SOC 系统集成验证
将该 SPI 主控和 ARM Cortex-M3 核集成在一起,并编写 C 代码对 Flash 进行读写测试。从 Flash 中读数据的测试结果如下。(已提前将 Flash 中的数据初始化为 0~256,即 Flash 地址 0~255 的值依次为 0~255,地址 256~511 的值同样依次为 0~255,其他地址的值以此类推)
测试的 C 代码:
![图片:C 代码截图]
串口打印结果:
![图片:串口打印结果]