日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
C還是Rust:選擇哪個(gè)用于硬件抽象編程

在 Rust 中使用類型級(jí)編程可以使硬件抽象更加安全。

創(chuàng)新互聯(lián)長(zhǎng)期為近1000家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為武強(qiáng)企業(yè)提供專業(yè)的成都做網(wǎng)站、網(wǎng)站建設(shè)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè),武強(qiáng)網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

Rust 是一種日益流行的編程語言,被視為硬件接口的最佳選擇。通常會(huì)將其與 C 的抽象級(jí)別相比較。本文介紹了 Rust 如何通過多種方式處理按位運(yùn)算,并提供了既安全又易于使用的解決方案。 

< 如顯示不全,請(qǐng)左右滑動(dòng) >

語言 誕生于 官方描述 總覽
C1972 年C 是一種通用編程語言,具有表達(dá)式簡(jiǎn)約、現(xiàn)代的控制流和數(shù)據(jù)結(jié)構(gòu),以及豐富的運(yùn)算符集等特點(diǎn)。(來源:CS 基礎(chǔ)知識(shí))C 是(一種)命令式語言,旨在以相對(duì)簡(jiǎn)單的方式進(jìn)行編譯,從而提供對(duì)內(nèi)存的低級(jí)訪問。(來源:W3schools.in)
Rust2010 年一種賦予所有人構(gòu)建可靠、高效的軟件的能力的語言(來源:Rust 網(wǎng)站)Rust 是一種專注于安全性(尤其是安全并發(fā)性)的多范式系統(tǒng)編程語言。(來源:維基百科)

在 C 語言中對(duì)寄存器值進(jìn)行按位運(yùn)算

在系統(tǒng)編程領(lǐng)域,你可能經(jīng)常需要編寫硬件驅(qū)動(dòng)程序或直接與內(nèi)存映射設(shè)備進(jìn)行交互,而這些交互幾乎總是通過硬件提供的內(nèi)存映射寄存器來完成的。通常,你通過對(duì)某些固定寬度的數(shù)字類型進(jìn)行按位運(yùn)算來與這些寄存器進(jìn)行交互。

例如,假設(shè)一個(gè) 8 位寄存器具有三個(gè)字段:

 
 
 
  1. +----------+------+-----------+---------+
  2. | (unused) | Kind | Interrupt | Enabled |
  3. +----------+------+-----------+---------+
  4.    5-7       2-4        1          0

字段名稱下方的數(shù)字規(guī)定了該字段在寄存器中使用的位。要啟用該寄存器,你將寫入值 1(以二進(jìn)制表示為 0000_0001)來設(shè)置 Enabled 字段的位。但是,通常情況下,你也不想干擾寄存器中的現(xiàn)有配置。假設(shè)你要在設(shè)備上啟用中斷功能,但也要確保設(shè)備保持啟用狀態(tài)。為此,必須將 Interrupt 字段的值與 Enabled 字段的值結(jié)合起來。你可以通過按位操作來做到這一點(diǎn):

 
 
 
  1. 1 | (1 << 1)

通過將 1 和 2(1 左移一位得到)進(jìn)行“或”(|)運(yùn)算得到二進(jìn)制值 0000_0011 。你可以將其寫入寄存器,使其保持啟用狀態(tài),但也啟用中斷功能。

你的頭腦中要記住很多事情,特別是當(dāng)你要在一個(gè)完整的系統(tǒng)上和可能有數(shù)百個(gè)之多的寄存器打交道時(shí)。在實(shí)踐上,你可以使用助記符來執(zhí)行此操作,助記符可跟蹤字段在寄存器中的位置以及字段的寬度(即它的上邊界是什么)

下面是這些助記符之一的示例。它們是 C 語言的宏,用右側(cè)的代碼替換它們的出現(xiàn)的地方。這是上面列出的寄存器的簡(jiǎn)寫。 的左側(cè)是該字段的起始位置,而右側(cè)則限制該字段所占的位:

 
 
 
  1. #define REG_ENABLED_FIELD(x) (x << 0) & 1
  2. #define REG_INTERRUPT_FIELD(x) (x << 1) & 2
  3. #define REG_KIND_FIELD(x) (x << 2) & (7 << 2)

