11个版本 (破坏性)
0.9.0 | 2023年9月29日 |
---|---|
0.8.1 | 2022年9月26日 |
0.8.0 | 2022年8月24日 |
0.7.0 | 2021年7月2日 |
0.1.0 | 2018年7月10日 |
#1 在 #bare-metal
每月下载量 21,746
用于 24 个crate (16 个直接)
90KB
1K SLoC
Tock寄存器接口
此crate提供定义和操作寄存器和位字段的接口和类型。
定义寄存器
该crate提供了三种与内存映射寄存器一起工作的类型:ReadWrite
,ReadOnly
和WriteOnly
,分别提供读写、只读和只写功能。这些类型实现了Readable
,Writeable
和ReadWriteable
特质。
使用register_structs
宏定义寄存器,该宏为每个寄存器期望一个偏移量、一个字段名和一个类型。寄存器必须按偏移量递增的顺序连续声明。在定义寄存器时必须显式注释偏移量和间隙标识符(通常使用字段名_reservedN
),但不提供类型。然后宏将自动处理计算间隙大小并插入适当的填充结构。结构体的末尾用其大小和@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).
(0x000 => cr: ReadWrite<u8, Control::Register>),
// Status register: read-only
(0x001 => s: ReadOnly<u8, Status::Register>),
// Registers can be bytes, halfwords, or words:
// Note that the second type parameter can be omitted, meaning that there
// are no bitfields defined for these registers.
(0x002 => byte0: ReadWrite<u8>),
(0x003 => byte1: ReadWrite<u8>),
(0x004 => short: ReadWrite<u16>),
// Empty space between registers must be marked with a padding field,
// declared as follows. The length of this padding is automatically
// computed by the macro.
(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 => ... ),
// Etc.
// The end of the struct is marked as follows.
(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).
cr: ReadWrite<u8, Control::Register>,
// Status register: read-only
s: ReadOnly<u8, Status::Register>
// Registers can be bytes, halfwords, or words:
// Note that the second type parameter can be omitted, meaning that there
// are no bitfields defined for these registers.
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.
}
此crate将在编译时生成额外的断言来验证寄存器结构体的各种不变量,例如
- 填充字段的正确起始偏移量,
- 实际字段的正确起始和结束偏移量,
- 字段类型的无效对齐,
- 匹配结构体大小的
@END
标记。
有关生成的断言的更多信息,请查看 test_fields!
宏文档。
默认情况下,生成的结构和字段的可见性是私有的。您可以使用 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.
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.
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.
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:
InterruptFlags [
UNDES 10,
TXEMPTY 9,
NSSR 8,
OVRES 3,
MODF 2,
TDRE 1,
RDRF 0
]
]
寄存器接口摘要
寄存器接口提供了四种类型:ReadOnly
、WriteOnly
、ReadWrite
和 Aliased
。它们通过 Readable
、Writeable
和 ReadWriteable
特性的实现暴露以下方法
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
.any_matching_bits_set(value: FieldValue<T, R>) -> bool // Check if any bits corresponding to the mask in the passed field are set
.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match
.matches_any(&self, fields: &[FieldValue<T, R>]) -> bool // Check if any 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
.any_matching_bits_set(value: FieldValue<T, R>) -> bool // Check if any bits corresponding to the mask in the passed field are set
.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match
.matches_any(&self, fields: &[FieldValue<T, R>]) -> bool // Check if any 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
.any_matching_bits_set(value: FieldValue<T, R>) -> bool // Check if any bits corresponding to the mask in the passed field are set
.matches_all(value: FieldValue<T, R>) -> bool // Check if all specified parts of a field match
.matches_any(&self, fields: &[FieldValue<T, R>]) -> bool // Check if any specified parts of a field match
.extract() -> LocalRegisterCopy<T, R> // Make local copy of register
Aliased
类型表示只读和只写寄存器,具有不同的含义,但被别名到相同的内存位置的情况。
第一个类型参数(UIntLike
类型)是 u8
、u16
、u32
、u64
、u128
或 usize
。
示例:使用寄存器和位域
假设我们已经定义了一个 Registers
结构体和相应的位域,如前两节所述。我们还有一个对 Registers
结构体的不可变引用,名为 registers
。
// -----------------------------------------------------------------------------
// RAW ACCESS
// -----------------------------------------------------------------------------
// Get or set the raw value of the register directly. Nothing fancy:
registers.cr.set(registers.cr.get() + 1);
// -----------------------------------------------------------------------------
// READ
// -----------------------------------------------------------------------------
// `range` will contain the value of the RANGE field, e.g. 0, 1, 2, or 3.
// The type annotation is not necessary, but provided for clarity here.
let range: u8 = registers.cr.read(Control::RANGE);
// Or one can read `range` as a enum and `match` over it.
let range = registers.cr.read_as_enum(Control::RANGE);
match range {
Some(Control::RANGE::Value::VeryHigh) => { /* ... */ }
Some(Control::RANGE::Value::High) => { /* ... */ }
Some(Control::RANGE::Value::Low) => { /* ... */ }
None => unreachable!("invalid value")
}
// `en` will be 0 or 1
let en: u8 = registers.cr.read(Control::EN);
// -----------------------------------------------------------------------------
// MODIFY
// -----------------------------------------------------------------------------
// Write a value to a bitfield without altering the values in other fields:
registers.cr.modify(Control::RANGE.val(2)); // Leaves EN, INT unchanged
// Named constants can be used instead of the raw values:
registers.cr.modify(Control::RANGE::VeryHigh);
// Enum values can also be used:
registers.cr.modify(Control::RANGE::Value::VeryHigh.into())
// Another example of writing a field with a raw value:
registers.cr.modify(Control::EN.val(0)); // Leaves RANGE, INT unchanged
// For one-bit fields, the named values SET and CLEAR are automatically
// defined:
registers.cr.modify(Control::EN::SET);
// Write multiple values at once, without altering other fields:
registers.cr.modify(Control::EN::CLEAR + Control::RANGE::Low); // INT unchanged
// Any number of non-overlapping fields can be combined:
registers.cr.modify(Control::EN::CLEAR + Control::RANGE::High + CR::INT::SET);
// In some cases (such as a protected register) .modify() may not be appropriate.
// To enable updating a register without coupling the read and write, use
// modify_no_read():
let original = registers.cr.extract();
registers.cr.modify_no_read(original, Control::EN::CLEAR);
// -----------------------------------------------------------------------------
// WRITE
// -----------------------------------------------------------------------------
// Same interface as modify, except that all unspecified fields are overwritten to zero.
registers.cr.write(Control::RANGE.val(1)); // implictly sets all other bits to zero
// -----------------------------------------------------------------------------
// BITFLAGS
// -----------------------------------------------------------------------------
// For one-bit fields, easily check if they are set or clear:
let txcomplete: bool = registers.s.is_set(Status::TXCOMPLETE);
// -----------------------------------------------------------------------------
// MATCHING
// -----------------------------------------------------------------------------
// You can also query a specific register state easily with `matches_all` or
// `any_matching_bits_set` or `matches_any`:
// Doesn't care about the state of any field except TXCOMPLETE and MODE:
let ready: bool = registers.s.matches_all(Status::TXCOMPLETE:SET +
Status::MODE::FullDuplex);
// This is very useful for awaiting for a specific condition:
while !registers.s.matches_all(Status::TXCOMPLETE::SET +
Status::RXCOMPLETE::SET +
Status::TXINTERRUPT::CLEAR) {}
// Or for checking whether any interrupts are enabled:
let any_ints = registers.s.any_matching_bits_set(Status::TXINTERRUPT + Status::RXINTERRUPT);
// Or for checking whether any completion states are cleared:
let any_cleared = registers.s.matches_any(&[Status::TXCOMPLETE::CLEAR, Status::RXCOMPLETE::CLEAR]);
// Or for checking if a multi-bit field matches one of several modes:
let sub_word_size = registers.s.matches_any(&[Size::Halfword, Size::Word]);
// Or for checking if any of several fields exactly match in the register:
let not_supported_mode = registers.s.matches_any(&[Status::Mode::HalfDuplex, Status::Mode::VARSYNC, Status::MODE::NOPARITY]);
// Also you can read a register with set of enumerated values as a enum and `match` over it:
let mode = registers.cr.read_as_enum(Status::MODE);
match mode {
Some(Status::MODE::Value::FullDuplex) => { /* ... */ }
Some(Status::MODE::Value::HalfDuplex) => { /* ... */ }
None => unreachable!("invalid value")
}
// -----------------------------------------------------------------------------
// LOCAL COPY
// -----------------------------------------------------------------------------
// More complex code may want to read a register value once and then keep it in
// a local variable before using the normal register interface functions on the
// local copy.
// Create a copy of the register value as a local variable.
let local = registers.cr.extract();
// Now all the functions for a ReadOnly register work.
let txcomplete: bool = local.is_set(Status::TXCOMPLETE);
// -----------------------------------------------------------------------------
// In-Memory Registers
// -----------------------------------------------------------------------------
// In some cases, code may want to edit a memory location with all of the
// register features described above, but the actual memory location is not a
// fixed MMIO register but instead an arbitrary location in memory. If this
// location is then shared with the hardware (i.e. via DMA) then the code
// must do volatile reads and writes since the value may change without the
// software knowing. To support this, the library includes an `InMemoryRegister`
// type.
let control: InMemoryRegister<u32, Control::Register> = InMemoryRegister::new(0)
control.write(Contol::BYTE_COUNT.val(0) +
Contol::ENABLE::Yes +
Contol::LENGTH.val(10));
请注意,modify
执行正好一次易失性加载和一次易失性存储,write
执行正好一次易失性存储,而 read
执行正好一次易失性加载。因此,您将确保单次调用同时设置或查询所有字段。
性能
在测试此接口时检查二进制文件,所有内容都编译为最优的内联位操作指令——换句话说,根据非正式的初步研究,没有运行时成本。
良好的类型检查
此接口通过类型检查帮助编译器捕获一些常见的错误类型。
如果您为例如控制寄存器定义了位域,您可以给它们一个描述性的组名,如 Control
。此位域组将仅与类型为 ReadWrite<_, Control>
(或 ReadOnly /
WriteOnly
等)的寄存器一起使用。例如,如果我们有如上定义的位域和寄存器
// This line compiles, because registers.cr is associated with the Control group
// of bitfields.
registers.cr.modify(Control::RANGE.val(1));
// This line will not compile, because registers.s is associated with the Status
// group, not the Control group.
let range = registers.s.read(Control::RANGE);
命名约定
在寄存器定义中有几个相关名称。以下是对每个名称的命名约定的描述
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
]
]
]
实现自定义寄存器类型
《可读》、《可写》和《可读写》特性使得可以在本包之外实现自定义寄存器类型。这种特性特别适用于CPU寄存器,例如ARM SPSRs或RISC-V CSRs。只需实现API中剩余的Readable::get
和Writeable::set
方法,其他API部分将由包提供的特性自动实现。有关此功能的更详细文档,请参阅接口模块文档。