Rust 如何在cortex-m0上有多个可变引用?
我正在为没有原子的cortex-m0处理器编写代码。我希望能够有一个引用仅用于读取值,另一个引用仅用于写入值。我想实现只强制写入和读取的设计约束 我的目标是使存储的变量的大小能够用单个CPU指令写入。这通常是通过允许读-修改-写的原子来完成的,但我不会进行读-修改-写,只读或只写 这种情况的用例是中断处理程序写入一个由主代码读取的值,或者主代码写入一个由中断处理程序读取的(不同的)值 我在想象我会用这样的物体:Rust 如何在cortex-m0上有多个可变引用?,rust,atomic,Rust,Atomic,我正在为没有原子的cortex-m0处理器编写代码。我希望能够有一个引用仅用于读取值,另一个引用仅用于写入值。我想实现只强制写入和读取的设计约束 我的目标是使存储的变量的大小能够用单个CPU指令写入。这通常是通过允许读-修改-写的原子来完成的,但我不会进行读-修改-写,只读或只写 这种情况的用例是中断处理程序写入一个由主代码读取的值,或者主代码写入一个由中断处理程序读取的(不同的)值 我在想象我会用这样的物体: let mut thing = get_new_thing(5 as u8); le
let mut thing = get_new_thing(5 as u8);
let mut reader = thing.get_reader();
let mut writer = thing.get_writer();
reader.read();
writer.write(12 as u8);
///not allowed
reader.write(5 as u8);
writer.read();
按照杰伊·皮的建议,你可以这样做
我不知道如何在safe-Rust中实现,因为您显然希望同时拥有读写器(
RefCell
将需要一些运行时开销)。请注意,没有同步,因此多线程需要一个互斥体,我提出了一个解决方案,它实现了
这应限于可在单个CPU周期内写入的数据类型(u8
,u16
,u32
,usize
,i8
,i16
,等等)。这段代码还没有做到这一点
Nothing
类型(字段r
,w
,\u b
)防止从单个IntSharedData
收集多个读卡器/写卡器,并强制执行读卡器和写卡器的生存期
不安全代码在这里起作用,因为只有一个实例在一次使用一个时钟周期写入值。读者对他们阅读的价值总是有一致的看法
#[derive(Debug)]
pub struct IntSharedData<T> {
data: T,
r: Nothing,
w: Nothing,
}
pub struct UsableSharedReader<'a, T, OWNER> {
d: *const T,
_b: &'a Nothing,
_owner: OWNER,
}
pub struct UsableSharedWriter<'a, T, OWNER> {
d: *mut T,
_b: &'a Nothing,
_owner: OWNER,
}
#[derive(Debug)]
pub struct Nothing;
pub struct Interrupt;
pub struct Thread;
pub fn new_data<T>(value: T) -> IntSharedData<T> {
IntSharedData::<T> {
data: value,
r: Nothing,
w: Nothing,
}
}
impl<'a, T, OWNER> UsableSharedWriter<'a, T, OWNER>
where
T: Copy,
{
pub fn write(&mut self, val: T) {
unsafe {
*self.d = val;
}
}
}
impl<'a, T, OWNER> UsableSharedReader<'a, T, OWNER>
where
T: Copy,
{
pub fn read(&self) -> T {
unsafe { *self.d }
}
}
impl<T> IntSharedData<T>
where
T: Copy,
{
pub fn get_rw_to_interrupt(
&mut self,
) -> (
UsableSharedReader<T, Interrupt>,
UsableSharedWriter<T, Thread>,
) {
let r = UsableSharedReader::<T, Interrupt> {
d: &mut self.data as *const T,
_owner: Interrupt,
_b: &mut self.r,
};
let wd = &mut self.data as *mut T;
let w = UsableSharedWriter::<T, Thread> {
d: wd,
_owner: Thread,
_b: &mut self.w,
};
return (r, w);
}
pub fn get_rw_from_interrupt(
&mut self,
) -> (
UsableSharedReader<T, Thread>,
UsableSharedWriter<T, Interrupt>,
) {
let r = UsableSharedReader::<T, Thread> {
d: &mut self.data,
_owner: Thread,
_b: &mut self.r,
};
let w = UsableSharedWriter::<T, Interrupt> {
d: &mut self.data,
_owner: Interrupt,
_b: &mut self.w,
};
return (r, w);
}
}
fn main() {
let mut element = new_data(5 as u8);
let (r, mut w) = element.get_rw_from_interrupt();
//let (mut a,mut b) = element.get_rw_from_interrupt();
let v1 = r.read();
w.write(5);
let v2 = r.read();
println!("v1: {}, v2: {}, t: {:?}", v1, v2, element);
}
#[派生(调试)]
pub结构IntSharedData{
数据:T,
r:没什么,
w:没什么,
}
pub结构UsableSharedData IntSharedData{
IntSharedData::{
数据:价值,
r:没什么,
w:没什么,
}
}
恳求
哪里
T:收到,
{
发布fn写入(&mut self,val:T){
不安全{
*self.d=val;
}
}
}
恳求
哪里
T:收到,
{
发布fn读取(&self)->T{
不安全的{*self.d}
}
}
impl IntSharedData
哪里
T:收到,
{
pub fn get_rw_to_中断(
&莫特·赛尔夫,
) -> (
可使用共享恐惧者,
UsableSharedWriter,
) {
设r=UsableSharedReader::{
d:&将self.data变为*const T,
_所有者:打断,
_b:&mut self.r,
};
设wd=&mut self.data为*mut T;
设w=UsableSharedWriter::{
d:wd,
_所有者:线程,
_b:&mut self.w,
};
返回(r,w);
}
pub fn从中断中获取(
&莫特·赛尔夫,
) -> (
可使用共享恐惧者,
UsableSharedWriter,
) {
设r=UsableSharedReader::{
d:&mut self.data,
_所有者:线程,
_b:&mut self.r,
};
设w=UsableSharedWriter::{
d:&mut self.data,
_所有者:打断,
_b:&mut self.w,
};
返回(r,w);
}
}
fn main(){
设mut元素=新的_数据(5为u8);
let(r,mut w)=元素。从_中断()获取_rw_;
//let(mut a,mut b)=元素。从_中断()获取_rw_;
设v1=r.read();
w、 写(5);
设v2=r.read();
println!(“v1:{},v2:{},t:{:?}”,v1,v2,元素);
}
如果我理解正确,您在M0单线程CPU上使用的是生锈的裸机,希望处理平台中断
您的场景似乎非常特殊,原因有两个:
- CPU没有任何原子支持,因为考虑到只有一个内核,它们没有任何意义
- 你仍然有某种形式的“线程”,即使你在一个CPU上,考虑到你的代码可能在任何地方被中断,以切换到处理中断钩子。中断处理程序应被视为单独的“线程”,您不能向它们发送非发送数据。事实上,在裸机环境中
这里解决方案的关键点是,即使CPU上没有跨核原子,Rust core中也应该有原子,至少支持加载
和存储
,跨中断提供安全的可变内存位置,这是一个非常难以正确解决的问题,因为如果编译器在单个内核上运行,并且可能不会被中断,则编译器能够广泛地优化内容。(因此,我不完全相信目前提出的其他解决方案是正确的,请参阅。)
使用这些原子(且无不安全因素),您的问题的解决方案如下所示:
注意:为了遵循常见的Rust语义,您可能希望在读写器上使用.get()
和.set()
作为方法(匹配项)。它还有一个额外的优点,就是它消除了每次是否要覆盖一个值的模糊性,而在阅读问题时,我最初并不完全确定这一点。我在实现中使用这些名称
#![无标准]
#![无主]
使用紧急停止作为uu;
使用cortex_m_rt::{entry,exception};
mod读写器{
//!用于限制原子读取或写入的简单包装器
使用core::sync::atomic::{AtomicUsize,Ordering};
发布结构AtomicUsizeWrapper{
内部:原子化,
}
植入原子音乐包装器{
酒吧常数fn新(val:usize)->Self{
自我{
内部:原子化::新建(val),
}
}
发布fn获取(&self)->使用{
self.internal.load(排序::SeqCst)
}
发布fn集(&self,val:usize)
#[derive(Debug)]
pub struct IntSharedData<T> {
data: T,
r: Nothing,
w: Nothing,
}
pub struct UsableSharedReader<'a, T, OWNER> {
d: *const T,
_b: &'a Nothing,
_owner: OWNER,
}
pub struct UsableSharedWriter<'a, T, OWNER> {
d: *mut T,
_b: &'a Nothing,
_owner: OWNER,
}
#[derive(Debug)]
pub struct Nothing;
pub struct Interrupt;
pub struct Thread;
pub fn new_data<T>(value: T) -> IntSharedData<T> {
IntSharedData::<T> {
data: value,
r: Nothing,
w: Nothing,
}
}
impl<'a, T, OWNER> UsableSharedWriter<'a, T, OWNER>
where
T: Copy,
{
pub fn write(&mut self, val: T) {
unsafe {
*self.d = val;
}
}
}
impl<'a, T, OWNER> UsableSharedReader<'a, T, OWNER>
where
T: Copy,
{
pub fn read(&self) -> T {
unsafe { *self.d }
}
}
impl<T> IntSharedData<T>
where
T: Copy,
{
pub fn get_rw_to_interrupt(
&mut self,
) -> (
UsableSharedReader<T, Interrupt>,
UsableSharedWriter<T, Thread>,
) {
let r = UsableSharedReader::<T, Interrupt> {
d: &mut self.data as *const T,
_owner: Interrupt,
_b: &mut self.r,
};
let wd = &mut self.data as *mut T;
let w = UsableSharedWriter::<T, Thread> {
d: wd,
_owner: Thread,
_b: &mut self.w,
};
return (r, w);
}
pub fn get_rw_from_interrupt(
&mut self,
) -> (
UsableSharedReader<T, Thread>,
UsableSharedWriter<T, Interrupt>,
) {
let r = UsableSharedReader::<T, Thread> {
d: &mut self.data,
_owner: Thread,
_b: &mut self.r,
};
let w = UsableSharedWriter::<T, Interrupt> {
d: &mut self.data,
_owner: Interrupt,
_b: &mut self.w,
};
return (r, w);
}
}
fn main() {
let mut element = new_data(5 as u8);
let (r, mut w) = element.get_rw_from_interrupt();
//let (mut a,mut b) = element.get_rw_from_interrupt();
let v1 = r.read();
w.write(5);
let v2 = r.read();
println!("v1: {}, v2: {}, t: {:?}", v1, v2, element);
}