本文以 Sparkfun RED-V 主机为例,分析Tock OS是如何支持的
main() 函数分析
// boards/redboard_redv/src/main.rs
#[no_mangle]
pub unsafe fn main() {
// only machine mode
rv32i::configure_trap_handler(rv32i::PermissionMode::Machine);
...
let peripherals = static_init!(E310xDefaultPeripherals, E310xDefaultPeripherals::new());
}
static_init! 宏
- 分配静态大小的全局内存数组,并为特定数据结构初始化内存
- 这个宏创建静态缓冲区,确保它被初始化为正确的类型,然后返回对它的
&'static mut
引用
E310 默认外设
pub struct E310xDefaultPeripherals<'a> {
pub uart0: sifive::uart::Uart<'a>,
pub gpio_port: crate::gpio::Port<'a>,
pub prci: sifive::prci::Prci,
pub pwm0: sifive::pwm::Pwm,
pub pwm1: sifive::pwm::Pwm,
pub pwm2: sifive::pwm::Pwm,
pub rtc: sifive::rtc::Rtc,
pub watchdog: sifive::watchdog::Watchdog,
}
impl<'a> E310xDefaultPeripherals<'a> {
pub fn new() -> Self {
Self {
uart0: sifive::uart::Uart::new(crate::uart::UART0_BASE, 16_000_000),
gpio_port: crate::gpio::Port::new(),
prci: sifive::prci::Prci::new(crate::prci::PRCI_BASE),
pwm0: sifive::pwm::Pwm::new(crate::pwm::PWM0_BASE),
pwm1: sifive::pwm::Pwm::new(crate::pwm::PWM1_BASE),
pwm2: sifive::pwm::Pwm::new(crate::pwm::PWM2_BASE),
rtc: sifive::rtc::Rtc::new(crate::rtc::RTC_BASE),
watchdog: sifive::watchdog::Watchdog::new(crate::watchdog::WATCHDOG_BASE),
}
}
}
struct Port
pub struct Port<'a> {
pins: [GpioPin<'a>; 32],
}
pub struct GpioPin<'a> {
registers: StaticRef<GpioRegisters>,
pin: Field<u32, pins::Register>,
set: FieldValue<u32, pins::Register>,
clear: FieldValue<u32, pins::Register>,
client: OptionalCell<&'a dyn hil::gpio::Client>,
}
附录
Tock Register Interface
这个crate提供了定义和操作寄存器和位字段的接口和类型
定义寄存器
三种用于处理内存映射寄存器的类型: ReadWrite、 ReadOnly 和 WriteOnly,分别提供了读写、只读和只写功能。这些类型实现了可读、可写和可读特性。
- 寄存器的定义是通过
register_structs
宏完成的,该宏要求每个寄存器有一个偏移量、一个字段名和一个类型。必须按照偏移量递增的顺序和连续性声明寄存器。 - 未定义的字段用
_reserved
标注 - 结构的末尾用它的大小和@END 关键字标记
use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly};
register_structs! {
Registers {
// Control register: read-write
// The 'Control' parameter constrains this register to only use fields from
// a certain group (defined below in the bitfields section).
// “Control”参数将该寄存器限制为仅使用某个组中的字段(定义见下面的bitfields部分)。
// 偏移 寄存器名 访问方式 寄存器大小 寄存器类型
(0x000 => cr: ReadWrite< u8, Control::Register>),
// Status register: read-only
(0x001 => s: ReadOnly<u8, Status::Register>),
// Registers can be bytes, halfwords, or words:
// 第二个类型参数可以省略,这意味着没有为这些寄存器定义位字段
(0x002 => byte0: ReadWrite<u8>),
(0x003 => byte1: ReadWrite<u8>),
(0x004 => short: ReadWrite<u16>),
// 寄存器之间的空白必须用填充字段标记,声明如下。此填充的长度由宏自动计算
(0x006 => _reserved),
(0x008 => word: ReadWrite<u32>),
// The type for a register can be anything. Conveniently, you can use an array when there are a bunch of similar registers.
// 寄存器的类型可以是任何类型。通常当存在一组类似的寄存器时,可以使用数组
(0x00C => array: [ReadWrite<u32>; 4]),
(0x01C => ... ),
// 结构的结尾标记如下, 与结构体大小匹配的@END 标记
(0x100 => @END),
}
}
这将生成以下格式的 c 样式结构
#[repr(C)]
struct Registers {
// Control register: read-write
// The 'Control' parameter constrains this register to only use fields from a certain group (defined below in the bitfields section).
// “Control”参数将该寄存器限制为仅使用某个组中的字段(定义见下面的bitfields部分)。
cr: ReadWrite<u8, Control::Register>,
// Status register: read-only
s: ReadOnly<u8, Status::Register>
// Registers can be bytes, halfwords, or words:
byte0: ReadWrite<u8>,
byte1: ReadWrite<u8>,
short: ReadWrite<u16>,
// The padding length was automatically computed as 0x008 - 0x006.
_reserved: [u8; 2],
word: ReadWrite<u32>,
// Arrays are expanded as-is, like any other type.
array: [ReadWrite<u32>; 4],
// Etc.
}
默认情况下,生成的结构和字段的可见性是私有的。您可以使用 pub 关键字将它们公开
register_structs! {
pub Registers {
(0x000 => foo: ReadOnly<u32>),
(0x004 => pub bar: ReadOnly<u32>),
(0x008 => @END),
}
}
将生成以下结构体
#[repr(C)]
pub struct Registers {
foo: ReadOnly<u32>,
pub bar: ReadOnly<u32>,
}
定义字段
位字段是通过 register_Bitfields!
宏定义的:
register_bitfields! [
// First parameter is the register width. Can be u8, u16, u32, or u64.
//第一个参数是寄存器宽度。可以是u8、u16、u32或u64。
u32,
// Each subsequent parameter is a register abbreviation, its descriptive name, and its associated bitfields. The descriptive name defines this 'group' of bitfields. Only registers defined as ReadWrite<_, Control::Register> can use these bitfields.
// 每个后续参数都是一个寄存器缩写、描述性名称及其关联的位字段。
// 描述性名称定义了这组位字段。
// 只有定义为ReadWrite<_,Control::Register>的寄存器才能使用这些位字段。
Control [
// Bitfields are defined as:
// 字段定义如下
// name OFFSET(shift) NUMBITS(num) [ /* optional values */ ]
// This is a two-bit field which includes bits 4 and 5
RANGE OFFSET(4) NUMBITS(2) [
// Each of these defines a name for a value that the bitfield can be written with or matched against. Note that this set is not exclusive-- the field can still be written with arbitrary constants.
// 其中每一项都定义了一个值的名称,该值可以写入或匹配位字段。
// 请注意,这个集合不是排他性的——字段仍然可以用任意常量写入
VeryHigh = 0,
High = 1,
Low = 2
],
// A common case is single-bit bitfields, which usually just mean'enable' or 'disable' something.
// 单字段,仅意味着enable/disable
EN OFFSET(3) NUMBITS(1) [],
INT OFFSET(2) NUMBITS(1) []
],
// Another example:
// Status register
Status [
TXCOMPLETE OFFSET(0) NUMBITS(1) [],
TXINTERRUPT OFFSET(1) NUMBITS(1) [],
RXCOMPLETE OFFSET(2) NUMBITS(1) [],
RXINTERRUPT OFFSET(3) NUMBITS(1) [],
MODE OFFSET(4) NUMBITS(3) [
FullDuplex = 0,
HalfDuplex = 1,
Loopback = 2,
Disabled = 3
],
ERRORCOUNT OFFSET(6) NUMBITS(3) []
],
// In a simple case, offset can just be a number, and the number of bits is set to 1:
// 在一个简单的情况下,偏移量可以只是一个数字,并且位数设置为1:
InterruptFlags [
UNDES 10,
TXEMPTY 9,
NSSR 8,
OVRES 3,
MODF 2,
TDRE 1,
RDRF 0
]
]
寄存器接口
寄存器接口提供了四种类型: ReadOnly、 WriteOnly、 ReadWrite 和 Aliased。他们分别通过 Readable、 writable 和 ReadWriteable traits 的实现公开了以下方法:
ReadOnly<T: UIntLike, R: RegisterLongName = ()>: Readable
.get() -> T // Get the raw register value
.read(field: Field<T, R>) -> T // Read the value of the given field
.read_as_enum<E>(field: Field<T, R>) -> Option<E> // Read value of the given field as a enum member
.is_set(field: Field<T, R>) -> bool // Check if one or more bits in a field are set
.matches_any(value: FieldValue<T, R>) -> bool // Check if any specified parts of a field match
.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match
.extract() -> LocalRegisterCopy<T, R> // Make local copy of register
WriteOnly<T: UIntLike, R: RegisterLongName = ()>: Writeable
.set(value: T) // Set the raw register value
.write(value: FieldValue<T, R>) // Write the value of one or more fields,
// overwriting other fields to zero
ReadWrite<T: UIntLike, R: RegisterLongName = ()>: Readable + Writeable + ReadWriteable
.get() -> T // Get the raw register value
.set(value: T) // Set the raw register value
.read(field: Field<T, R>) -> T // Read the value of the given field
.read_as_enum<E>(field: Field<T, R>) -> Option<E> // Read value of the given field as a enum member
.write(value: FieldValue<T, R>) // Write the value of one or more fields,
// overwriting other fields to zero
.modify(value: FieldValue<T, R>) // Write the value of one or more fields,
// leaving other fields unchanged
.modify_no_read( // Write the value of one or more fields,
original: LocalRegisterCopy<T, R>, // leaving other fields unchanged, but pass in
value: FieldValue<T, R>) // the original value, instead of doing a register read
.is_set(field: Field<T, R>) -> bool // Check if one or more bits in a field are set
.matches_any(value: FieldValue<T, R>) -> bool // Check if any specified parts of a field match
.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match
.extract() -> LocalRegisterCopy<T, R> // Make local copy of register
Aliased<T: UIntLike, R: RegisterLongName = (), W: RegisterLongName = ()>: Readable + Writeable
.get() -> T // Get the raw register value
.set(value: T) // Set the raw register value
.read(field: Field<T, R>) -> T // Read the value of the given field
.read_as_enum<E>(field: Field<T, R>) -> Option<E> // Read value of the given field as a enum member
.write(value: FieldValue<T, W>) // Write the value of one or more fields,
// overwriting other fields to zero
.is_set(field: Field<T, R>) -> bool // Check if one or more bits in a field are set
.matches_any(value: FieldValue<T, R>) -> bool // Check if any specified parts of a field match
.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match
.extract() -> LocalRegisterCopy<T, R> // Make local copy of register
- Aliased 类型表示具有不同含义的只读和只写寄存器别名到同一内存位置的情况
- 第一个类型参数(UIntLike 类型)是 u8、 u16、 u32、 u64、 u128或 usize
命名约定
use tock_registers::registers::ReadWrite;
#[repr(C)]
struct Registers {
// The register name in the struct should be a lowercase version of the register abbreviation, as written in the datasheet:
// 结构中的寄存器变量名称应为数据表中所述的寄存器缩写的小写版本
cr: ReadWrite<u8, Control::Register>,
}
register_bitfields! [
u8,
// The name should be the long descriptive register name, camelcase, without the word 'register'.
// 寄存器名(驼峰),该名称应为长描述性寄存器名称,不含“寄存器”一词。
Control [
// The field name should be the capitalized abbreviated field name, as given in the datasheet.
// 字段名(大写)应为数据表中给出的大写缩写字段名
RANGE OFFSET(4) NUMBITS(3) [
// Each of the field values should be camelcase, as descriptive of their value as possible.
//每个字段值都应该是驼峰,尽可能描述它们的值
VeryHigh = 0,
High = 1,
Low = 2
]
]
]
对于 API 实现 Readable:: get 和 writable:: set 方法就足够了,其余部分方法可以由 crate 提供的 traits 自动实现
Tock OS UART
pub struct Uart<'a> {
// 指向静态分配的可变数据(如内存映射的I/O寄存器)的指针。
registers: StaticRef<UartRegisters>,
clock_frequency: u32,
tx_client: OptionalCell<&'a dyn hil::uart::TransmitClient>,
rx_client: OptionalCell<&'a dyn hil::uart::ReceiveClient>,
stop_bits: Cell<hil::uart::StopBits>,
buffer: TakeCell<'static, [u8]>,
len: Cell<usize>,
index: Cell<usize>,
}
UartRegisters
SiFive FE310-G002 内核中 UART 寄存器内容如下 :
Uart 接口
接口序号 | 地址 | div_width | div_init | TxFIFO dep | RX FIFO dep |
---|---|---|---|---|---|
0 | 0x10013000 | 16 | 3 | 8 | 8 |
1 | 0x10023000 | 16 | 3 | 8 | 8 |
地址映射
UART内存映射被设计为只需要自然对齐的32位内存访问
register_structs! {
UartRegisters {
// 偏移 寄存器名(小写) 访问方式 寄存器大小 寄存器类型
(0x00 => txdata: ReadWrite<u32, TxData::Register>),
(0x04 => rxdata: ReadWrite<u32, RxData::Register>),
(0x08 => txctrl: ReadWrite<u32, TxCtrl::Register>),
(0x0c => rxctrl: ReadWrite<u32, RxCtrl::Register>),
(0x10 => ie: ReadWrite<u32, Ie::Register>),
(0x14 => ip: ReadOnly<u32, Ip::Register>),
(0x18 => div: ReadWrite<u32, Div::Register>),
}
}
register_bitfields! [u32,
// 每个后续参数都是一个寄存器缩写、描述性名称及其关联的位字段。
// 描述性名称定义了这组位字段。
// 只有定义为ReadWrite<_,Control::Register>的寄存器才能使用这些位字段。
TxData [
// 字段定义如下
// name OFFSET(shift) NUMBITS(num) [ /* optional values */ ]
DATA OFFSET(0) NUMBITS(8) [],
FULL OFFSET(31) NUMBITS(1) [],
],
RxData [
data OFFSET(0) NUMBITS(8) [],
empty OFFSET(31) NUMBITS(1) [],
],
TxCtrl [
txen OFFSET(0) NUMBITS(1) [],
nstop OFFSET(0) NUMBITS(1) [
OneStopBit = 0,
TwoStopBit = 1,
],
txcnt OFFSET(16) NUMBITS(3) [],
empty OFFSET(31) NUMBITS(1) [],
],
....
]
上面是将UART的寄存器用rust语言描述好了,那么TockOS中的UART类型如下:
pub struct Uart<'a> {
registers: StaticRef<UartRegisters>,
clock_frequency: u32,
tx_client: OptionalCell<&'a dyn hil::uart::TransmitClient>,
rx_client: OptionalCell<&'a dyn hil::uart::ReceiveClient>,
stop_bits: Cell<hil::uart::StopBits>,
buffer: TakeCell<'static, [u8]>,
len: Cell<usize>,
index: Cell<usize>,
}
impl<'a> Uart<'a> {
pub const fn new(base: StaticRef<UartRegisters>, clock_frequency: u32) -> Uart<'a> {
Uart {
registers: base,
clock_frequency: clock_frequency,
tx_client: OptionalCell::empty(),
rx_client: OptionalCell::empty(),
stop_bits: Cell::new(hil::uart::StopBits::One),
buffer: TakeCell::empty(),
len: Cell::new(0),
index: Cell::new(0),
}
}
//...
}
GpioRegister
pub struct GpioRegisters {
/// Pin value.
value: ReadOnly<u32, pins::Register>,
/// Pin Input Enable Register
input_en: ReadWrite<u32, pins::Register>,
/// Pin Output Enable Register
output_en: ReadWrite<u32, pins::Register>,
/// Output Port Value Register
port: ReadWrite<u32, pins::Register>,
/// Internal Pull-Up Enable Register
pullup: ReadWrite<u32, pins::Register>,
/// Drive Strength Register
drive: ReadWrite<u32, pins::Register>,
/// Rise Interrupt Enable Register
rise_ie: ReadWrite<u32, pins::Register>,
/// Rise Interrupt Pending Register
rise_ip: ReadWrite<u32, pins::Register>,
/// Fall Interrupt Enable Register
fall_ie: ReadWrite<u32, pins::Register>,
/// Fall Interrupt Pending Register
fall_ip: ReadWrite<u32, pins::Register>,
/// High Interrupt Enable Register
high_ie: ReadWrite<u32, pins::Register>,
/// High Interrupt Pending Register
high_ip: ReadWrite<u32, pins::Register>,
/// Low Interrupt Enable Register
low_ie: ReadWrite<u32, pins::Register>,
/// Low Interrupt Pending Register
low_ip: ReadWrite<u32, pins::Register>,
/// HW I/O Function Enable Register
iof_en: ReadWrite<u32, pins::Register>,
/// HW I/O Function Select Register
iof_sel: ReadWrite<u32, pins::Register>,
/// Output XOR (invert) Register
out_xor: ReadWrite<u32, pins::Register>,
}