从零手写操作系统之RVOS外设中断实现-04

news2024/11/24 6:39:47

从零手写操作系统之RVOS外设中断实现-04

  • RISC-V 中断(Interrupt)的分类
  • RISC-V Trap (中断)处理中涉及的寄存器
  • 寄存器 mie、mip
  • 中断处理流程
  • PLIC 介绍
    • 外部中断(external interrupt )
    • PLIC
      • PLIC Interrupt Source
      • PLIC 编程接口 - 寄存器
      • 操作流程
    • 采用中断方式从 UART 实现输入
    • 测试


本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整理而来,主要作为xv6操作系统学习的一个前置基础。

RVOS是本课程基于RISC-V搭建的简易操作系统名称。

课程代码和环境搭建教程参考github仓库: https://github.com/plctlab/riscv-operating-system-mooc/blob/main/howto-run-with-ubuntu1804_zh.md

前置知识:

  • RVOS环境搭建-01
  • RVOS操作系统内存管理简单实现-02
  • RVOS操作系统协作式多任务切换实现-03
  • RISC-V 学习篇之特权架构下的中断异常处理

RISC-V 中断(Interrupt)的分类

在这里插入图片描述


RISC-V Trap (中断)处理中涉及的寄存器

在这里插入图片描述


寄存器 mie、mip

mstatus寄存器中的MIE位用于控制全局中断是否开启,而mie作为次级中断控制寄存器,用于在全局中断打开的情况下,控制某个具体的中断类型是否开启:
在这里插入图片描述
mip寄存器用于告诉我们是否发生了某类中断。


中断处理流程

  • 中断发生时 Hart 自动执行如下状态转换
    在这里插入图片描述
  • 退出中断 :编程调用 MRET 指令

在这里插入图片描述


PLIC 介绍

外部中断(external interrupt )

我们的外设想要和CPU进行通信,就需要通过中断的方式进行通信,那么考虑到外设的可插拔性,我们需要做好中断信号的转换和汇聚处理:
在这里插入图片描述


PLIC

中断信号转换和汇聚的工作由PLIC完成,也就是中断平台控制器:
在这里插入图片描述
PLIC 是 “Platform-Level Interrupt Controller” 的缩写,它是一种用于处理中断的硬件模块,常用于处理器系统中。PLIC 负责管理和分发各种中断信号,并将它们传递给适当的处理器核心或其他设备。

PLIC 的主要功能包括:

  1. 中断管理:PLIC 能够接收来自不同外设的中断请求,并为每个中断分配一个唯一的中断号。
  2. 中断优先级:PLIC 支持设置中断优先级,以确保高优先级的中断能够及时响应。
  3. 中断分发:根据中断的优先级和目标处理器核心的可用性,PLIC 将中断请求分发给适当的处理器核心。
  4. 中断确认和清除:当处理器核心接收到中断时,PLIC 负责确认中断并在中断处理完成后清除中断状态。

PLIC 在大型多核处理器系统中特别有用,因为它可以协调多个处理器核心之间的中断处理。每个处理器核心可以向 PLIC 注册其中断处理程序,并通过 PLIC 获取适当的中断。

PLIC 的具体实现和配置取决于具体的处理器架构和系统设计。在 RISC-V 架构中,PLIC 是标准的中断控制器,用于处理中断请求和分发。在某些 SOC 中,例如 FU540-C000,PLIC 是其中的一部分,用于管理系统中的中断。


PLIC Interrupt Source

在这里插入图片描述
PLIC(Platform-Level Interrupt Controller)中的中断源是指可以触发中断请求的硬件设备或其他事件。每个中断源都有一个唯一的标识符或中断号,用于在 PLIC 中进行识别和管理。

PLIC 中的中断源可以是各种外设或模块,例如:

  1. 定时器:定时器可以生成周期性的中断请求,用于实现定时功能。
  2. 外部设备:外部设备(如串口、网络控制器、GPIO 等)可以触发中断请求,通知处理器有数据可用或事件发生。
  3. 总线错误:当在总线上发生错误时,例如内存访问错误或设备通信错误,可以生成中断请求来通知系统。
  4. 异常和故障:处理器内部的异常或故障条件,例如除以零、内存访问异常等,也可以作为中断源。