然后,你可以使用這些來抽象化寄存器值的操作,如下所示:

 
 
 
  1. void set_reg_val(reg* u8, val u8);
  2.  
  3. fn enable_reg_with_interrupt(reg* u8) {
  4.     set_reg_val(reg, REG_ENABLED_FIELD(1) | REG_INTERRUPT_FIELD(1));
  5. }

這就是現(xiàn)在的做法。實(shí)際上,這就是大多數(shù)驅(qū)動(dòng)程序在 Linux 內(nèi)核中的使用方式。

有沒有更好的辦法?如果能夠基于對(duì)現(xiàn)代編程語言研究得出新的類型系統(tǒng),就可能能夠獲得安全性和可表達(dá)性的好處。也就是說,如何使用更豐富、更具表現(xiàn)力的類型系統(tǒng)來使此過程更安全、更持久?

在 Rust 語言中對(duì)寄存器值進(jìn)行按位運(yùn)算

繼續(xù)用上面的寄存器作為例子:

 
 
 
  1. +----------+------+-----------+---------+
  2. | (unused) | Kind | Interrupt | Enabled |
  3. +----------+------+-----------+---------+
  4.    5-7       2-4        1          0

你想如何用 Rust 類型來表示它呢?

你將以類似的方式開始,為每個(gè)字段的偏移定義常量(即,距最低有效位有多遠(yuǎn))及其掩碼。掩碼是一個(gè)值,其二進(jìn)制表示形式可用于更新或讀取寄存器內(nèi)部的字段:

 
 
 
  1. const ENABLED_MASK: u8 = 1;
  2. const ENABLED_OFFSET: u8 = 0;
  3.  
  4. const INTERRUPT_MASK: u8 = 2;
  5. const INTERRUPT_OFFSET: u8 = 1;
  6.  
  7. const KIND_MASK: u8 = 7 << 2;
  8. const KIND_OFFSET: u8 = 2;

接下來,你將聲明一個(gè) Field 類型并進(jìn)行操作,將給定值轉(zhuǎn)換為與其位置相關(guān)的值,以供在寄存器內(nèi)使用:

 
 
 
  1. struct Field {
  2. value: u8,
  3. }
  4.  
  5. impl Field {
  6. fn new(mask: u8, offset: u8, val: u8) -> Self {
  7. Field {
  8. value: (val << offset) & mask,
  9. }
  10. }
  11. }

最后,你將使用一個(gè) Register 類型,該類型會(huì)封裝一個(gè)與你的寄存器寬度匹配的數(shù)字類型。 Register 具有 update 函數(shù),可使用給定字段來更新寄存器:

 
 
 
  1. struct Register(u8);
  2.  
  3. impl Register {
  4. fn update(&mut self, val: Field) {
  5. self.0 = self.0 | field.value;
  6. }
  7. }
  8.  
  9. fn enable_register(&mut reg) {
  10. reg.update(Field::new(ENABLED_MASK, ENABLED_OFFSET, 1));
  11. }

使用 Rust,你可以使用數(shù)據(jù)結(jié)構(gòu)來表示字段,將它們與特定的寄存器聯(lián)系起來,并在與硬件交互時(shí)提供簡(jiǎn)潔明了的工效。這個(gè)例子使用了 Rust 提供的最基本的功能。無論如何,添加的結(jié)構(gòu)都會(huì)減輕上述 C 示例中的某些晦澀的地方?,F(xiàn)在,字段是個(gè)帶有名字的事物,而不是從模糊的按位運(yùn)算符派生而來的數(shù)字,并且寄存器是具有狀態(tài)的類型 —— 這在硬件上多了一層抽象。

一個(gè)易用的 Rust 實(shí)現(xiàn)

用 Rust 重寫的第一個(gè)版本很好,但是并不理想。你必須記住要帶上掩碼和偏移量,并且要手工進(jìn)行臨時(shí)計(jì)算,這容易出錯(cuò)。人類不擅長(zhǎng)精確且重復(fù)的任務(wù) —— 我們往往會(huì)感到疲勞或失去專注力,這會(huì)導(dǎo)致錯(cuò)誤。一次一個(gè)寄存器地手動(dòng)記錄掩碼和偏移量幾乎可以肯定會(huì)以糟糕的結(jié)局而告終。這是最好留給機(jī)器的任務(wù)。

