在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 16275|回复: 4

[资料] qsys使用经验

[复制链接]
发表于 2013-11-16 12:36:15 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x

Altera公司在Quartus II 11.0 之后推出了Qsys集成开发工具从开始流程上看,与之前的SOPC builder没有太大的区别,但是在实际开发中有很多的不同点,Qsys取代SOPC builder也将成为一个趋势。Quartus II 11.0版本还没有取消SOPC builder,不过取消了之前版本的快捷方式,取而代之的是Qsys快捷方式,具体在菜单Tool->SOPC builder启动。

Qsys系统集成工具自动生成互联逻辑,连接知识产权(IP)功能和子系统,从而显著节省了时间,减轻了FPGA设计工作量。Qsys是下一代SOPC Builder工具,在FPGA优化芯片网络(NoC)新技术支持下,与SOPC Builder相比,提高了性能,增强了设计重用功能,更迅速的进行验证。

Qsys优点

加速开发

·
使用方便的GUI界面,支持IP功能和子系统的快速集成。

·
自动生成互联逻辑(地址/数据总线连接、总线宽度匹配逻辑、地址解码逻辑以及仲裁逻辑等)

·
Altera及其IP合作伙伴提供的即插即用Qsys兼容IP

·
系统hdl自动生成

·
分层设计流程,实现了灵活的设计,支持基于团队的设计,提高了设计重用能力

·
SOPC Builder设计移植到Qsys的移植流程

更快的时序收敛

·
SOPC Builder系统互联架构相比,基于NoC体系结构的高性能Qsys互联以及自动流水线将性能提高了两倍。

·
控制功能强大的自动流水线,满足fMAX和延时系统要求。

更快的完成验证

·
利用自动测试台生成功能并使用验证IP套装迅速开始您的仿真

·
通过系统控制台进行发送读写系统级操作,来加快电路板开发

以下将通过一个在DE2-70开发板的一个例子叙述Qsys的开发过程:

1
新建工程qsys_test,选择EP2C70F896C6芯片。

2
打开Qsys,系统默认添加了clk模块,这和SOPC builder有很大的区别。往系统中添加系统需要的模块,存储器SDRAM,还有用来连接外部的PIO端口,不同模块的连接都采用新的方式,需要仔细的研究,在System菜单中选择自动分配地址等,系统构架图如图1所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg

1 Qsys系统构架图

需要注意的一点就是Qsys生成顶层文件的方式和SOPC builder不一样,所以如果模块有Conduit接口的都要在click to export点击,如图2示,否则在Generate之后再HDL example界面将看不到这些接口,而只会出现clkreset接口。我们也注意到在系统默认clkExport栏目是有默认值的。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png

2  Conduit接口修改

1
修改完毕,系统无错误提示之后,设置Project Settings设置Clock crossingadapter type中设置类型,一共有3种类型:HandshakefifoAuto,这3中分别有不同的含义,大体如下:

Handshake:采用简单的握手协议处理跨时钟域数据传输,在这种模式下耗用的资源比较少,适用于数据吞吐量比较少时的情况;
FIFO
:采用了双时钟的FIFO做同步处理,这种模式下可以处理吞吐量比较大的数据传输,但是总体延时是Handshake的两倍,适用于吞吐量比较大的存储器映射的数据传输;
Auto
:这种模式下同时采用HandshakeFIFO方式的连接,在突发连接中使用FIFO方式,其它情况下使用Handshake方式。

本案例选择Auto模式,如图3示:

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image005.jpg

3 Project Settings设置

4Generation菜单中进行,仿真设置,默认状态下是None,这个根据自己的需要来设置就可以,设置完毕之后点击Generate,如图4所示

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image007.jpg

5、复制顶层文件

Qsys生成模块成功之后顶层文件有专门的标签下点击copy来复制,这个和SOPC builder生成硬件描述语言文件不同,本例子的顶层文件如图5所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image009.jpg

回到Quartus II,新建系统顶层文件,给Qsys系统生成的模块端口赋值,完成之后编译,编译通过编译之后配置引脚再编译

至此,项目的硬体开发部分已经全部完成,下面将进入软体Nios II 11.0 IDE开发阶段。

7、使用Nios II 11.0 Software Build Tools forEclipse进入工程目录,如图7所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image010.png

新建工程,选择Hello World模板,选择何种模板根据系统需求来定。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image011.png

QsysuC/OS学习笔记1:与Qsys的第一次亲密接触


Quartus II 11.0开始,喜新厌旧的Altera就不厌其烦的炒作SOPC Builder的替代者Qsys。记得去年参加他们的研讨会时就已经炒得火热,如今12.0sp2都已经release了,12以后更是完全摒弃了SOPC Buider,如果再不加紧找个理由上Qsys练练手,咱可就要OUT了。


正好近期对uC/OS非常感兴趣,苦于手上没有一款比较高端的板子用于实践。于是通过层层关系最终在Altera的大学计划经理John处讨得一套TerasicDE2-115,板载EP4CE115F29C7N器件,丰富的片上资源和外设足以应付各种功能需求。上个图,让各位看官也垂涎三尺。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image012.jpg

1


废话就此打住,后面要上电有营养的文字。话说特权同学也是第一次接触Qsys,虽然这两天抽空恶补了一下Qsys的各种pdf,但毕竟学海无涯苦作舟,咱也就多懂点皮毛,提前班门弄斧一下,只是实实在在的把自己知道的、明白的、领会的一一道来,期间肯定有疏漏和不妥之处,甚至也会带些疑问而来,还请高手不吝惜键盘跟帖指点。


第一步当然是新建工程,这等小儿科步骤不详究,直接步入主题。如图2所示,两种方式均可打开Qsys

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image013.jpg

2


进入Qsys后,如图3所示,界面窗口的布局内容多少还有些似曾相识,毕竟还是SOPC Builder一脉相承的,一个最大的变化时Qsys tabs的选项要比SOPC Buider多得多,Qsys的更多系统个性化编辑和设置也都得益于此了。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image014.jpg

3


初次打开QsysSystem Content中默认已经添加了一个孤零零的CLOCK组件,其他啥也没有,光杆司令只是个摆设,啥活干不了。于是乎,咱觉得在ComponentLibrary中各种查找,添加了几个常用组件,如NIOS II处理器、JTAG UART、定时器TIMER8bit输出PIOsystem ID200KB的片内RAM。如图4所示,这些常用的组件各就各位,基本就可以搭建起一个最小嵌入式系统了,至少是可以写代码在线运行程序的。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image015.jpg