在 PLIC 中,每个中断源都被分配一个唯一的中断号,这些中断号用于识别和区分不同的中断源。当某个中断源产生中断请求时,PLIC 根据中断号和优先级确定中断的处理顺序,并将中断请求发送给适当的处理器核心。

具体的 PLIC 中断源数量和配置取决于处理器架构和 SOC 设计。在 FU540-C000 等特定 SOC 中,具体的中断源和中断号分配可能会有所不同。

上图显示的串口设备的中断源为10

中断源–>中断号—>中断向量–>中断服务程序(ISR)–>中断返回


PLIC 编程接口 - 寄存器

在这里插入图片描述
在这里插入图片描述

  • 优先级寄存器的作用就是根据其设置的优先级级别,确定中断的处理顺序。通过设置不同中断源的优先级,可以在处理多个中断时,确保高优先级的中断得到及时处理,提高系统的响应性能。
  • 举一个简单的例子,假设系统有两个中断源:定时器中断和串口中断。定时器中断的处理优先级设置为高,串口中断的处理优先级设置为低。
  • 如果定时器中断和串口中断同时发生,由于定时器中断的优先级高于串口中断,系统会首先处理定时器中断。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在PLIC(Platform-Level Interrupt Controller)中,"claim"寄存器和"complete"寄存器是用于处理中断请求和中断完成的寄存器。

  1. Claim寄存器(claim register):用于处理中断请求。每个处理器核心在PLIC中有一个claim寄存器。当处理器核心准备处理中断时,它会读取claim寄存器,以获取待处理的中断源(interrupt source)。当处理器核心读取claim寄存器时,PLIC会将最高优先级的未处理中断源的标识位设置为1,并将中断源的ID(interrupt ID)写入claim寄存器。这样,处理器核心就可以知道要处理的中断源是哪一个,并将其从PLIC中“claim”(申请)出来。

  2. Complete寄存器(complete register):用于处理中断完成。每个处理器核心在PLIC中有一个complete寄存器。当一个处理器核心完成对某个中断源的处理后,它会将中断源的ID写入complete寄存器,PLIC会更新中断的状态,并将该中断标记为已完成。这意味着该中断不再是待处理状态,而是已经处理完毕。然后,PLIC会继续检查是否有其他中断源处于就绪状态,并将就绪的中断源写入到相应的可认领寄存器中,以通知处理器核心有新的中断可供处理。

这样,其他处理器核心可以通过读取认领寄存器来获取待处理的中断,并开始处理这些中断。当一个处理器核心正在处理一个中断时,其他处理器核心可以认领并处理其他已经就绪的中断,从而实现并行处理多个中断。

因此,PLIC在将中断标记为已完成后,会继续处理其他已经就绪的中断,并允许其他处理器核心去处理这些中断。这样可以提高系统的并发性和响应性。


操作流程

大家可以对照下图,看看各个寄存器在PILC电路图中的位置,以及其作用域范围:
在这里插入图片描述

在这里插入图片描述


采用中断方式从 UART 实现输入

实现思路:
在这里插入图片描述
代码实现:
在这里插入图片描述

  • 初始化plic设备
void plic_init(void)
{
    //获取hartId
	int hart = r_tp();
  
	/* 
	 * Set priority for UART0.
	 *
	 * Each PLIC interrupt source can be assigned a priority by writing 
	 * to its 32-bit memory-mapped priority register.
	 * The QEMU-virt (the same as FU540-C000) supports 7 levels of priority. 
	 * A priority value of 0 is reserved to mean "never interrupt" and 
	 * effectively disables the interrupt. 
	 * Priority 1 is the lowest active priority, and priority 7 is the highest. 
	 * Ties between global interrupts of the same priority are broken by 
	 * the Interrupt ID; interrupts with the lowest ID have the highest 
	 * effective priority.
	 */
	//设置UART中断源的优先级 
	*(uint32_t*)PLIC_PRIORITY(UART0_IRQ) = 1;
 
	/*
	 * Enable UART0
	 *
	 * Each global interrupt can be enabled by setting the corresponding 
	 * bit in the enables registers.
	 */
	 //设置当前hart对应的enable寄存器中UART位为1,即针对当前hart开启UART中断源
	*(uint32_t*)PLIC_MENABLE(hart)= (1 << UART0_IRQ);

	/* 
	 * Set priority threshold for UART0.
	 *
	 * PLIC will mask all interrupts of a priority less than or equal to threshold.
	 * Maximum threshold is 7.
	 * For example, a threshold value of zero permits all interrupts with
	 * non-zero priority, whereas a value of 7 masks all interrupts.
	 * Notice, the threshold is global for PLIC, not for each interrupt source.
	 */
	 //针对当前hart设置中断源优先级阈值为0
	*(uint32_t*)PLIC_MTHRESHOLD(hart) = 0;

	/* enable machine-mode external interrupts. */
	//打开mie次级中断控制器中外部中断使能
	w_mie(r_mie() | MIE_MEIE);

	/* enable machine-mode global interrupts. */
	//开启全局中断
	w_mstatus(r_mstatus() | MSTATUS_MIE);
}
  • 获取待处理的最高优先级中断
