tag 标签: 多线程

相关帖子
相关博文
  • 热度 11
    2022-10-26 10:48
    1945 次阅读|
    0 个评论
    从今年暑假开始,接触到了freertos的操作系统,打开了多线程世界的大门,正好又碰上了感芯科技这款并行处理器,可以将两者进行对比 这是一款小巧而精致的最小系统板,板子上集成了两路稳压(3.3V,1.8V),48M高速晶振和ch553g芯片,满足芯片的下载,供电以及时钟电路(如果能够在集成一个复位按键和一个电源指示灯就更好了) 1.环境搭建 MC3172使用 国产 的 MounRiver Studio 作为开发工具,从地址: http://www.mounriver.com/download 可以下载。 官方还提供了线程配置器,程序烧录器以及配套的模板历程,可以直接导入进编译器。这些资料从地址:http://www.gxchip.cn/down/show-70.html 可以下载。 导入程序后的效果如图1-1所示: 图1-1 导入工程 通过线程配置器完成线程的配置如图1-2所示: 图1-2 完成线程的配置 修改线程内容如图1-3所示: 图1-3修改线程内容 完成编译通过下载烧录工具进行烧录如图1-4所示: 图1-4 烧录工具 2.程序编写测试 本文主要针对该芯片的GPIO,UART和线程之间的切换进行测试 我在线程0中接受串口传回的值,并解析成二进制,点亮或熄灭对应的led灯;在线程1中接收串口的值,并打印出去;在线程3和线程4中同时翻转led,来测试每个线程执行的顺序 int bss_value1; u16 rx_data; void delay(u32 time) { for (u32 var = 0; var < time; ++var) { NOP(); } } void gpio_read_val(u32 gpio_sel) { INTDEV_SET_CLK_RST(gpio_sel,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2)); // GPIO_SET_INPUT_EN_VALUE(gpio_sel,(GPIO_PIN8|GPIO_PIN9|GPIO_PIN10|GPIO_PIN11),GPIO_SET_ENABLE); GPIO_SET_OUTPUT_EN_VALUE(gpio_sel,(GPIO_PIN0|GPIO_PIN1|GPIO_PIN2|GPIO_PIN3),GPIO_SET_ENABLE); /*< 完成io初始化 */ GPIO_SET_OUTPUT_PIN_VALUE(gpio_sel,(GPIO_PIN0|GPIO_PIN1|GPIO_PIN2|GPIO_PIN3),(bss_value1)); for (u32 var = 0; var < 5000; ++var); } //////////////////////////////////////////////////////////// void thread_end(void) { while(1); } //////////////////////////////////////////////////////////// void thread0_main(void) { while(1){ gpio_read_val(GPIOA_BASE_ADDR); } thread_end(); } //////////////////////////////////////////////////////////// void thread1_main(void) { INTDEV_SET_CLK_RST(GPCOM8_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV4)); GPCOM_SET_IN_PORT(GPCOM8_BASE_ADDR,(GPCOM_RXD_IS_P2)); //设置p2为rxd GPCOM_SET_OUT_PORT(GPCOM8_BASE_ADDR,( \ GPCOM_P0_OUTPUT_DISABLE|GPCOM_P3_OUTPUT_ENABLE|GPCOM_P2_OUTPUT_DISABLE|GPCOM_P1_OUTPUT_DISABLE| \ GPCOM_P0_IS_HIGH |GPCOM_P3_IS_TXD |GPCOM_P2_IS_HIGH |GPCOM_P1_IS_HIGH \ )); GPCOM_SET_COM_MODE(GPCOM8_BASE_ADDR,GPCOM_UART_MODE); //设置模式为串口模式 GPCOM_SET_COM_SPEED(GPCOM8_BASE_ADDR,12000000,115200); //设置波特率 GPCOM_SET_OVERRIDE_GPIO(GPCOM8_BASE_ADDR, ( \ GPCOM_P2_OVERRIDE_GPIO|GPCOM_P2_INPUT_ENABLE | \ GPCOM_P3_OVERRIDE_GPIO \ )); // u8 rx_data_rp=0; rx_data_rp=GPCOM_GET_RX_WP(GPCOM8_BASE_ADDR); GPCOM_PUSH_TX_DATA(GPCOM8_BASE_ADDR,rx_data_rp); while(1){ // user code section if(rx_data_rp!=(GPCOM_GET_RX_WP(GPCOM8_BASE_ADDR))) { bss_value1=GPCOM_GET_RX_DATA(GPCOM8_BASE_ADDR,rx_data_rp); GPCOM_PUSH_TX_DATA(GPCOM8_BASE_ADDR,bss_value1); rx_data_rp++; rx_data_rp &= 0x0f; } } thread_end(); } //////////////////////////////////////////////////////////// void thread2_main(void) { INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2)); GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR,(GPIO_PIN4),GPIO_SET_ENABLE); while(1){ //TIMER_COMPARER_EXAMPLE(TIMER2_BASE_ADDR); //user code section GPIO_SET_OUTPUT_PIN_INV(GPIOA_BASE_ADDR,(GPIO_PIN4)); delay(5); } thread_end(); } //////////////////////////////////////////////////////////// void thread3_main(void) { INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2)); GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR,(GPIO_PIN6),GPIO_SET_ENABLE); while(1){ //TIMER_COMPARER_EXAMPLE(TIMER2_BASE_ADDR); //user code section GPIO_SET_OUTPUT_PIN_INV(GPIOA_BASE_ADDR,(GPIO_PIN6)); delay(5); } thread_end(); } 测试结果如下: 与freertos相比,该芯片可以通过全局变量来进行数据的传递,无需定义队列等操作。 与此同时线程2和线程3在同时翻转电平,经测试线程3会比线程2慢大约100ns,这可能是线程之间转换所导致的延迟。如图所示: 本文主要验证了该芯片的串口,GPIO,多线程切换的功能。因为最近正在调试ad7124芯片,下一步将尝试着用该芯片去控制ad7124进行数据采集。
  • 热度 17
    2016-4-25 17:19
    1411 次阅读|
    0 个评论
    参考网上的例子,是在VC6下运行的。 void ThreadFunc() {     CTime time;   CString strTime;     m_bRun=TRUE;   while(m_bRun)   {   time=CTime::GetCurrentTime();   strTime=time.Format("%H:%M:%S");   ::SetDlgItemText(AfxGetMainWnd()-m_hWnd,IDC_TIME,strTime);   Sleep(1000);   } } void CSingleThreadDlg::OnBnClickedStart() {   hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,  NULL,0,ThreadID);   GetDlgItem(IDC_START)-EnableWindow(FALSE);   GetDlgItem(IDC_STOP)-EnableWindow(TRUE); } 编译时提示:SingleThread.exe 中的 0x00413437 处有未经处理的异常: 0xC0000005: 读取位置 0x00000020 时发生访问冲突。 查询MSDN了解到,CreateThread函数,第四个参数是传递到ThreadFunc函数的参数, 所以把程序修改为: void ThreadFunc(LPVOID p) {     CTime time;     CString strTime;     m_bRun = TRUE;     HWND hwnd = (HWND)p;     while (m_bRun)     {         time = CTime::GetCurrentTime();         strTime = time.Format("%H:%M:%S");                           SetDlgItemText(hwnd, IDC_TIME, strTime);  //涉及进程间通信,                                                                                      //把用户界面进程的句柄传递给工作线程         Sleep(1000);     } } void CMultiThread1Dlg::OnBnClickedStart() {     // TODO: 在此添加控件通知处理程序代码          hThread=CreateThread(NULL,         0,         (LPTHREAD_START_ROUTINE)ThreadFunc,         this-m_hWnd,               //这个参数是将要传递给ThreadFunc函数的参数,         0,         ThreadID);     this-GetDlgItem(IDC_START)-EnableWindow(FALSE);     this-GetDlgItem(IDC_STOP)-EnableWindow(TRUE);      } 程序正常运行!
  • 热度 20
    2014-12-5 10:56
    1520 次阅读|
    0 个评论
    在使用GCC编译有关多线程的程序时,可能会出现如下错误: undefined reference to `pthread_create' collect2: error: ld returned 1 exit status 解决方法是,为GCC的编译命令增加“-lpthread”参数,比如: gcc 11-pthread-test.c -o 11-pthread-test -lpthread
  • 热度 17
    2013-4-8 10:54
    1570 次阅读|
    2 个评论
    翻译最后一弹:后续可能继续^~^                       OR1200处理器的多线程扩展                               曾坤    刘福东                               中国湖南长沙 邮箱:kunzeng.nudt@gmail.com   lfd010509@163.com 摘要: 多线程是一门拥有广泛运用前景的技术,它被广泛用于通用目的处理器中,用来避免长时延问题,例如高速缓存器丢失。本文提出一种基于OR1200处理器的支持多线程的嵌入式处理器设计方案。这种多线程OR1200处理器支持在一个周期内四个线程的交叉执行。其硬件设计的评估是通过寄存器传输语言(verilog代码)模拟仿真实现的。结果显示多线程的交叉执行能够有效地解决存储器时延,并且能够使速度平均提高1.16倍。 关键字 交叉执行     OR1200    多线程     存储器时延 一:简介 硬件多线程是诸多能够有效回避存储器时延方法中的一种。多线程能够被处理器同时执行。如果其中的一个线程占据了存储器通道,另外的线程仍然能够被执行,而使流水线继续处于繁忙的工作状态。 许多通用目的处理器都利用多线程的优点来增强其性能,诸如Nehalem和UltraSparcT2。但是很少有嵌入式处理器支持多线程。对于此现象的解释如下:第一,嵌入式处理器的频率并没有通用目的处理器的高,这使得内存墙问题表现得不明显。其二,嵌入式处理器通常用SRAM而非DRAM,这也就减轻了它们之间的代沟。第三,很少有线程级别的并行出现在嵌入式应用中。然而,随着嵌入式系统的发展,这种情况就随之发生了变化。例如,一些嵌入式处理器的频率可以达到1.5GH,同时DRAM也被广泛用于移动电话中。对于嵌入式处理器而言,硬件多线程则显得越来越重要了。 本文是对OR1200处理器的扩展,这是一种开源嵌入式处理器,支持四线程的交叉执行。我们使用寄存器传输语言(verilog代码)模拟仿真对多线程OR1200处理器的性能进行评估。结果显示多线程OR1200处理器的性能平均比非多线程处理器要高16%。 二: 相关内容 多线程的思想可以追溯至20世纪80年代到90年代的HEP系统,此系统中运用了多线程来避免访问主存储器时出现的长时延。近年来,则利用多线程技术来解决较短时延问题,如主高速缓冲器的丢失。这些多线程方法可以分为三类:交错多线程,阻塞多线程和同步多线程。交错多线程处理器,如Cray MTA,ASA及ltraSparcT2,取另一个线程的指令送至执行流水线中。阻塞多线程处理器,如MIT J-Machine,PL/PS Machine和Fermi,一直执行一个线程直到长时延操作出现。同步多线程处理器,如IBM Power7和Intel Nehalem,从多线同时发送多个指令,用以填满超标量处理器的执行单元。 由于OR1200处理器是一种单发送RISC处理器,因而同步多线程对其不适用。与阻塞多线程相比,同步多线程的硬件花费较少,这使得它更适合与嵌入式处理器。因此,本文将把OR1200处理器扩展成为一种交叉多线程处理器。 三:OR1200处理器的扩展 1、 OR1200处理器的概述 OR1200是32位开源嵌入式处理器,它服从OpenRISC1000指令集结构。OR1200拥有一个5级流水线,并支持虚拟内存以及基本的DSP功能。图(1)显示OR1200的基本结构。 图(1)OR1200处理器结构 如图(1)所示,OR1200处理器由内核是由七个部分组成:指令单元、异常单元、系统单元、通用目的寄存器组、整数执行单元、乘及计算单元和存取单元。指令单元从存储器系统取指令,然后发送指令至合适的执行单元,并且维持in-flight指令之间的相关性信息。系统单元和异常单元掌控系统寄存器的数据访问和异常。通用目的寄存器组可实现把32个32位寄存器当做两个同步双端口存储器使用。整数执行流水线完成Open-RISC 32位指令,包括算术指令,比较指令,循环指令和移位指令。MAC单元执行32位乘-累加操作。累加器的宽度是48位。存取单元在GRPs和OR1200内部总线中传输数据。系统单元实现所有系统成为特殊的目的寄存器。 对于基本OR1200内核,有两个主要的特征应当引入进来用以支持四线程的交叉执行。第一,寄存器组应当能够增大以维持四线程的运行环境;第二,执行流水线应当能够扩展用以支持四线程的交叉执行。 2、 寄存器组的扩展 OR1200处理器的寄存器组包含32个通用目的寄存器,我们简单将寄存器组复制四次用以维持四线程的运行环境,同时增加了一些额外的控制信号控制对四寄存器组栈的访问。 图(2)四栈寄存器组的读逻辑 每个线程被标以线程号,用作访问寄存器组地址的一部分。图(2)显示了四栈多线程组的读逻辑。多线程寄存器组的地址包括两个部分:线程号及寄存器地址。线程号定义了读哪个线程,从OR1200指令中可获得寄存器地址,其定义了读哪个寄存器。线程号被译码为四位的写使能信号,每一位对应一个栈(bank),当写使能信号为高时,相应的栈被激活。寄存器地址送至所有的栈用以读正确的寄存器。线程号也被用作多路器的控制信号,用其选择正确的输出数据。 如图(3)所示,四栈寄存器组的写逻辑与读逻辑相似,与之不同处是需要无输出多路器,而且输入数据送至所有的栈。当输入数据有效时写使能信号被标记。 在四栈寄存器组中有两个读端口和一个写端口,意味着写逻辑被复制两次。 图(3)四栈寄存器组的写逻辑 3、 执行流水线的扩展 根据操作码图(4)显示了扩展的OR1200流水线的块状表。四个PC寄存器维持支撑四线程。在线程的选择阶段,四个PC寄存器中的一个被选中,被送至取指单元。在取指单元阶段,取指单元利用送达的PC作为地址来访问指令高速缓存器。在指令译码阶段,指令被翻译用以获得操作码并且寄存器组也被访问用以获得源操作数。在执行单元,功能单元根据操作码(图(4)操作)完成适当的操作。执行阶段包含不止一个流水线阶段,根据操作是什么,例如,存储器访问操作跨越两级流水线阶段。功能单元产生的结果在回写阶段被写回寄存器组。线程号(图(4)线程号)在所有流水线中流通,用来确定结果是对应哪个线程的。  图(4)扩展OR1200流水线 四线程用一种循环的方式执行。每一周期内,从四线程内去一条指令然后发送。如果必须的功能单元并不繁忙,指令就会成功发送并且与之响应的PC寄存器也会加四。如果必须的功能单元繁忙,指令就不能成功发送。因而,与之响应的PC寄存器不会更新,进而在下一个循环中相同的指令能够被再次发送。这种执行模式被称作阻塞-重试模式。 PC寄存器更新的逻辑应当能够支持循环执行和阻塞-重试模式。在原始的OR1200处理器中,对于PC寄存器有两个基本的操作。第一,当从指令高速缓存器中取一条指令的时候,PC寄存器加四,然后指向下一条指令;第二,当一条分支指令被执行的时候,分支指令的地址应当被写入PC寄存器。但是这种情况对于多线程OR1200处理器去并非如此。第一,PC寄存器的更新是依据线程号的。当一条指令被取到的时候,仅仅是PC寄存器相关的线程更新;因而,当一条线程号为N的分支指令被执行的时候,仅仅是线程号为N的PC寄存器被更新。第二,如果由于之前的长时延而使一条指令不能够被成功发送时,PC寄存器也不发送变化以便这条指令能够被再次发送。 图(5)显示了PC寄存器的更新逻辑。成功发送的信号标记线程0的指令是否成功发送。成功发送的分支信号标记线程1的分支指令是否成功发送。分支PC信号是由分支指令产生的新的PC。线程0的线程号被译码,用来控制哪个PC寄存器将要更新。 图(5)PC寄存器的更新逻辑 八线程的交叉执行能够避免在执行流水线中的长时延操作。举在图(6)中的指令流为例,在第一个周期里,线程0的取指令被发送至存取单元,致使一个高速缓存丢失(未命中)和LSU变得繁忙起来。在第二个周期中,线程1的存指令由于LSU忙于管理高速缓存丢失(未命中)二不能被发送。因而线程1的PC寄存器不能被更新来容许存指令再次发送。在下一 图(6)一个四线程执行的例子 个周期中,加指令由于ALU并非繁忙而成功发送。算术计算和存储器访问能够并行执行,并且无流水线阻塞产生。 更值得一提的是,八线程的交叉执行消除了在不同阶段中in-flight指令的数据相关性,这是由于in-flight指令来至不同的线程。这就大大精简了在不同流水线阶段之间的相关性判断逻辑和数据前送逻辑,进而明显的增加了频率。 4、 硬件花费评估 我们使用ISE9.1综合多线程OR1200处理器来评估硬件花费。表(I)显示了多线程OR1200处理器和OR1200处理器之间的对比情况。 表(I)硬件花费对比 多线程OR1200处理器的block ram使用数是OR1200处理器的四倍,寄存器组大小也为四倍。同时4输入LUTs的使用率提高了5.3%,触发器使用率提高了3.2%。 5、 性能指标 1)方法 我们利用寄存器传输语言器模拟仿真我们写的多线程OR1200处理器的verilog代码,依据执行周期数来衡量评价性能。在我们的测试中用到了7个内核基准。FFT, Laplace, Stencil是我们用标准的数学算法开发的内核。BMM, Hydro 和 SF来至Livemore测试体系。表(II)列出了详细的内核基准信息。 表(II)基准的详细信息 对于每个内核基准,有两个版本的阐述研究,有一个单线程版本能够在一个线程中实现所有的计算工作,还有一个多线程版本能够把计算工作分成四个线程。单线程版本在OR1200处理器中使用,多线程版本在多线OR1200处理器中使用。 这两种处理器的存储器系统结构是相同的。它们在一块组成一种2级高速缓存系统。L1指令高速缓存和数据高速缓存大小为16KB,并且是直接映射的关系。L2高速缓存是128KB的四集合相关关系。L1和L2的高速缓存块大小都是32字节。L2高速缓存在8个周期中会有一个命中时延,对其的访问为全流水线形式。存储器访问时延为80个周期。 2)结果 如图(7)所示,多线程OR1200处理器在所有基准上都优于OR1200处理器。相应于Inner, Hydro和 Laplace,多线程OR1200处理器能够提高1.33,1.23和1.23倍,但对于BMM,SF而言,速度提高就并不明显(1.02和1.09)。那是因为Inner, Hydro和 Laplace是数据敏感内核,同时也因为它们会产生很多的高速缓存丢失(未命中)。多线程OR1200可以有效的避免这种情况,进而使相应的速度提高。BMM是计算敏感内核,产生并不多的高速缓存丢失(未命中),因而多线程的优点体现的并不明显。 图(7)执行时间对比 我们改变多线程OR1200处理器中的线程数并进行了性能上的对比。图(8)体现了随着线程数的增加而带来的内核基准速度变化的趋势。四线程版的性能在所有基准上表现的都要比双线程版的较为优越。但是六线程和八线程版的在速度变化上并不是那么明显,性能也不再那么优越。对BMM而言,在线程版本达到四时,速度就趋于平稳,八线程版的更是如此。因而对于多线程OR1200处理器而言,线程数定为4是最合适的。 图(8)速度VS线程数 6、 总结 多线程OR1200处理器基于开源的OR1200处理器,支持四线程的交叉执行。速度平均提高1.16倍,然而花费也会增加5%。 7、 鸣谢 作者在此要感谢唐云华,吴俊杰以及匿名的读者对于工作提供的大力支持与帮助。本工作依靠NFS授予的60921062和60873014基金。
相关资源