4


不过,看图4咱可就有点看不懂了,SOPC Builder可不是这样,Connections应该是完全在添加组件过程中自动互联上才对的,而Qsys则一片空白,貌似啥都不连接,别说,还真这么回事。那么就这么着把,绝对不可以,人家系统肯定罢工。怎么办?自己动手,丰衣足食!找了相关资料,都说Qsys可以很智能的进行组件的互联,愣是没找到一个智能按键让他们自动互联,看来Altera把这项考验真水平的活都下放给最智能的人脑来干了,能不智能吗?哈哈,好了,还好特权同学底子不差,虽然以前都让SOPC Buider自个连接从不干预,但是实际接口都是牢记于心的,三下五除二便连接成功。如图5所示,看到小圆圈点点空心实心就会变化,实心代表连接上了。这连接的活可好玩了,一点不比那些年不知道祸害了多少有志青年的连连看差多少,那比得是速度,咱比的是准确。系统的连接其实也非常简单,我们的时钟clk和复位reset都没有做太复杂,都是clk_0组件输出,所以所有的组件都和clk_0的时钟复位连接上就对了;cpu的数据存储器和代码存储器都必须由片内RAM来担当,所以nios2_qsysdata_masterinstruction_master均与代表onchip_mem的从机总线s1连接上。而其他作为总线slave的外设均连接到nios2_qsysdata_master上即可。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image016.jpg

5


另外,要说明的是作为系统与外部连接的接口不像SOPC Buider一样直接引出了,需要设计者特别设置一下。如图6所示,选择Export列的属性为*_external connection,然后该接口前面会出现一个export的图标。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image017.jpg

6


接着我们还要双击打开nios2_qsys组件,将其Reset VectorException Vector均设为onchip_mem。如图7所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image018.jpg

7

就此,一个漂亮的测试系统搭建完毕,后面的事情就是分配地址、中断优先级等,这个可以如同SOPC Builder一样使用菜单栏上的自动分别选项一键分配。特权同学就常常这么干,咱对地址还真没讲究,中断优先级有时还可以根据需要调整一下。


前面提到了Qsys tabs是一大特色,这里不一一细说,偷懒贴几张图。大家自己使用的时候可以慢慢体味。如图8Address Map对地址的管理一目了然,而且对于不同的Master可以有不同的地址空间映射。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image019.jpg

8


如图9所示,System Inspector中罗列所有的信号接口以及相关属性参数,甚至可以在此处进行修改。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image020.jpg

9


如图10所示,HDL Example中直接给出了当前系统的例化模板,直接复制到工程顶层模块后进行修改即可,这比之前专门要到工程目录下找相关文件查看要方便得多。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image021.jpg

10


最后,如图11所示,Generation里可以选择系统仿真、综合以及各种输出的相关设置,最后点击右下角的Generate即可启动当前系统的生成。大家可别忘了在Output Directory中设置后系统输出路径。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image022.jpg

11

QsysuC/OS学习笔记2:系统仿真


仿真在FPGA设计过程中举足轻重,在板级调试前若不好好花功夫做一些前期的验证和测试工作,后期肯定要不断的返工甚至推倒重来,这是FPGA设计的迭代特性所决定的。因此,在设计的前期做足了仿真测试工作,虽然不能完全避免后期问题和错误的发生,却能够大大减少后期调试和排错的工作量。


逻辑设计中需要做仿真,是因为逻辑设计大都是设计者原型开发的,不做仿真的话设计者肯定心里也没底。而用Qsys搭建的系统多是由已经成熟验证过的IP核组成的,还需要仿真否?这是个仁者见仁智者见智的问题,特权同学也无意深入其中不能自拔。过去用SOPC Builder时还确实想动手做做这类带CPU的系统级仿真,只可惜倒腾半天不是缺这个就是少那个,大都无功而返。这回上了Qsys,而且ModelSim-Altera对于Quatus II的支持也是做得越来越体贴了,所以今个再做了一些尝试,果然成功了,原厂把工具的使用做得越来越傻瓜便利的同时,工程师们从中大大获益。


如图1所示,首先在GenerationsSimulation选项中做好设置。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image023.jpg

1

        Simulation设置选项的具体含义如图2所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image024.jpg

2


因为都是用verilog,所以simulation modeltestbenchsimulation model我们都选择Verilog,话说Altera其实主打的是Verilog语言,所以各种功能对Verilog的支持都是非常到位的,VHDL就不一定了。也用过Xilinx的东西,则正好相反。也不能谈论孰优孰劣,也是习惯使然。

        StandardSimple的模型主要差别在于后者只是简单的在testbench里产生clockreset信号,而前者则会对所有export信号产生激励或引出便于监视观察。


确定完成Simulation的设置后就可以点击左下角的Generate重新生成系统。


系统生成完毕,到工程目录\myqsys\testbench\myqsys_tb\simulation”这个路径下有测试脚本的顶层文件myqsys_tb.v,打开后代码如下:

`timescale 1 ps/ 1 ps

module myqsys_tb(

   );

   wire       myqsys_inst_clk_bfm_clk_clk;                   // myqsys_inst_clk_bfm:clk -> [myqsys_inst:clk_clk,myqsys_inst_reset_bfm:clk]

   wire       myqsys_inst_reset_bfm_reset_reset;             // myqsys_inst_reset_bfm:reset -> myqsys_inst:reset_reset_n

   wire [7:0] myqsys_inst_led_pio_external_connection_export; //myqsys_inst:led_pio_external_connection_export ->myqsys_inst_led_pio_external_connection_bfm:sig_export

   myqsys myqsys_inst (

       .clk_clk                           (myqsys_inst_clk_bfm_clk_clk),                   //                        clk.clk

       .reset_reset_n                     (myqsys_inst_reset_bfm_reset_reset),             //                      reset.reset_n

       .led_pio_external_connection_export(myqsys_inst_led_pio_external_connection_export) // led_pio_external_connection.export

   );

   altera_avalon_clock_source #(

       .CLOCK_RATE (50)

   ) myqsys_inst_clk_bfm (

       .clk (myqsys_inst_clk_bfm_clk_clk) // clk.clk

   );

   altera_avalon_reset_source #(

       .ASSERT_HIGH_RESET    (0),

       .INITIAL_RESET_CYCLES (50)

   ) myqsys_inst_reset_bfm (

       .reset (myqsys_inst_reset_bfm_reset_reset), // reset.reset_n

       .clk  (myqsys_inst_clk_bfm_clk_clk)       //   clk.clk

   );

   altera_conduit_bfm myqsys_inst_led_pio_external_connection_bfm (

       .sig_export (myqsys_inst_led_pio_external_connection_export) //conduit.export

   );

endmodule


该代码中首先例化了被测试系统myqsys,将其3export信号引出。然后分别针对这3export信号产生相应的激励和响应,即altera_avalon_clock_source用于产生clockaltera_avalon_reset_source用于产生reset信号,altera_conduit_bfm则用于观察led_pio输出。这三个模块的详细代码都可以在同目录的submodules子文件夹下找到。


接下来,我们需要打开EDS中的软件工程并对其进行仿真。我们接着使用上一个笔记中创建的countbinary_prj工程进行仿真,首先我们需要到BSP Editor里面去重新generate,因为Qsys有改动并重新生成了。接着修改其main函数如下:

int main(void)

{

   alt_u16 cnt;

   for(cnt=0;cnt<256;cnt++)

   {

       IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE,cnt);

   }

   while(1);

}


目前工程代码执行的意图是:在上电初始化完成后,led_pio会从0连续的递增一直到255,然后停止。保存修改的软件代码并重新编译工程,然后在应用工程上点击右键值并选择Rus asàNiso II ModelSim,如图3所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image025.jpg

3


第一次运行通常会弹出如图4所示的窗口,需要对仿真选项做一些配置。选择选择仿真的工程名(Project name)、仿真的elf可执行文件(Project ELF file name)、ModelSim软件的安装路径(ModelSim path)和Qsys测试脚本封装描述文件(Qsys Testbench Simulation PackageDescriptor File name)存储位置。设计好后点击Run即启动ModelSim进行仿真。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image026.jpg

4


弹出ModelSim-Altera后,我们可以讲顶层文件的3export信号添加的Wave窗口中,然后Run起来,看看仿真时间有2-3秒后我们可以回放仿真波形(具体的时间需要看PC的状况,特权同学的Pentium E5800跑了应该有半分多钟),如图5、图6和图7所示。


如图5,刚上电0ns开始,reset信号有一段时间的低脉冲,大约50clk周期,正如我们的testbench中所设计的。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image027.jpg

5


如图6所示,在仿真进行到大约1.3s时刻,led_pio信号有一段变化的波形,初始0值在经过这段变化波形后最终变为255,这也是我们软件代码里面所设置的最终值。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image028.jpg

6


再来看图7,我们将led_pio的变化段波形放大,果然是我们软件编程的递增的值,一直从0递增到255为止。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image029.jpg

7


仿真的流程基本就是这样,很easy,我们只要动动指尖就可以完成,当然了,如果要做很多个性化的细致的仿真验证,那么在testbench里面我们倒是可以动些手脚,输出结果也不光只是看看波形而已。

QsysuCOS学习笔记3Hello uC/OS-II

        uC/OS-II(又名Micro C/OS)是基于嵌入式系统的完整的,可移植、可固化、可裁剪的可剥夺型实时内核,其已经广泛应用在航空飞行器、医疗设备、工业控制等可靠性和稳定性要求较高的场合。该内核的代码也是完全开源的,如果不做商业用途,完全免费。因此对于广大的嵌入式爱好者与工程师们而言,了解OSuC/OS-II开始不失为一个很好的选择。


之前是使用特权同学自己的SF-NIOS2开发套件进行了EDS上的uC/OS-II样板工程测试,为了当前学习笔记的持续性,这里重新就DE2-115板重新整理一个Hello uC/OS-II实例的创建和演示。

Qsys组件添加


在一个工程实例基础上,添加一个Interval Timer外设,设置该Timer的定时Period10ms,用于作为uC/OS-II的时钟节拍(Clock tick),如图1所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image030.jpg

1


修改该Timer外设名称为ucosii_timer。将它的clkreset信号分别连接到clk组件的clkclk_reset信号上,将它的Avalon从机接口s1连接到nios_qsys_0data_master上,并将它的irq连接到nios_qsys_0d_irq上。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image031.jpg

2


自动分配地址,点击SystemàAssign Base Addresses。自动分配中断向量号,点击SystemàAssign Interrupt Numbers,接着点击Generate生成新的系统。


完成Qsys新系统的Generate,接着重新编译Quartus IIproject。自此,硬件的修改已经就绪。

软件工程创建


如图3所示,打开EDS后,点击FileàNewàNios IIApplication and BSP from Template新建模板工程。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image032.jpg

3


如图4所示,在新建工程向导中,选择SOPC Information File name为当前工程目录下的sopcinfo文件。Project name命名为ucosii_swprj,选择Project templateHello MicroC/OSII。最后点击Finish创建工程。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image033.jpg

4


新建工程出现在工程管理窗口后,右键单击ucosii_swprj文件夹,选择NIOS IIàBSP Editor,如图5所示。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image034.jpg

5


如图6所示,确定Main页面中Common里面的stderr/stdin/stdout均为jtag_uartucosii_timersys_clk_timer即可。点击Generate更新设置。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image035.jpg

6


右键点击应用工程,选择Build Project进行软件工程编译。完成后Console窗口打印如图7所示的信息,可见这个uC/OS-II内核以及软件的HAL占用了大约94KB的存储空间,uC/OS-II其实还是很小的,只不过NIOS II各种外设的HAL比较大,不过也都是可以裁剪的。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image036.jpg

7

uC/OS-II运行调试


首先将Quartus II工程产生的sof硬件配置文件烧录到FPGA中。


接着应用工程上点击右键弹出菜单选择Run asàNios II Hardware,在线运行uC/OS-II实例工程。


这个uC/OS-II工程的实验目的只是创建两个task分别打印一串字符,正如readme所描述:

Readme - HelloMicroC/OS-II Hello Software Example

Hello_uosii is asimple hello world program running MicroC/OS-II. The

purpose of thedesign is to be a very simple application that just

demonstratesMicroC/OS-II running on NIOS II. The design doesn't account

for issues suchas checking system call return codes. etc.


NIOS II Console中,我们可以看到最终运行的效果,如图8所示,两个任务所打印的字符串”Hello from task1””Hello from task2”循环出现。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image037.jpg

8


主要实例源码如下:

#include

#include"includes.h"

/* Definition ofTask Stacks */

#define  TASK_STACKSIZE       2048

OS_STK   task1_stk[TASK_STACKSIZE];

OS_STK   task2_stk[TASK_STACKSIZE];

/* Definition ofTask Priorities */

#defineTASK1_PRIORITY      1

#defineTASK2_PRIORITY      2

/* Prints"Hello World" and sleeps for three seconds */

void task1(void*pdata)

{

while (1)

{

   printf("Hello from task1\n");

   OSTimeDlyHMSM(0, 0, 3, 0);

}

}

/* Prints"Hello World" and sleeps for three seconds */

void task2(void*pdata)

{

while (1)

{

   printf("Hello from task2\n");

   OSTimeDlyHMSM(0, 0, 3, 0);

}

}

/* The mainfunction creates two task and starts multi-tasking */

int main(void)

{

OSTaskCreateExt(task1,

                 NULL,

                 (void *)&task1_stk[TASK_STACKSIZE-1],

                 TASK1_PRIORITY,

                 TASK1_PRIORITY,

                 task1_stk,

                 TASK_STACKSIZE,

                 NULL,

                 0);

            

              

OSTaskCreateExt(task2,

                 NULL,

                 (void *)&task2_stk[TASK_STACKSIZE-1],

                 TASK2_PRIORITY,

                 TASK2_PRIORITY,

                 task2_stk,

                 TASK_STACKSIZE,

                 NULL,

                 0);

OSStart();

return 0;

}


源码中,一个标准的uC/OS-II工程,如图9所示,初始化时调用OSInit();函数;接着调用OSTaskCreate();OSTaskCreateExt();函数创建用户任务;最后调用OSStart();函数运行任务。这里的main函数里虽然没有出现OSInit();函数,但实际上在HAL后台外设初始化时候肯定调用了。中间是任务的创建,这里创建两个任务task1task2,优先级分别为12,并且分配了相应的堆栈空间。在两个任务中,分别打印字符串”Hello fromtask1””Hello from task2”,字符串打印后调用OSTimeDlyHMSM(0, 0, 3, 0);函数做了3s的延时。如果修改这个延时时间,打印效果会发生改变,根据延时的情况,Console窗口出现的打印字样频率和速度会不一样。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image038.jpg

9


前面提到我们的实例其实是有OSInit();函数存在的,而且是在系统的main();函数之前就调用了。这里也探个究竟,也算是给大家讲讲NIOS II的软件执行顺序吧。NIOS II软件在上电后其实并非如同一帮的裸奔的嵌入式软件一样直接从main();函数开始执行程序,而是先执行HAL里面的alt_main();函数,这个函数在bsp工程下的HALàsrc里面,找到名为alt_main();的函数便是。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image039.jpg

10


打开alt_main.c源代码文件后,如图11所示,alt_main();函数中执行了Qsys系统的几乎所有可用外设的初始化,因为我们这个模板工程用的是uC/OS-II的例子,所以必有其初始化,图11中的ALT_OS_INIT();便是。如果右击该函数,选择Open Declaration,则我们便能看到这样的定义:

#defineALT_OS_INIT()    OSInit();

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image040.jpg

11


再来简单的熟悉一下这个模板工程中所涉及的几个函数,包括OSInit();函数、OSTaskCreate();函数、OSTaskCreateExt();函数、OSStart();函数。

OSInit();函数

void OSInit(void)


在使用uC/OS-II的任何功能之前,必须调用该函数。该函数建立了两个任务:空闲任务——在所有其他任务均未就绪时运行;统计任务——计算CPU的利用率。此外,该函数也对uC/OS-II所有的变量和数据结构进行初始化。

OSTaskCreate();函数

INT8U OSTaskCreate(void (*task)(void *p_arg),

                       void *p_arg,

                       OS_STK *ptos,

                       INT8U prio)


除了OSInit();函数执行时为系统建立的2个基本任务(优先级最低的2个任务),uC/OS-II建议用户另外保留2个优先级最低的任务和4个优先级最高的任务,为了将来的内核升级只用,当然了,其实也可以不保留。所以,通常来讲,用户至少可以建立56个任务。

         OSTaskCreate();函数有4个入口参数,其含义分别为:task是指向任务代码的指针;p_arg是任务开始执行时,传递给任务的参数的指针;ptos是分配给任务的堆栈的栈顶指针;prio是分配给任务的优先级。

OSTaskCreateExt();函数

INT8U OSTaskCreateExt(void   (*task)(void *p_arg),

                       void    *p_arg,

                       OS_STK *ptos,

                       INT8U    prio,

                       INT16U   id,

                       OS_STK *pbos,

                       INT32U   stk_size,

                       void    *pext,

                       INT16U   opt)

        OSTaskCreateExt();函数其实是OSTaskCreate();函数的扩展,前4个参数的定义用法完全一致,后面5个入口参数的定义为:id是为要建立的任务创建一个特殊标志符,主要是保留为了将来内核升级使用,当前只要将此参数值设置成和任务的优先级一样就行;pbos指向任务堆栈栈底的指针,用于堆栈的检验;stk_size用于指定堆栈的容量;pext指向用户附加的数据域的指针,用来扩展任务的任务控制块OS_TCBopt用于设定OSTaskCreateExt();的选项,指定是否允许堆栈的检验,是否将堆栈清0,任务是否要进行浮点操作等。

OSStart();函数

void OSStart(void)


该函数负责从任务就绪表中找出用户建立的优先级最高的任务控制块,并开始执行这个任务。调用该函数后,软件就将控制权交给了uC/OS-II的内核,开始运行多任务。在调用该函数之前,必须先建立一个任务,否则,应用程序将会崩溃。

        NIOS II上的uC/OS-II移植,其实就这么简单。当然了,如果要不断的深入进去,一定大有学问。

QsysuC/OS-II学习笔记4:任务状态与工作机制

前面一个笔记我们已经可以轻松的使用EDS提供的HAL构建一个uC/OS-II的模板工程,在这个工程里,所有和移植有关的问题都不用我们操心,我们只要放心的去设计我们的应用程序便可。而一个最简单的uC/OS-II工程也已经呈现在我们面前,三个最基本的步骤就可以完成一个我们曾经以为多么神奇的操作系统。但是,虽然我们能够构建两个最基本的任务,但说实在话,我们还没搞懂它到底如何工作的,依葫芦画瓢没有错,若能够搞清楚它的工作机理就更好了。


先来回顾一下两个task,如下代码:

/* Prints"Hello World" and sleeps for three seconds */

void task1(void*pdata)

{

while (1)

{

   printf("Hello from task1\n");

   OSTimeDlyHMSM(0, 0, 3, 0);

}

}

/* Prints"Hello World" and sleeps for three seconds */

void task2(void*pdata)

{

while (1)

{

   printf("Hello from task2\n");

   OSTimeDlyHMSM(0, 0, 3, 0);

}

}


两个task的代码几乎如出一辙,他们都有自己的主循环while(1),而在while(1)里面都是先打印一串字符,然后调用uC/OS-II的任务延时函数。在打印结果上看,task1task2的打印信息是不断的交错打印,没有哪个task会连续得到打印的机会,其奥秘就在于他们的任务延时是一致的。关于任务延时这里先不做文章,下篇笔记再详细探讨,这里我们只做一个和任务延时有关的小小测试,将task2的任务延时函数注释,如下修改task2的函数:

/* Prints"Hello World" and sleeps for three seconds */

void task2(void*pdata)

{

while (1)

{

   printf("Hello from task2\n");

   //OSTimeDlyHMSM(0, 0, 3, 0);

}

}


重新编译软件工程,然后在硬件上在线运行,此时我们看到在Console中打印的字符串信息如图1所示。是不是很出乎我们的意料,没错,这个测试中我们就是要拿优先级低的task2来说事,在没有任务延时函数的task2中,虽然它的优先级没有task1高,但它却一直占有着CPU的控制权,这到底怎么回事?确实是任务延时函数在作怪,本笔记先不深入,只要大家先记住:任务延时的主要作用便是先把当前任务的CPU控制权释放,交给其他的优先级最高的就绪任务控制。该测试中,其实task1只是在刚启动系统的时候执行了一次,然后它调用任务延时函数将CPU控制权交出,这一交不要紧,task2就赖着不还了,因为task2在运行过程中自始至终没有调用任务延时,所以它将一直占用着CPU,当然了,这种状况可不是我们希望看到的。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image041.jpg

1


当前两个task,每个task通常也都要有自己的while(1),如同大多数裸奔的MCU一样,这又是为什么?裸奔着的MCU通常都只有一个main函数中的while(1)控制着CPU的使用权,CPU的寄存器也供它独享。而uC/OS-II却要改变这一状况,如图2所示,每个任务都有自己的堆栈和任务控制块,而CPU的控制权通常是轮流着使用的,话说风水轮流转嘛,也有这么点味道。当某个任务占用CPU使用权时,CPU的寄存器自然也是它独自享用,它可以将当前堆栈里面的数据对应搬移到CPU寄存器中执行,而其他的任务无论在此之前处于何种状态,都要把他们的数据存储中他们自家的堆栈中;一旦当前任务完成工作或者因为某种系统允许的任务切换情况出现,那么它就得乖乖的交出CPU使用权,把CPU寄存器中的当前数据搬回到自己的堆栈中。到底何时切换任务,何时抢占任务,这都由每个任务自己的任务控制块把持着,他们不会越权抢占别人的CPU控制权,当然也不会允许他人随意的抢走属于自己的CPU控制权。uC/OS-II内部的任务切换说白了就是CPU控制权的切换以及相应的入栈出栈操作,但是这其中涉及到的诸如任务优先权的轮换或者说任务仲裁等问题的处理机制就是非常体现功力的地方,后续有机会也要好好探讨下这方面的议题。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image042.jpg

2


前面我们谈到任务的切换,在同一时刻不同的任务都处于不同的状态,运行着的任务肯定只有一个,那么其他任务的状态都如何?如图3所示,uC/OS-II把任务的状态划分为5种:即睡眠态任务、就绪态任务、运行态任务、被中断态任务和等待状态任务。这里我们先简单的将这5种状态罗列,并没有将他们之间的转换关系示意处理,但各个不同状态之间的变化也都是通过一定的事件触发或者函数调用引起的。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image043.jpg

3


作者原著的《嵌入式实时操作系统uC/OS-II》一书中对这5种状态有如下描述:就绪态意味着任务已经准备好,可以运行,但由于该任务的优先级比正在运行的任务优先级低,还暂时不能运行;运行态是指任务掌握了CPU的使用权,正在运行;挂起态也称作等待状态任务,指任务在等待某一事件的发生(例如等待某外设I/O操作,等待某共享资源由暂不能使用变成能使用状态,等待定时脉冲的到来,或等待超时信号的到来以结束目前的等待,等等);发生中断时,CPU提供相应的中断服务,原来正在运行的任务暂不能运行,就进入了被中断任务。


就拿我们前面列举的实例来讲,当task1运行时,它处于运行态,task2就处于就绪态。而其他几种状态我们暂时还没有接触,后面遇到了再逐一谈论。

QsysuC/OS-II学习笔记5:任务切换


上个笔记提到调用任务延时函数后,系统将会进行任务切换,否则当前运行任务就会一直霸占着CPU的使用权。那么这个任务延时函数中到底有什么奥秘?调用它为什么能够让任务切换自如?这个笔记咱就要揭开uC/OS-II的一大设计精髓——任务切换。


特权同学并非软件工程或是计算机科班出身,还真没学过什么操作系统,对于CPU内部架构和工作机制的理解和认识完全靠自身的实践、摸索加一些教科书的研读。对于一些概念的阐述或许不够专业,如果有些偏差也非常欢迎大家提出来加以纠正,但是我想这些草根式的图文或许多少能够帮助大家快速的理解和认识一些工作机理,但愿八九不离十应该是形容这种状态比较合适的词汇吧。其实如果能起到这样的效果,那么对这些文章而言也就足够。毕竟一板一眼、中规中矩的教科书我们看得太多了,真的是有些审美疲劳了。


因为要说任务调度的机理,那么我们不得不先把几乎所有嵌入式处理器相关的书籍中都会提及的中断概念再提一下。虽然讲中断的书满大街都是,但是我想像图1这样一个简单示意图就能够把中断说清楚的还真不多(怎么有点王婆卖瓜自卖自夸的嫌疑,脸红中~~)。一个裸奔CPU软件,无非就是一个main函数里面while(1)中包办所有功能,偶尔来个中断响应一些实时性要求较高的处理,仅此而已。那么,很显然,中断响应时有一个脱离当前main函数的举动发生,想要让中断响应前后CPU回到原有的main函数执行状态,则必须有一些额外的工作要干,第36步的出栈、入栈便是。这个示意图中,大家需要明白,当某个函数或某些指令占用CPU时,意味着CPU中的寄存器存储着和当前处理状态相关的各种中间数据信息;同样的道理,当中断函数占有CPU数据时,CPU中的寄存器存储着和中断函数相关的各种中间数据。堆栈是专门开辟的一片存储空间,用于和CPU寄存器相映射。一个在main函数中运行着的程序(包括在它的子函数中运行),如果被一个中断信号打断后去执行中断处理,最后返回,这样一个过程,发生了以下一些事情:

1.
应用程序(即Main函数)中执行某条指令,此时应用程序控制CPU的使用权,表现为占有CPU寄存器。

2.
一个中断源产生,应用程序停下了CPU的使用。

3.
应用程序停下来那一刻的CPU寄存器内容被copy到堆栈中,即我们称之为入栈操作。

4.
程序转到中断处理函数执行,表现为即将到来的下一时刻中断处理函数占有CPU使用权。

5.
中断处理函数拥有CPU使用权,表现为占有CPU寄存器,直到中断处理函数执行完成。

6.
CPU寄存器恢复执行入栈操作前的状态,即从堆栈中copy之前入栈的信息,我们称之为出栈。

7.
应用程序回到中断前的下一条指令开始执行,虽然经过2-6步的意外事件,但是除了应用程序比预期延时了一小段时间执行外,好像这个意外事件没有发生过一样。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image044.jpg

1


再来看uC/OS-II中的任务切换是如何实现的,应该说,和传统CPU的中断机制有着异曲同工之妙。说白了,uC/OS-II其实也是假借中断之名偷梁换柱般完成了任务的切换。因为uC/OS-II中的每个task都好比裸奔着的软件程序中的main函数,他们都有机会独立的占用CPU的使用权。Task的写法通常有两种:

void user_task(void* pdata)

{

while (1)

{

    //用户代码

}

}

或者

void user_task(void* pdata)

{

//用户代码

OSTaskDel(OS_PRIO_SELF); //删除当前任务

}


前者我们已经接触过,在用户代码的最后我们通常也会加上任务延时函数,让出CPU的控制权。而后者相当于一次性完成的任务,执行过一次该任务后,自我删除,从此销声匿迹,除非该任务在其他任务函数中被重新建立恢复。

OSTaskDel();函数

INT8U        OSTaskDel              (INT8U            prio);


当任务创建并由OSStart()函数启动后,要么处于运行态(同一时刻有且只有一个运行态的任务),要么处于就绪态,如果处于某个正在运行的任务使用OSTaskDel()函数删除了该任务本身或者其他任务(空闲任务OSTaskIdle()是唯一不能被删除的任务),那么被删除的任务并不是从存储代码的程序中物理消失了,这段代码还在,只不过它已经不在任务切换优先级列表中了,以后的任务切换中不会考虑运行该任务,我们说这种状态叫做休眠态,如果要从休眠态唤醒到就绪态,则需要重新创建该函数。

OSTaskIdle()函数


空闲任务是在OSInit();函数中被建立的,看这个函数的具体内容,发现它其实并没有干什么大事,无非是在那里消磨时间,也的确是这样。但uC/OS-II中必须建立空闲函数,而且它的优先级一定是最低的,至于为什么,我们接下来先讲讲任务切换的机理,然后大家很容易就能明白的。

void OS_TaskIdle (void *p_arg)

{

#if OS_CRITICAL_METHOD ==3                     /* Allocate storage for CPU statusregister           */

    OS_CPU_SR cpu_sr = 0;

#endif

   (void)p_arg;                                /* Prevent compiler warning for not using 'p_arg'     */

    for (;;) {

        OS_ENTER_CRITICAL();

        OSIdleCtr++;

        OS_EXIT_CRITICAL();

        OSTaskIdleHook();                       /* Call user definableHOOK                          */

    }

}


如图2所示,这便是任务切换的大体流程。和中断很相似,这里假设task1要切换到task2task1中一定会调用任务延时函数(上一个笔记已经提到,这是任务切换的必要条件),咱还是简单的12345把它说明白:

1.
Task1拥有CPU的控制权,当前CPU寄存器存储着和task1当前执行指令相关的信息。

2.
Task1调用了任务延时函数。

3.
CPU寄存器的数据信息被copy到了堆栈中,即入栈,这个堆栈是task1独有的,圣神不可侵犯。

4.
Task2独有的堆栈数据信息pasteCPU寄存器中,即出栈,这是要恢复task2在上一次拥有CPU控制权的最后一条执行指令留下的现场。当然也可能task2之前未曾拥有过CPU控制权,没关系,那么默认这个堆栈是应该是个空的。

5.
模拟产生了一个软中断源产生,任务延时函数被中断,停止继续执行。

6.
模拟中断处理函数,大概这个函数中什么都不做,为的是有一个中断返回的动作(还真有点买椟还珠的味道)。

7.
中断返回时,当前的CPU寄存器是task2的工作现场,那么这意味着task2已经拥有了CPU的控制权。怎么样?真得是被偷梁换柱了,CPU已经从task1while(1)里面被俘虏到了task2中。至此,一次任务切换完成。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image045.jpg

2


大家可以好好消化一下,其实任务的切换还真的不是传说中那么神奇。到这里,还是没有把任务延时函数的神秘面纱揭开,没关系,一个一个来,咱要各个击破,每个知识点都吃透了才行。


我们先给出任务延时的一个最基本函数OSTimeDly()的程序,注意看该函数的最后调用了OS_Sched()函数,该函数便是CPU任务切换的罪魁祸首,好奇心强的朋友可不能放过它。

void OSTimeDly (INT16U ticks)

{

    INT8U      y;

#if OS_CRITICAL_METHOD ==3                     /* Allocate storage for CPU statusregister           */

    OS_CPU_SR cpu_sr = 0;

#endif

    if (OSIntNesting > 0){                     /* See if trying to call from an    ISR                 */

        return;

    }

    if (ticks > 0){                            /* 0 means nodelay!                                 */

        OS_ENTER_CRITICAL();

        y =OSTCBCur->OSTCBY;        /* Delay currenttask                                */

        OSRdyTbl[y] &=~OSTCBCur->OSTCBBitX;

        if (OSRdyTbl[y] == 0) {

           OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

        }

        OSTCBCur->OSTCBDly =ticks;             /* Load ticks inTCB                                 */

        OS_EXIT_CRITICAL();

       OS_Sched();                             /* Find next task torun!                            */

    }

}

        OS_Sched()函数完成任务级的调度,该函数完成了前一个任务CPU寄存器的入栈和后一个任务CPU寄存器的出栈,并且在最后做了一次模拟中断返回的操作,这个操作是由OS_TASK_SW()函数里完成的。

void OS_Sched (void)

{

#if OS_CRITICAL_METHOD ==3                           /* Allocate storage for CPU status register     */

    OS_CPU_SR cpu_sr = 0;

#endif

    OS_ENTER_CRITICAL();

    if (OSIntNesting == 0){                          /* Schedule only if all ISRs done and ...      */

        if (OSLockNesting == 0){                     /* ... scheduler is notlocked                 */

           OS_SchedNew();

            if(OSPrioHighRdy != OSPrioCur){          /* No Ctx Sw if currenttask is highest rdy     */

               OSTCBHighRdy = OSTCBPrIOTbl[OSPrioHighRdy];

#if OS_TASK_PROFILE_EN > 0

               OSTCBHighRdy->OSTCBCtxSwCtr++;        /* Inc. # of context switches to this task      */

#endif

               OSCtxSwCtr++;                         /* Increment context switchcounter            */

               OS_TASK_SW();                         /* Perform a contextswitch                    */

            }

        }

    }

    OS_EXIT_CRITICAL();

}


函数void OSCtxSw(void);咱还真无法右键open打开,通常不是用C语言写的,而是用汇编来完成这个操作。


再回到 OSTaskIdle()函数,试想想如果系统中只有两个用户任务task1task2,如果他们都调用任务延时函数,那么模拟中断返回后系统应该到哪里继续执行程序呢?不得而知,或许程序就要跑飞了,基于此,OSTaskIdle()函数就有存在的必要了,虽然它好像不干什么事,但至少它能保证系统在没有task可执行的时候处于一个可控的状态中。除此以外,OSTaskIdle()函数中还做了一件或许大家多少还是有些在意的CPU使用率的计算,它是通过计算空闲时间来推断每秒钟CPU的使用率的。



QsysuC/OS学习笔记6:任务切换-

uC/OS-II总是运行进入就绪态任务中优先级最高的任务。确定哪个优先级最高,下面要由哪个任务运行了,这一工作是由任务调度函数OS_Sched (void)完成的。当前就绪任务要交出CPU控制权并进行任务切换的相关操作都调用了OS_Sched (void)函数。

如图1所示,当前运行态任务交出CPU控制权必须是以下某个函数被调用或某事件发生:OSFlagPend()OSMboxPend()OSMutexPend()OSQPend()OSSemPend()OSTaskSuspend()OSTimeDly()OSTimeDlyHMSM()OSTaskDel()或中断等。

file:///C:/Users/zhuzhiqi/AppData/Local/Temp/msohtmlclip1/01/clip_image046.jpg

1


我们来看看OS_Sched (void)函数的程序:

//*_bspàUCOSIIàsrcàos_core.c

void OS_Sched (void)

{

#if OS_CRITICAL_METHOD == 3    /* Allocate storage for CPUstatus register */

    OS_CPU_SR cpu_sr = 0;

#endif

    OS_ENTER_CRITICAL();

    if (OSIntNesting == 0) { /* Schedule only if allISRs done and ... */

        if (OSLockNesting == 0) { /*... scheduler is not locked */

           OS_SchedNew();

            if(OSPrioHighRdy != OSPrioCur) {

                    /*No Ctx Sw if current task is highest rdy */

               OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

#if OS_TASK_PROFILE_EN > 0

    OSTCBHighRdy->OSTCBCtxSwCtr++;  /* Inc. # ofcontext switches to this task */

#endif

               OSCtxSwCtr++;   /* Increment context switch counter */

               OS_TASK_SW();   /* Perform a context switch  */

            }

        }

    }

    OS_EXIT_CRITICAL();

}

        

在该函数中,简单的讲,只是做了两件事,首先找出当前优先级最高的就绪任务(也可能是运行态任务本身),其次调用了任务级的任务切换函数OS_TASK_SW(),由此进行切换任务间的出栈入栈操作,并模拟一次CPU中断完成任务切换。


任务级的任务切换函数OS_TASK_SW()首先对当前运行任务在CPU寄存器中的现场进行保存,即入栈;其次把即将运行的就绪态任务上一次运行时的现场恢复到当前CPU寄存器中,即出栈,注意每个任务都有自己专属的堆栈区;最后使用软中断指令或陷阱TRAP人为模拟一次中断产生,从而让这个中断返回的时候顺利的从原任务程序中切换到了新任务程序中(因为当前CPU寄存器的现场已经从原任务的变成了新任务的)。

         OS_TASK_SW()实际上是个宏调用,而OSCtxSw(void)函数通常是汇编语言编写的,因为C编译器通常不支持C语言直接操作CPU的寄存器。

//*_bspàHALàincàos_cpu.h

#define OS_TASK_SW          OSCtxSw

void OSCtxSw(void);

         OSCtxSw(void)函数程序如下:

//*_bspàHALàsrcàos_cpu_a.S

/*********************************************************************************

*   PERFORM A CONTEXT SWITCH

*   void OSCtxSw(void)    - from task level

*   void OSIntCtxSw(void) - from interrupt level

*

* Note(s): 1) Upon entry,

*            OSTCBCur     points to the OS_TCB of the task to suspend

*            OSTCBHighRdy points to the OS_TCB of the task to resume

*

********************************************************************************/

        .globalOSIntCtxSw            

        .global OSCtxSw

OSIntCtxSw:

OSCtxSw:   

      /*

       * Save the remaining registers to thestack.

       */

      addi sp, sp, -44

#ifdef ALT_STACK_CHECK

      bltu sp, et, .Lstack_overflow

#endif

#if OS_THREAD_SAFE_NEWLIB

      ldw r3, %gprel(_impure_ptr)(gp)  /* load the pointer */

#endif /* OS_THREAD_SAFE_NEWLIB */

      ldw r4, %gprel(OSTCBCur)(gp)

      stw ra, 0(sp)

      stw fp, 4(sp)

      stw r23, 8(sp)

      stw r22, 12(sp)

      stw r21, 16(sp)

      stw r20, 20(sp)

      stw r19, 24(sp)

      stw r18, 28(sp)

      stw r17, 32(sp)

      stw r16, 36(sp)

#if OS_THREAD_SAFE_NEWLIB

      /*

       * store the current value of_impure_ptr so it can be restored

       * later; _impure_ptr is asigned on aper task basis. It is used

       * by Newlib to achievereentrancy.  

       */

      stw r3,40(sp)                 /* save the impure pointer */

#endif /* OS_THREAD_SAFE_NEWLIB */

      /*

       * Save the current tasks stackpointer into the current tasks OS_TCB.

       * i.e. OSTCBCur->OSTCBStkPtr = sp;

       */

      stw sp,(r4)                 /* save the stackpointer (OSTCBStkPtr */

                                   /* is the first element in the OS_TCB */

                                   /* structure.                         */

      /*

       * Call the user definableOSTaskSWHook()

       */

      call OSTaskSwHook

0:

9:

      /*

       * OSTCBCur = OSTCBHighRdy;

       * OSPrioCur = OSPrioHighRdy;

       */

      ldw r4, %gprel(OSTCBHighRdy)(gp)

      ldb r5, %gprel(OSPrioHighRdy)(gp)

      stw r4,%gprel(OSTCBCur)(gp)   

                            /* set the current task to be the new task */

      stb r5, %gprel(OSPrioCur)(gp)

                            /* store the new task's priority as the current */

                            /*task's priority  */

      /*

       * Set the stack pointer to point tothe new task's stack

       */

      ldw sp, (r4) /* the stack pointer is thefirst entry in the OS_TCB structure */

     

#if defined(ALT_STACK_CHECK) && (OS_TASK_CREATE_EXT_EN > 0)

      ldw et,8(r4)                 /* load the new stack limit */

#endif

#if OS_THREAD_SAFE_NEWLIB

      /*

       * restore the value of _impure_ptr ;_impure_ptr is asigned on a

       * per task basis. It is used byNewlib to achieve reentrancy.   

       */

      ldw r3,40(sp)                 /* load the new impure pointer */

#endif /* OS_THREAD_SAFE_NEWLIB */

      /*

       * Restore the saved registers for thenew task.

       */

      ldw ra, 0(sp)

      ldw fp, 4(sp)

      ldw r23, 8(sp)

      ldw r22, 12(sp)

      ldw r21, 16(sp)

      ldw r20, 20(sp)

      ldw r19, 24(sp)

      ldw r18, 28(sp)

      ldw r17, 32(sp)

      ldw r16, 36(sp)

#if OS_THREAD_SAFE_NEWLIB

      stw r3, %gprel(_impure_ptr)(gp) /* update_impure_ptr */

#endif /* OS_THREAD_SAFE_NEWLIB */

#if defined(ALT_STACK_CHECK) && (OS_TASK_CREATE_EXT_EN > 0)

      stw et, %gprel(alt_stack_limit_value)(gp)

#endif

      addi sp, sp, 44

      /*

       * resume execution of the new task.

       */

      ret

#ifdef ALT_STACK_CHECK

.Lstack_overflow:

        break 3

#endif

.set OSCtxSw_SWITCH_PC,0b-OSCtxSw


这个OS_TASK_SW()函数貌似非常神秘,毕竟是用汇编语言写的,估计大伙都看不懂。不过没有关系,它在做的事情也并不神秘。正如我们前面所言,它首先模拟产生一次软中断,接着让当前运行的任务入栈,让即将运行的最高优先级的就绪态任务出栈,就此完成CPU寄存器现场的转换(偷梁换柱的精髓就在此),最后执行一条ret指令表示前面的软中断程序已经执行完毕,返回(即进入新的任务执行程序)。


关于软中断如何产生,开始也让笔者非常纳闷,教科书上总是非常学术的告诉我们使用软中断指令或陷阱TRAP人为模拟一次中断产生,而理论上这个软中断或TRAP指令应该是一条简单的汇编指令而已,但在NIOS II中移植的这个OS_TASK_SW()函数中却没能找到,整个函数寻觅下来好像真没有哪条指令看上去像软中断或TRAP指令,找遍NIOS IIProcessor Reference Handbook也没能看到哪条指令能够完成软中断或TRAP的功能。在原作者的《嵌入式实时操作系统uC/OS-II(第2版)》第14章给出的80x86上移植的OS_TASK_SW()函数实例中也没有找到类似的指令,其操作程序和NIOS II中移植的大同小异,那到底怎么回事?


有意思的是,最一篇讲述软中断指令的文章(http://course.cug.edu.cn/21cn/微机原理与应用/0329.htm)中找到了蛛丝马迹,这里提出了8086/8088中软中断的助记符为INT OPR,并且给出了这条指令实际运行状况却是多个相关寄存器的躲闪腾挪后完成的。那么回头看作者给出的80x86NIOS II移植程序,虽然没有和INT OPR类似的专用的软中断指令,但函数里面某些指令操作却同样能够完成软中断这个动作。


发表于 2014-7-16 09:52:38 | 显示全部楼层
楼主, 我在Qsys 里面打开tool\system  console\  显示的是cannot load library:‘/altera/quartus/bin\jre\bin\client\jvm.dll
实在是没辙了,还请 大师指点一下,Q1060535881,不胜感激
发表于 2014-7-18 11:38:01 | 显示全部楼层
学习了。。。
发表于 2014-11-22 23:39:20 | 显示全部楼层
能说一说new component的建立吗?不胜感激!!!
发表于 2016-7-5 01:35:41 | 显示全部楼层
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /3 下一条

×

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-25 07:55 , Processed in 0.046816 second(s), 10 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表