/* 
 * DESCRIPTION:
 *	Query the PLIC what interrupt we should serve.
 *	Perform an interrupt claim by reading the claim register, which
 *	returns the ID of the highest-priority pending interrupt or zero if there 
 *	is no pending interrupt. 
 *	A successful claim also atomically clears the corresponding pending bit
 *	on the interrupt source.
 * RETURN VALUE:
 *	the ID of the highest-priority pending interrupt or zero if there 
 *	is no pending interrupt.
 */
int plic_claim(void)
{
    //获取当前hart id
	int hart = r_tp();
	//返回待处理的中断源ID
	int irq = *(uint32_t*)PLIC_MCLAIM(hart);
	return irq;
}
  • 通过PLIC某个中断处理完毕
/* 
 * DESCRIPTION:
  *	Writing the interrupt ID it received from the claim (irq) to the 
 *	complete register would signal the PLIC we've served this IRQ. 
 *	The PLIC does not check whether the completion ID is the same as the 
 *	last claim ID for that target. If the completion ID does not match an 
 *	interrupt source that is currently enabled for the target, the completion
 *	is silently ignored.
 * RETURN VALUE: none
 */
void plic_complete(int irq)
{
	int hart = r_tp();
	//将处理完毕的中断源id写入complete寄存器
	*(uint32_t*)PLIC_MCOMPLETE(hart) = irq;
}

在这里插入图片描述

  • 在trap_handler中增加对外部中断的处理—外部中断的中断号为11

这里是引用

reg_t trap_handler(reg_t epc, reg_t cause)
{
	reg_t return_pc = epc;
	reg_t cause_code = cause & 0xfff;
	
	if (cause & 0x80000000) {
		/* Asynchronous trap - interrupt */
		switch (cause_code) {
		case 3:
			uart_puts("software interruption!\n");
			break;
		case 7:
			uart_puts("timer interruption!\n");
			break;
	   //处理外部中断		
		case 11:
			uart_puts("external interruption!\n");
			external_interrupt_handler();
			break;
		default:
			uart_puts("unknown async exception!\n");
			break;
		}
	} else {
		/* Synchronous trap - exception */
		printf("Sync exceptions!, code = %d\n", cause_code);
		panic("OOPS! What can I do!");
		//return_pc += 4;
	}

	return return_pc;
}
  • 外部中断实际处理函数
void external_interrupt_handler()
{
    //获取中断源ID
	int irq = plic_claim();
    //处理UART中断源
	if (irq == UART0_IRQ){
      		uart_isr();
	} else if (irq) {
	    //其他中断源不进行处理
		printf("unexpected interrupt irq = %d\n", irq);
	}
	//中断源合法,告知PLIC中断源处理完毕 
	if (irq) {
		plic_complete(irq);
	}
}

在这里插入图片描述

  • 在uart设备初始化逻辑中开启接收中断
    在这里插入图片描述
  • 将接收中断使能位设置为 1,表示允许接收中断的触发。当有数据到达 UART 接收缓冲区时,将触发接收中断请求,从而执行相应的中断处理程序。
/*
 * handle a uart interrupt, raised because input has arrived, called from trap.c.
 */
void uart_isr(void)
{
	while (1) {
	    //获取uart接收到字符
		int c = uart_getc();
		//将字符写出
		if (c == -1) {
			break;
		} else {
			uart_puts("uart revice word: ");
			uart_putc((char)c);
			uart_putc('\n');
		}
	}
}