其次,從結(jié)構(gòu)上進(jìn)行思考:如果有一種方法可以讓字段的類型攜帶掩碼和偏移信息呢?如果可以在編譯時(shí)就發(fā)現(xiàn)硬件寄存器的訪問和交互的實(shí)現(xiàn)代碼中存在錯(cuò)誤,而不是在運(yùn)行時(shí)才發(fā)現(xiàn),該怎么辦?也許你可以依靠一種在編譯時(shí)解決問題的常用策略,例如類型。

你可以使用 typenum 來修改前面的示例,該庫(kù)在類型級(jí)別提供數(shù)字和算術(shù)。在這里,你將使用掩碼和偏移量對(duì) Field 類型進(jìn)行參數(shù)化,使其可用于任何 Field 實(shí)例,而無需將其包括在調(diào)用處:

 
 
 
  1. #[macro_use]
  2. extern crate typenum;
  3.  
  4. use core::marker::PhantomData;
  5.  
  6. use typenum::*;
  7.  
  8. // Now we'll add Mask and Offset to Field's type
  9. struct Field {
  10. value: u8,
  11. _mask: PhantomData,
  12. _offset: PhantomData,
  13. }
  14.  
  15. // We can use type aliases to give meaningful names to
  16. // our fields (and not have to remember their offsets and masks).
  17. type RegEnabled = Field;
  18. type RegInterrupt = Field;
  19. type RegKind = Field;

現(xiàn)在,當(dāng)重新訪問 Field 的構(gòu)造函數(shù)時(shí),你可以忽略掩碼和偏移量參數(shù),因?yàn)轭愋椭邪撔畔ⅲ?/p>

 
 
 
  1. impl Field {
  2. fn new(val: u8) -> Self {
  3. Field {
  4. value: (val << Offset::U8) & Mask::U8,
  5. _mask: PhantomData,
  6. _offset: PhantomData,
  7. }
  8. }
  9. }
  10.  
  11. // And to enable our register...
  12. fn enable_register(&mut reg) {
  13. reg.update(RegEnabled::new(1));
  14. }

看起來不錯(cuò),但是……如果你在給定的值是否適合該字段方面犯了錯(cuò)誤,會(huì)發(fā)生什么?考慮一個(gè)簡(jiǎn)單的輸入錯(cuò)誤,你在其中放置了 10 而不是 1

 
 
 
  1. fn enable_register(&mut reg) {
  2.     reg.update(RegEnabled::new(10));
  3. }

在上面的代碼中,預(yù)期結(jié)果是什么?好吧,代碼會(huì)將啟用位設(shè)置為 0,因?yàn)?10&1 = 0。那真不幸;最好在嘗試寫入之前知道你要寫入字段的值是否適合該字段。事實(shí)上,我認(rèn)為截掉錯(cuò)誤字段值的高位是一種 1未定義的行為(哈)。

出于安全考慮使用 Rust

如何以一般方式檢查字段的值是否適合其規(guī)定的位置?需要更多類型級(jí)別的數(shù)字!

你可以在 Field 中添加 Width 參數(shù),并使用它來驗(yàn)證給定的值是否適合該字段:

 
 
 
  1. struct Field {
  2. value: u8,
  3. _mask: PhantomData,
  4. _offset: PhantomData,
  5. _width: PhantomData,
  6. }
  7.  
  8. type RegEnabled = Field;
  9. type RegInterrupt = Field;
  10. type RegKind = Field;
  11.  
  12. impl Field {
  13. fn new(val: u8) -> Option {
  14. if val <= (1 << Width::U8) - 1 {
  15. Some(Field {
  16. value: (val << Offset::U8) & Mask::U8,
  17. _mask: PhantomData,
  18. _offset: PhantomData,
  19. _width: PhantomData,
  20. })
  21. } else {
  22. None
  23. }
  24. }
  25. }

現(xiàn)在,只有給定值適合時(shí),你才能構(gòu)造一個(gè) Field !否則,你將得到 None 信號(hào),該信號(hào)指示發(fā)生了錯(cuò)誤,而不是截掉該值的高位并靜默寫入意外的值。

但是請(qǐng)注意,這將在運(yùn)行時(shí)環(huán)境中引發(fā)錯(cuò)誤。但是,我們事先知道我們想寫入的值,還記得嗎?鑒于此,我們可以教編譯器完全拒絕具有無效字段值的程序 —— 我們不必等到運(yùn)行它!

