3. RTL仿真基础与Verilog核心语法

好,咱们进入第三章。这一章我打算带你把蜂鸟E203的RTL代码翻个底朝天。说白了,就是搞清楚它的“骨架”怎么搭的,“内脏”怎么长的。很多初学者拿到代码就懵,不知道从哪看起。我当年也是这样,对着一个工程目录发呆半小时。

别急,咱们一步步来。先看目录结构,再拆核心模块,最后聊聊总线和存储。看完这章,你至少能闭着眼睛画出E203的顶层框图。

3.1 蜂鸟E203 RTL目录结构:别在文件海洋里迷路

打开E203的RTL目录,你会看到一堆文件夹。别慌,其实就几大类。我个人习惯先看顶层,再往下钻。

目录/文件 作用 我的备注
rtl/e203/core 核心模块,CPU的大脑 最常改的地方,性能调优主战场
rtl/e203/soc 系统级集成,总线、外设都在这里 加外设、改地址映射时来这
rtl/e203/mem 存储子系统,ITCM/DTCM 跑裸机程序时重点关注
rtl/e203/periphs 外设模块,比如GPIO、UART 驱动开发时用得着
rtl/e203/debug 调试模块,JTAG相关 做调试器适配时才碰

你看,结构很清晰。核心、系统、存储、外设、调试,五大块。我建议你从e203_top.v开始看,它是整个芯片的顶层,把所有模块串起来。

小技巧: 用 grep -r "module" rtl/ 快速列出所有模块名,比翻文件夹快多了。

3.2 核心模块(Core)详解:CPU的“五脏六腑”

核心模块在rtl/e203/core下,这是E203的心脏。我拆开给你看。

3.2.1 流水线结构:两级流水,简单粗暴

E203用的是两级流水线:取指+执行。为什么这么设计?说白了就是为了低功耗、小面积。你想想看,高性能处理器动辄十几级流水,但E203的目标是IoT场景,够用就行。

  • 取指阶段(IFU): 从ITCM取指令,做简单预解码。
  • 执行阶段(EXU): 真正的干活阶段,解码、执行、访存、写回全在这。

我记得第一次看E203的代码时,觉得这流水线也太“寒酸”了。但后来在项目中跑实际负载才发现,两级流水避免了大部分控制冒险,编译器稍微优化一下,性能并不差。

3.2.2 关键子模块:IFU、EXU、 LSU

核心模块里,有三个子模块你必须要搞懂:

模块 全称 职责 避坑点
IFU Instruction Fetch Unit 取指令、分支预测(静态) 分支预测错误会浪费一个周期
EXU Execution Unit 算术逻辑运算、跳转 乘法指令是迭代的,注意延迟
LSU Load Store Unit 数据访存,处理DTCM访问 未对齐访问会触发异常

我曾经在调试一个死循环问题时,折腾了两天才发现是LSU的地址计算逻辑有个边界条件没处理好。嗯,从那以后我养成了一个习惯:先检查LSU的地址对齐。

3.3 系统总线与外设模块:怎么把东西连起来

E203用的是ICB总线(Internal Chip Bus),这是蜂鸟自研的简单总线协议。说白了,就是一套“主-从”握手规则。

3.3.1 ICB总线协议:简单到令人发指

ICB只有几个信号:

  • icb_cmd_valid / icb_cmd_ready:命令通道握手
  • icb_rsp_valid / icb_rsp_ready:响应通道握手
  • icb_cmd_addr / icb_cmd_read / icb_cmd_wdata:命令内容

你想想看,比起AXI那套复杂的通道和乱序支持,ICB简直就是一股清流。我建议你在做仿真时,先盯着这几个信号看,总线通不通一眼就能看出来。

3.3.2 外设模块:GPIO、UART怎么挂上去的

外设通过ICB总线挂在系统上。每个外设都有一个基地址,CPU通过load/store指令访问它们。

举个例子,GPIO模块的地址映射:

// 地址映射示例(简化版)
#define GPIO_BASE  0x10000000
#define GPIO_OUT   (*(volatile uint32_t *)(GPIO_BASE + 0x00))
#define GPIO_IN    (*(volatile uint32_t *)(GPIO_BASE + 0x04))
#define GPIO_DIR   (*(volatile uint32_t *)(GPIO_BASE + 0x08))

写GPIO_OUT = 0xFF,就能让8个GPIO引脚同时输出高电平。简单吧?

注意: 外设寄存器的访问必须用 volatile,否则编译器优化会把你坑惨。我曾经在调试UART时,数据死活发不出去,最后发现是编译器把写操作优化掉了。

3.4 存储子系统(ITCM/DTCM)设计:程序和数据放哪

E203没有传统的Cache,而是用了TCM(Tightly Coupled Memory)。说白了,就是直接把SRAM挂在CPU旁边,访问延迟固定,一个周期搞定。

3.4.1 ITCM:指令紧耦合存储器

ITCM存指令。E203默认大小是64KB,地址从0x80000000开始。为什么用这个地址?因为RISC-V规范里,这是默认的指令起始地址。

  • 优点: 访问延迟确定,适合实时性要求高的场景。
  • 缺点: 容量固定,不能像Cache那样动态利用。

3.4.2 DTCM:数据紧耦合存储器

DTCM存数据,默认也是64KB。地址从0x90000000开始。LSU模块直接访问DTCM,没有Cache的缓存一致性问题。

我遇到过一个问题:程序跑着跑着突然崩溃,查了半天发现是ITCM和DTCM的地址范围重叠了。嗯,这种低级错误,犯过一次就再也不会犯了。

3.4.3 存储子系统结构图

下面这张图,是我手绘风格的SVG,展示了E203的存储子系统结构:

蜂鸟E203存储子系统结构 蜂鸟E203 存储子系统结构 E203 Core 指令请求 数据请求 ITCM 64KB @ 0x80000000 DTCM 64KB @ 0x90000000 ICB系统总线 GPIO / UART 其他外设 说明:ITCM/DTCM直接挂在Core旁,延迟固定1周期;外设通过ICB总线访问,延迟稍大

从图上你能看到:Core直接连ITCM和DTCM,不走总线。外设则通过ICB总线挂载。这种设计的好处是,CPU访问指令和数据时没有总线竞争,性能有保障。

核心要点: E203的存储子系统没有Cache,用TCM替代。优点是延迟确定、设计简单;缺点是容量固定、灵活性差。适合对实时性要求高、程序大小可控的嵌入式场景。

3.5 本章小结:你该记住什么

好,这一章的内容就这些。我带你看了目录结构、拆了核心模块、聊了总线、画了存储。你至少应该记住:

  • E203的RTL分五大块:core、soc、mem、periphs、debug
  • 核心模块是两级流水:IFU取指,EXU执行,LSU访存
  • 总线是ICB协议,简单但够用
  • 存储用ITCM/DTCM,没有Cache