测试

void start_kernel(void)
{
	uart_init();
	uart_puts("Hello, RVOS!\n");

	page_init();

	trap_init();
    //新增plic模块初始化
	plic_init();

	sched_init();

	os_main();

	schedule();

	uart_puts("Would not go here!\n");
	while (1) {}; // stop here!
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/609288.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

精调万分(Fine tune SAM)-万分预测器的解读和精调之一

缘起 分割万物(segment-anything model, SAM, 万分), 是图像分割领域的革命, 图像分割从此进入大模型时代. 如何自定义这个大模型以为己用? 或者说, 通过精调取长补短用于自己的项目?这是一个值得研究的问题, 在这里我试着探索一下, 万分在医学影像学里面的脊柱分割的应用. …

【sentinel】滑动时间窗口算法在Sentinel中的应用

固定窗口算法&#xff08;计数器法&#xff09; 算法介绍 计数器法是限流算法里最简单也是最容易实现的一种算法。比如我们规定&#xff0c;对于A接口来说&#xff0c;我们1秒的访问次数不能超过10次。那么我们可以这么做&#xff1a;在一开始的时候&#xff0c;我们可以设置…

ESP-BOX官方例程实践

1.下载esp-box项目代码 github仓库&#xff1a;https://github.com/espressif/esp-box gitee仓库&#xff1a;https://gitee.com/EspressifSystems/esp-box 使用git工具和如下命令进行下载&#xff1a; git clone --recursive https://github.com/espressif/esp-box.git or gi…

【C++ 基础篇:21】:friend 友元四连问:什么是友元?友元类?友元函数?什么时候用友元?

本系列 C 相关文章 仅为笔者学习笔记记录&#xff0c;用自己的理解记录学习&#xff01;C 学习系列将分为三个阶段&#xff1a;基础篇、STL 篇、高阶数据结构与算法篇&#xff0c;相关重点内容如下&#xff1a; 基础篇&#xff1a;类与对象&#xff08;涉及C的三大特性等&#…

S7-200 PLC的CPU模块介绍

更多关于西门子S7-200PLC内容查看&#xff1a;西门子200系列PLC学习课程大纲(课程筹备中) 1.什么是西门子200PLC的CPU? 如下图1-1所示&#xff0c;S7-200 PLC CUP是将一个微处理器&#xff0c;一个集成电源&#xff0c;一定的数字量或模拟量I/O&#xff0c;一定的通信接口等…

【Linux】—— git的管理以及使用

前言&#xff1a; 在上篇我们已经学习了关于调试器gdb的相关知识&#xff0c;本期我将为大家介绍的是关于版本控制工具——git的使用教程&#xff01;&#xff01;&#xff01; 目录 前言 &#xff08;一&#xff09;git的历史介绍 &#xff08;二&#xff09;github和gite…

Unity异步编程【6】——Unity中的UniTask如何取消指定的任务或所有的任务

今天儿童节&#xff0c;犬子已经9个多月了&#xff0c;今天是他的第一个儿童节。中年得子&#xff0c;其乐无穷&#xff08;音&#xff1a;ku bu kan yan&#xff09;…回头是岸啊 〇、 示例效果 一连创建5个异步任务[id 从0~4]&#xff0c;先停止其中的第id 4的任务&#x…

Flutter进阶篇-布局(Layout)原理

1、约束、尺寸、位置 overrideWidget build(BuildContext context) {return Scaffold(body: LayoutBuilder(builder: (context, constraints) {print("body约束:" constraints.toString());return Container(color: Colors.black,width: 300,height: 300,child: L…

【企业化架构部署】基于Nginx搭建LNMP架构

文章目录 一、安装 MySQL 数据库1. 安装Mysql环境依赖包2. 创建运行用户3. 编译安装4. 修改mysql 配置文件5. 更改mysql安装目录和配置文件的属主属组6. 设置路径环境变量7. 初始化数据库8. 添加mysqld系统服务9. 修改mysql 的登录密码10. 授权远程登录 二、编译安装 nginx 服务…

Maven 工具

Maven 工具 Maven简介Maven 基础概念创建 Maven项目依赖配置生命周期与插件分模块开发聚合和继承聚合继承聚合与继承的区别 属性版本管理多环境配置与应用私服 Maven简介 Maven 本质是一个项目管理工具&#xff0c;将项目开发和管理过程抽象成一个项目对象模型&#xff08;POM…

【爬虫】3.4爬取网站复杂数据

1. Web服务器网站 进一步把前面的Web网站的mysql.html, python.html, java.html丰富其中 的内容&#xff0c;并加上图形&#xff1a; mysql.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>my…

ICV报告: 智能座舱SoC全球市场规模预计2025年突破50亿美元

在智能化、互联化车辆需求不断增加的推动下&#xff0c;汽车行业正在经历一场范式转变。这一转变的前沿之一是智能座舱SoC。本市场研究报告对智能座舱SoC市场进行了全面的分析&#xff0c;包括其应用领域、当前状况和主要行业参与者。 智能座舱SoC指的是现代汽车智能座舱系统的…

Qt6.5.1+WebRTC学习笔记(十)开发环境搭建(win10+vs2022)

一、准备 1.操作系统win10 64位 2.合理的上网方式&#xff0c;需要正常访问google,最好有40G以上流量 3.安装VS2022&#xff0c;笔者使用的是社区版&#xff0c;并选中C相关&#xff0c;笔者设置如下 注意&#xff0c;win10的sdk需要是10.0.22621.0&#xff0c;其他版本可能…

吴恩达 ChatGPT Prompt Engineering for Developers 系列课程笔记--06 Transforming

06 Transforming 大语言模型&#xff08;LLM&#xff09;很擅于将输入转换为不同格式的输出&#xff0c;比如翻译、拼写校正或HTML格式转化。相比于复杂的正则表达式&#xff0c;Chat GPT实现更加准确和高效。 1) 不同语种的转换 下述语句实现了英文到西班牙语的翻译。 pro…

Windows IIS/docker+gunicorn两种方式部署django项目

写在最前 本篇文章并不涉及如何使用宝塔搭建django项目&#xff0c;仅适用于windows和docker部署&#xff0c;其中docker是运行在linux平台上的&#xff0c;如果您想在windows上运行docker&#xff0c;请自行搜索如何在windows上运行docker 一、Windows IIS部署 软件版本Win…

MySQL-Linux版安装

MySQL-Linux版安装 1、准备一台Linux服务器 云服务器或者虚拟机都可以&#xff1b; Linux的版本为 CentOS7&#xff1b; 2、 下载Linux版MySQL安装包 下载地址 3、上传MySQL安装包 使用FinalShell软件上传即可&#xff01; 4、 创建目录,并解压 mkdir mysqltar -xvf my…

【SpringCloud学习笔记】zuul网关

【SpringCloud学习笔记】 为什么需要网关zuul网关搭建zuul网关服务网关过滤器 为什么需要网关 微服务项目一般有多个服务&#xff0c;每个服务的地址都不同&#xff0c;客户端如果直接访问服务&#xff0c;无疑是增加客户端开发难度&#xff0c;项目小还好&#xff0c;如果项目…

【图像任务】Transformer系列.1

介绍几篇改进Transformer模型实现亮度增强、图像重建的任务&#xff1a;LLFormer&#xff08;AAAI2023&#xff09;&#xff0c;DLSN&#xff08;TPAMI2023&#xff09;&#xff0c;CAT&#xff08;NeurIPS2022&#xff09;。 Ultra-High-Definition Low-Light Image Enhanceme…

Linux | 进程控制

啊我摔倒了..有没有人扶我起来学习.... &#x1f471;个人主页&#xff1a; 《 C G o d 的个人主页》 \color{Darkorange}{《CGod的个人主页》} 《CGod的个人主页》交个朋友叭~ &#x1f492;个人社区&#xff1a; 《编程成神技术交流社区》 \color{Darkorange}{《编程成神技术…

Redis的内存策略

过期Key处理: 1)Redis之所以性能强大&#xff0c;最主要的原因就是基于内存来存储&#xff0c;然而单节点的Redis内存不宜设置的过大&#xff0c;否则会影响持久化或者是主从复制的性能&#xff0c;可以通过修改配置文件来设置redis的最大内存&#xff0c;通过maxmemory 1gb&am…