這次,你將向 new 的新實(shí)現(xiàn) new_checked 中添加一個(gè)特征綁定(where 子句),該函數(shù)要求輸入值小于或等于給定字段用 Width 所能容納的最大可能值:

 
 
 
  1. struct Field {
  2. value: u8,
  3. _mask: PhantomData,
  4. _offset: PhantomData,
  5. _width: PhantomData,
  6. }
  7.  
  8. type RegEnabled = Field;
  9. type RegInterrupt = Field;
  10. type RegKind = Field;
  11.  
  12. impl Field {
  13. const fn new_checked() -> Self
  14. where
  15. V: IsLessOrEqual,
  16. {
  17. Field {
  18. value: (V::U8 << Offset::U8) & Mask::U8,
  19. _mask: PhantomData,
  20. _offset: PhantomData,
  21. _width: PhantomData,
  22. }
  23. }
  24. }

只有擁有此屬性的數(shù)字才實(shí)現(xiàn)此特征,因此,如果使用不適合的數(shù)字,它將無法編譯。讓我們看一看!

 
 
 
  1. fn enable_register(&mut reg) {
  2. reg.update(RegEnabled::new_checked::());
  3. }
  4. 12 | reg.update(RegEnabled::new_checked::());
  5. | ^^^^^^^^^^^^^^^^ expected struct `typenum::B0`, found struct `typenum::B1`
  6. |
  7. = note: expected type `typenum::B0`
  8. found type `typenum::B1`

new_checked 將無法生成一個(gè)程序,因?yàn)樵撟侄蔚闹涤绣e(cuò)誤的高位。你的輸入錯(cuò)誤不會(huì)在運(yùn)行時(shí)環(huán)境中才爆炸,因?yàn)槟阌肋h(yuǎn)無法獲得一個(gè)可以運(yùn)行的工件。

就使內(nèi)存映射的硬件進(jìn)行交互的安全性而言,你已經(jīng)接近 Rust 的極致。但是,你在 C 的第一個(gè)示例中所寫的內(nèi)容比最終得到的一鍋粥的類型參數(shù)更簡(jiǎn)潔。當(dāng)你談?wù)摑撛诳赡苡袛?shù)百甚至數(shù)千個(gè)寄存器時(shí),這樣做是否容易處理?

讓 Rust 恰到好處:既安全又方便使用

早些時(shí)候,我認(rèn)為手工計(jì)算掩碼有問題,但我又做了同樣有問題的事情 —— 盡管是在類型級(jí)別。雖然使用這種方法很不錯(cuò),但要達(dá)到編寫任何代碼的地步,則需要大量樣板和手動(dòng)轉(zhuǎn)錄(我在這里談?wù)摰氖穷愋偷耐x詞)。

我們的團(tuán)隊(duì)想要像 TockOS mmio 寄存器之類的東西,而以最少的手動(dòng)轉(zhuǎn)錄生成類型安全的實(shí)現(xiàn)。我們得出的結(jié)果是一個(gè)宏,該宏生成必要的樣板以獲得類似 Tock 的 API 以及基于類型的邊界檢查。要使用它,請(qǐng)寫下一些有關(guān)寄存器的信息,其字段、寬度和偏移量以及可選的枚舉類的值(你應(yīng)該為字段可能具有的值賦予“含義”):

 
 
 
  1. register! {
  2. // The register's name
  3. Status,
  4. // The type which represents the whole register.
  5. u8,
  6. // The register's mode, ReadOnly, ReadWrite, or WriteOnly.
  7. RW,
  8. // And the fields in this register.
  9. Fields [
  10. On WIDTH(U1) OFFSET(U0),
  11. Dead WIDTH(U1) OFFSET(U1),
  12. Color WIDTH(U3) OFFSET(U2) [
  13. Red = U1,
  14. Blue = U2,
  15. Green = U3,
  16. Yellow = U4
  17. ]
  18. ]
  19. }

由此,你可以生成寄存器和字段類型,如上例所示,其中索引:WidthMaskOffset 是從一個(gè)字段定義的 WIDTHOFFSET 部分的輸入值派生的。另外,請(qǐng)注意,所有這些數(shù)字都是 “類型數(shù)字”;它們將直接進(jìn)入你的 Field 定義!

