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

Download history · Rust 包仓库 4666/week @ 2024-04-20 · Rust 包仓库 4393/week @ 2024-04-27 · Rust 包仓库 4081/week @ 2024-05-04 · Rust 包仓库 3827/week @ 2024-05-11 · Rust 包仓库 4170/week @ 2024-05-18 · Rust 包仓库 4854/week @ 2024-05-25 · Rust 包仓库 4277/week @ 2024-06-01 · Rust 包仓库 4072/week @ 2024-06-08 · Rust 包仓库 4672/week @ 2024-06-15 · Rust 包仓库 5130/week @ 2024-06-22 · Rust 包仓库 4193/week @ 2024-06-29 · Rust 包仓库 7397/week @ 2024-07-06 · Rust 包仓库 7103/week @ 2024-07-13 · Rust 包仓库 6032/week @ 2024-07-20 · Rust 包仓库 3802/week @ 2024-07-27 · Rust 包仓库 3677/week @ 2024-08-03 · Rust 包仓库

每月下载量 21,746
用于 24 个crate (16 个直接)

MIT/Apache

90KB
1K SLoC

Tock寄存器接口

此crate提供定义和操作寄存器和位字段的接口和类型。

定义寄存器

该crate提供了三种与内存映射寄存器一起工作的类型:ReadWriteReadOnlyWriteOnly,分别提供读写、只读和只写功能。这些类型实现了ReadableWriteableReadWriteable特质。

使用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
    ]
]

寄存器接口摘要

寄存器接口提供了四种类型:ReadOnlyWriteOnlyReadWriteAliased。它们通过 ReadableWriteableReadWriteable 特性的实现暴露以下方法

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 类型)是 u8u16u32u64u128usize

示例:使用寄存器和位域

假设我们已经定义了一个 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::getWriteable::set方法,其他API部分将由包提供的特性自动实现。有关此功能的更详细文档,请参阅接口模块文档

无运行时依赖

功能