生成的代碼通過為寄存器及字段指定名稱來為寄存器及其相關(guān)字段提供名稱空間。這很繞口,看起來是這樣的:

 
 
 
  1. mod Status {
  2. struct Register(u8);
  3. mod On {
  4. struct Field; // There is of course more to this definition
  5. }
  6. mod Dead {
  7. struct Field;
  8. }
  9. mod Color {
  10. struct Field;
  11. pub const Red: Field = Field::new();
  12. // &c.
  13. }
  14. }

生成的 API 包含名義上期望的讀取和寫入的原語,以獲取原始寄存器的值,但它也有辦法獲取單個(gè)字段的值、執(zhí)行集合操作以及確定是否設(shè)置了任何(或全部)位集合的方法。你可以閱讀完整生成的 API上的文檔。

粗略檢查

將這些定義用于實(shí)際設(shè)備會(huì)是什么樣?代碼中是否會(huì)充斥著類型參數(shù),從而掩蓋了視圖中的實(shí)際邏輯?

不會(huì)!通過使用類型同義詞和類型推斷,你實(shí)際上根本不必考慮程序的類型層面部分。你可以直接與硬件交互,并自動(dòng)獲得與邊界相關(guān)的保證。

這是一個(gè) UART 寄存器塊的示例。我會(huì)跳過寄存器本身的聲明,因?yàn)榘ㄔ谶@里就太多了。而是從寄存器“塊”開始,然后幫助編譯器知道如何從指向該塊開頭的指針中查找寄存器。我們通過實(shí)現(xiàn) DerefDerefMut 來做到這一點(diǎn):

 
 
 
  1. #[repr(C)]
  2. pub struct UartBlock {
  3. rx: UartRX::Register,
  4. _padding1: [u32; 15],
  5. tx: UartTX::Register,
  6. _padding2: [u32; 15],
  7. control1: UartControl1::Register,
  8. }
  9.  
  10. pub struct Regs {
  11. addr: usize,
  12. }
  13.  
  14. impl Deref for Regs {
  15. type Target = UartBlock;
  16.  
  17. fn deref(&self) -> &UartBlock {
  18. unsafe { &*(self.addr as *const UartBlock) }
  19. }
  20. }
  21.  
  22. impl DerefMut for Regs {
  23. fn deref_mut(&mut self) -> &mut UartBlock {
  24. unsafe { &mut *(self.addr as *mut UartBlock) }
  25. }
  26. }

一旦到位,使用這些寄存器就像 read()modify() 一樣簡(jiǎn)單:

 
 
 
  1. fn main() {
  2. // A pretend register block.
  3. let mut x = [0_u32; 33];
  4.  
  5. let mut regs = Regs {
  6. // Some shenanigans to get at `x` as though it were a
  7. // pointer. Normally you'd be given some address like
  8. // `0xDEADBEEF` over which you'd instantiate a `Regs`.
  9. addr: &mut x as *mut [u32; 33] as usize,
  10. };
  11.  
  12. assert_eq!(regs.rx.read(), 0);
  13.  
  14. regs.control1
  15. .modify(UartControl1::Enable::Set + UartControl1::RecvReadyInterrupt::Set);
  16.  
  17. // The first bit and the 10th bit should be set.
  18. assert_eq!(regs.control1.read(), 0b_10_0000_0001);
  19. }

當(dāng)我們使用運(yùn)行時(shí)值時(shí),我們使用如前所述的選項(xiàng)。這里我使用的是 unwrap,但是在一個(gè)輸入未知的真實(shí)程序中,你可能想檢查一下從新調(diào)用中返回的某些東西1 2

 
 
 
  1. fn main() {
  2.     // A pretend register block.
  3.     let mut x = [0_u32; 33];
  4.  
  5.     let mut regs = Regs {
  6.         // Some shenanigans to get at `x` as though it were a
  7.         // pointer. Normally you'd be given some address like
  8.         // `0xDEADBEEF` over which you'd instantiate a `Regs`.
  9.         addr: &mut x as *mut [u32; 33] as usize,
  10.     };
  11.  
  12.     let input = regs.rx.get_field(UartRX::Data::Field::Read).unwrap();
  13.     regs.tx.modify(UartTX::Data::Field::new(input).unwrap());
  14. }

解碼失敗條件

根據(jù)你的個(gè)人痛苦忍耐程度,你可能已經(jīng)注意到這些錯(cuò)誤幾乎是無法理解的??匆幌挛宜f的不那么微妙的提醒:

 
 
 
  1. error[E0271]: type mismatch resolving `, typenum::B0>, typenum::B1>, typenum::B0>, typenum::B0> as typenum::IsLessOrEqual, typenum::B0>, typenum::B1>, typenum::B0>>>::Output == typenum::B1`
  2. --> src/main.rs:12:5
  3. |
  4. 12 | less_than_ten::();
  5. | ^^^^^^^^^^^^^^^^^^^^ expected struct `typenum::B0`, found struct `typenum::B1`
  6. |
  7. = note: expected type `typenum::B0`
  8. found type `typenum::B1`

expected struct typenum::B0, found struct typenum::B1 部分是有意義的,但是 typenum::UInt 到底是什么呢?好吧,typenum 將數(shù)字表示為二進(jìn)制 cons 單元!像這樣的錯(cuò)誤使操作變得很困難,尤其是當(dāng)你將多個(gè)這些類型級(jí)別的數(shù)字限制在狹窄的范圍內(nèi)時(shí),你很難知道它在說哪個(gè)數(shù)字。當(dāng)然,除非你一眼就能將巴洛克式二進(jìn)制表示形式轉(zhuǎn)換為十進(jìn)制表示形式。

在第 U100 次試圖從這個(gè)混亂中破譯出某些含義之后,我們的一個(gè)隊(duì)友簡(jiǎn)直《瘋了,地獄了,不要再忍受了Mad As Hell And Wasn’t Going To Take It Anymore》,并做了一個(gè)小工具 tnfilt,從這種命名空間的二進(jìn)制 cons 單元的痛苦中解脫出來。tnfilt 將 cons 單元格式的表示法替換為可讓人看懂的十進(jìn)制數(shù)字。我們認(rèn)為其他人也會(huì)遇到類似的困難,所以我們分享了 tnfilt。你可以像這樣使用它:

 
 
 
  1. $ cargo build 2>&1 | tnfilt

它將上面的輸出轉(zhuǎn)換為如下所示:

 
 
 
  1. error[E0271]: type mismatch resolving `>::Output == typenum::B1`

現(xiàn)在才有意義!

結(jié)論

當(dāng)在軟件與硬件進(jìn)行交互時(shí),普遍使用內(nèi)存映射寄存器,并且有無數(shù)種方法來描述這些交互,每種方法在易用性和安全性上都有不同的權(quán)衡。我們發(fā)現(xiàn)使用類型級(jí)編程來取得內(nèi)存映射寄存器交互的編譯時(shí)檢查可以為我們提供制作更安全軟件的必要信息。該代碼可在 bounded-registers crate(Rust 包)中找到。

我們的團(tuán)隊(duì)從安全性較高的一面開始,然后嘗試找出如何將易用性滑塊移近易用端。從這些雄心壯志中,“邊界寄存器”就誕生了,我們?cè)?Auxon 公司的冒險(xiǎn)中遇到內(nèi)存映射設(shè)備的任何時(shí)候都可以使用它。


此內(nèi)容最初發(fā)布在 Auxon Engineering 博客上,并經(jīng)許可進(jìn)行編輯和重新發(fā)布。


  1. 從技術(shù)上講,從定義上看,從寄存器字段讀取的值只能在規(guī)定的范圍內(nèi),但是我們當(dāng)中沒有一個(gè)人生活在一個(gè)純凈的世界中,而且你永遠(yuǎn)都不知道外部系統(tǒng)發(fā)揮作用時(shí)會(huì)發(fā)生什么。你是在這里接受硬件之神的命令,因此與其強(qiáng)迫你進(jìn)入“可能的恐慌”狀態(tài),還不如給你提供處理“這將永遠(yuǎn)不會(huì)發(fā)生”的機(jī)會(huì)。 ?

  2. get_field 看起來有點(diǎn)奇怪。我正在專門查看 Field::Read 部分。Field 是一種類型,你需要該類型的實(shí)例才能傳遞給 get_field。更干凈的 API 可能類似于:regs.rx.get_field::(); 但是請(qǐng)記住,Field 是一種具有固定的寬度、偏移量等索引的類型的同義詞。要像這樣對(duì) get_field 進(jìn)行參數(shù)化,你需要使用更高級(jí)的類型。 ?


網(wǎng)站欄目:C還是Rust:選擇哪個(gè)用于硬件抽象編程
文章源于:http://www.5511xx.com/article/cdecipo.html