本文将手把手教你如何基于arm designstart计划,在fpga上搭建一个cortex-m3软核处理器。 以xilinx artix-7系列fpga为例,介绍如何定制一颗arm cortex-m3 soc软核,并添加gpio和uart外设,使用keil mdk环境开发应用程序,jlink下载、调试arm程序,最终的实现效果是led闪烁,串口输出hello world信息。
都有哪些内容?
必要的基础知识
cortex-m3 fpga ip核下载
硬件准备
软件准备
cortex-m3软核搭建
新建vivado工程
添加ip核搜索路径
创建blockdesign设计
添加ip核,gpio和uart外设
swd接口引出
外设基地址分配
管脚分配
bit流文件生成下载
cortex-m3软核程序设计
新建keil工程
gpio输入输出控制实现
串口数据发送和接收实现
延时函数实现
flash编程算法生成
下载运行
开源地址
参考资料
1.必要的基础知识
为了更快的完成在fpga上实现arm cortex-m3软核,一些必要的基础知识还是要有的!
fpga开发基础知识,如fpga开发流程,设计、综合、布局、布线、约束、下载
xilinx vivado开发环境使用基础,如blockdesign设计方式,管脚分配,bit流文件生成与下载
arm cortex-m3内核的使用基础,如stm32、mm32、gd32、ch32等微控制器的开发。
keil-mdk开发环境的使用基础,基本的工程建立、编译、下载流程。
如果以上知识都具备,那么,恭喜你!可以在2小时内完成arm cortex-m3软核在fpga上的实现。
2.cortex-m3 fpga ip核下载
首先,我们需要从arm官网上获取arm cortex-m3 fpga软核ip包。
下载地址如下:
https://silver.arm.com/browse/at426
文件名称:cortex-m3 designstart fpga-xilinx edition(r0p1-00rel0)
文件大小:7.52mb
md5sum:cd67536c29023429cde47130d51b6f49
官网下载需要先注册账号,如果下载速度很慢,可以在公众号后台回复:220318,获取下载链接,复制到浏览器下载。
arm官网
压缩包解压之后,共有4个文件夹:
压缩包内容
各个文件夹存放的内容:
docs
存放arm cortex-m3处理器参考手册、designstart fpga版本使用说明、基于arty-a7开发板的顶层blockdesign框图等文件。
hardware
存放基于digilent arty-a7开发板的vivado工程,顶层blockdesign文件,管脚约束文件,testbench文件等。
software
存放keil-mdk工程,spi flash的编程算法文件等。
vivado
包括designstart cortex-m3 xilinx fpga版本的ip核文件,其中arm_ipi_repository文件夹就是内核源文件了,ip文件内容已经加密,没有可读性。
ip核源码
3.硬件准备
为了完成ds cm3在fpga上的搭建,我们至少需要以下硬件:
一块artix-7开发板,用于构建cortex-m3软核soc,我使用的是正点原子达芬奇pro开发板,fpga型号为xc7a100t。
xilinx fpga下载器,用于下载软核bit流到fpga,如platform usb cable,jtag-hs2/hs3等。
arm cortex-m3调试器,用于调试arm核程序下载和调试,如jlinkv9,jlink-ob等。
官方的ds cm3 ip核是基于digilent的arty-a7开发板,fpga型号为xc7a35t/100t,vivado版本为v2019.1,如果你手头正好有这块开发板,那么可以直接使用官方提供的示例工程。
digilent arty-a7开发板:
arty-a7开发板
正点原子达芬奇pro开发板:
正点原子达芬奇pro开发板
4.软件准备
xilinx vivado开发环境,官方建议版本为2018.2以上,我使用的是2018.3版本
keil mdk开发环境,如5.33版本
ds_cm3的keil器件包
从keil官网上下载designstart cortex-m3所专用的器件支持包,下载链接如下:
https://keilpack.azureedge.net/pack/keil.v2m-mps2_dsx_bsp.1.1.0.pack
5.cortex-m3软核搭建
准备好以上软硬件,就可以开始cortex-m3软核的搭建了。
首先,新建一个文件夹,命名为cortex_m3_on_xc7a100t,用于存放本次示例所有的工程文件,并新建以下几个文件夹:
目录结构
每个文件夹的功能:
bd文件夹
用来存放blockdesign设计
cm3_core文件夹
用来存放的是arm cortex-m3内核ip核文件,
doc文件夹
用来存放设计文档
flash文件夹
用来存放生成的bit和mcs文件
rtl文件夹
用来存放用户设计的verilog源文件
xdc文件夹
用来存放管脚、时序约束文件
其中cm3_core文件夹,需要将官方压缩文件文件中的arm_ipi_repository文件夹复制过来,路径为at426-bu-98000-r0p1-00rel0vivadoarm_ipi_repository
以上文件夹准备好之后,就可以开始新建工程了。
5.1 新建vivado工程
打开vivado 2018.3,打开工程创建向导,输入工程名称,工程的存放路径为之前我们新建的文件夹。
新建工程
选择fpga芯片的完整型号:xc7a100tfgg484。
选择芯片型号
最终创建完成之后的工程目录
vivado工程目录
5.2 添加ip核搜索路径
为了能在blockdesign中搜索到arm cortex-m3处理器ip核,我们需要把arm 软核ip所在的路径添加到搜索路径。
添加到搜索路径
5.3 创建blockdesign设计
为了方便后续使用图形化的方式连接各ip核,我们采用blockdesign图形化的设计方式,这样可以快速的搭建出一颗定制化的软核处理器。
新建blockdesign,命名为cm3_core,保存到最初创建的bd文件夹中。
在画布中添加cortex-m3处理器核:
添加arm核
双击cortex-m3 ip核进行一些基本配置,我们不需要trace功能,选择no trace,使用swd接口调试,禁用jtag端口:
配置arm核
指令空间和数据空间大小,这里设置成64kb,都不进行初始化。
itcm核dtcm配置
5.4 添加一些必要的ip核
时钟pll
用于提供给内核、总线、外设时钟,这里我们配置成50mhz单端输入,pll输出配置成50mhz,如果时钟频率设置更高,综合后会提示wns,tns时序不满足,可能会影响系统的正常运行。
处理器复位ip
用于提供内核、外设、互联组件所需要的复位信号,不需要进行定制,保持默认设置。
总线互联ip
cortex-m3内核为ahb总线,而且内部已经转换成了axi3总线,而xilinx官方提供的gpio/uart等外设ip核是axi4-lite总线,所以需要添加一个总线互联矩阵,用于将不同协议进行转换,从机数量配置为1,主机数量配置为2,连接到处理器的sys总线。
基本逻辑门ip
cortex-m3内核需要低电平复位,而复位ip输出为高电平复位,需要在中间插入一个非门来进行转换。
常量ip
本次软核搭建不涉及中断部分,所以irq和nmi都给定常量0即可,如果需要将中断接入处理器,可以通过concat核将多个中断源合并成一个连接到irq。
将以上ip添加到blockdesign画布中,并按照下图进行连接:
原理图连接
从官方手册中可以知道,arm提供的软核ip中已经包括了itcm和dtcm存储器,所以我们无需添加外部的bram来作为程序和数据的存储区。
cortex-m3内核结构
内核中提供itcm和dtcm都是基于ram实现,这也就意味着后续我们使用keil下载程序只是下载到ram中,掉电数据会丢失。
至此,arm cortex-m3处理器内核就搭建完成了,下面来添加gpio和uart外设。
5.5 添加gpio和uart外设
一些常用的单片机,如stm32,芯片内部的tim、uart、spi、can等外设一般是固定数量的,而我们使用fpga来搭建arm软核soc就比较灵活了,如果你不需要spi,那就不用添加spi外设,需要10个uart就添加10个uart,外设配置比较灵活,当然这些外设都是基于fpga逻辑资源实现的,实际添加的数量会受限于fpga芯片的逻辑资源大小。
下面以添加一组axi gpio和一组axi uart为例,介绍如何使用arm软核来控制这两个外设。
xilinx官方提供的axi gpio外设具有以下特性:
内部有两个通道,通道1和通道2,每个通道最多支持32个管脚
每个管脚可以配置成输入或输出模式
每个管脚可以设置复位初值
支持中断输出
提供的axi uart外设有以下特性:
全双工
支持5-8位数据位
支持奇偶校验
可配置波特率110-230400
这里我们将gpio配置成双通道,通道1为输出模式,低4位用于连接led,通道2为输入模式,低4位用于连接按键。
gpio配置
uart配置成115200波特率,8位数据位,无奇偶校验。
uart配置
配置完成之后,将它们连接的到互联ip的主机接口上:
原理图连接
这两组ip的时钟可以和处理器使用同样的时钟,复位可以使用复位ip输出的外设复位信号。
关于axi gpio和axi uart的详细使用,可以查看官方文档:
pg144-axi-gpio.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf
pg142-axi-uartlite.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_uartlite/v2_0/pg142-axi-uartlite.pdf
5.6 swd接口的引出
官方的designstart ip核资料中,除了cortex-m3处理器,还有一个dap-link调试核,如果使用dap-link调试器需要添加这个ip核。
dap-link
这里我们不使用dap-link调试器,而是使用jlink swd模式。swd模式一共需要两根线,一个是swclk时钟信号,一个是swdio双向数据信号,处理器提供了3个管脚:swdi,swdo和swdoen,我们还需要实现一个双向端口模块。
基于iobuf原语实现的双向端口模块,内容如下:
module swdio_tri_buffer( //inputs input swd_o, input swd_oe, //outputs output swd_i, //inouts inout swd_io);iobuf swd_iobuf_inst( .o(swd_i), .i(swd_o), .io(swd_io), .t(!swd_oe));endmodule
将它添加到我们的设计中。
swd接口连接
最终的blockdesign设计如下图所示:
原理图连接
5.7 分配外设基地址
添加完外设ip之后,我们还需要对外设进行基地址和空间分配,在地址编辑框,右键选择自动分配。
基地址分配
分配完成之后,使用设计验证(validate design)功能,可以检查当前blockdesign设计连接的合法性。
验证设计
5.8 生成wrapper并例化到顶层
为了方便后续添加自定义的fpga逻辑模块,我们将cortex-m3软核处理器作为一个处理器例化到顶层设计中。
在blockdesign源文件上右键,先选择generate output products,耐心等待生成完成之后,选择create hdl wrapper。
生成wrapper
之后就会生成一个_wrapper的verilog文件。
新建顶层文件top_hdl.v并保存到rtl文件夹,将_wrapper例化到顶层。
module top_hdl( //inputs input clk, input rst_n, input swclk, input uart_rxd, input [3:0] sw, //outputs output [3:0] led, output uart_txd, //inouts inout swdio);cm3_core_wrapper cm3_core_wrapper_ut0( //inputs .cm3_clk(clk), .cm3_resetn(rst_n), .cm3_gpio_in_tri_i(sw[3:0]), .cm3_swclk(swclk), .cm3_uart_rxd(uart_rxd), //outputs .cm3_gpio_out_tri_o(led[3:0]), .cm3_uart_txd(uart_txd), //inouts .cm3_swdio(swdio));endmodule //top_hdl end
5.9 管脚分配
综合(synthesis)完成之后,使用vivado的图形化工具进行管脚分配,尤其注意要将swdio和swdclk引出到排针管脚上,方便后续使用外接的jlink调试器进行arm程序下载。
分配管脚
或者直接新建xdc文件,使用约束语句进行管脚分配。
部分约束语句:
set_property package_pin r4 [get_ports clk]set_property package_pin v13 [get_ports swclk]set_property package_pin v14 [get_ports swdio]set_property package_pin e14 [get_ports uart_rxd]set_property package_pin d17 [get_ports uart_txd]set_property package_pin u7 [get_ports rst_n]set_property package_pin v9 [get_ports {led[3]}]set_property package_pin y8 [get_ports {led[2]}]set_property package_pin y7 [get_ports {led[1]}]set_property package_pin w7 [get_ports {led[0]}]set_property package_pin t4 [get_ports {key[3]}]set_property package_pin t3 [get_ports {key[2]}]set_property package_pin r6 [get_ports {key[1]}]set_property package_pin t6 [get_ports {key[0]}]
如果你的板子和我的(正点原子达芬奇pro)一样,那么可以直接使用以上管脚约束。
如果你分配的时钟管脚不是fpga的全局时钟管脚,需要添加bufg原语进行缓冲。
5.10 bit流文件生成和下载
我的板子使用的是qspi flash,为了提高下载和启动速度,在生成bit流时,配置生成选项:数据压缩、50m读取速度,4位数据线。
生成bit流配置
或者直接使用xdc语句进行约束:
set_property bitstream.general.compress true [current_design]set_property bitstream.config.configrate 50 [current_design]set_property config_voltage 3.3 [current_design]set_property cfgbvs vcco [current_design]set_property bitstream.config.spi_buswidth 4 [current_design]
以上约束不是必须的,只是为了提高下载和配置速度。
耐心等待工程综合完成,生成bit流文件,综合的速度和处理器主频、核心数有关。
和常规的fpga下载方式一样,将生成的软核bit文件通过xilinx下载器下载到fpga内部,先不要固化到外部spi flash 。
手头没有xilinx下载器的,可以参考之前的文章,自己做一个jtag-hs2下载器!
开源、低成本的xilinx fpga下载器
5.11 jlink连接测试
下载完成之后,现在fpga内部运行的就是一颗基于arm cortex-m3的软核处理器了,使用jlink等调试工具可以连接到芯片。
将jlink调试器的swclk和swdio连接到我们分配的管脚v13和v14上。
手头没有jlink的,也可以参考之前的文章,自己做一个jlink-ob!
手把手教你制作jlink-ob调试器
使用keil开发designstart cortex-m3软核的程序,需要先安装一个designstart专用的器件包。
下载地址如下:
https://keilpack.azureedge.net/pack/keil.v2m-mps2_dsx_bsp.1.1.0.pack
打开一个stm32 keil工程,器件修改为刚刚安装的arm ds_cm3,在option->debug-setting界面中选择swd方式,第一次连接会提示需要选择一个器件,这里选择cortex-m3:
选择器件型号
如果以上配置均正确,就能看到已经连接到的arm cortex-m3核心。如果没有,说明fpga工程配置有错误,需要确认是否和以上配置流程一致。
连接到arm核心
至此,arm cortex-m3软核基本搭建完成,接下来我们使用keil来编写arm核的程序,实现gpio和uart的控制。
6.cortex-m3软核程序设计
和常规的arm cortex-m3内核单片机开发流程类似,使用keil新建工程,源文件,根据外设使用手册,读写指定的寄存器实现gpio的控制,uart数据写入,编译下载,调试。
在之前创建的cortex_m3_on_xc7a100t文件夹下,新建mdk_prj文件夹,用于保存keil-mdk的工程,并新建以下3个文件夹:
application //用户源文件object //编译生成的文件project //keil的工程文件
6.1 新建keil工程
打开keil-mdk,选择project->new project,新建一个工程,命名为ds_cm3_prj,保存到project目录下。
keil工程目录
器件型号选择我们新安装的arm cortex-m3 ds_cm3内核。
选择器件型号
组件管理界面中,添加cmsis内核文件和startup启动文件:
添加内核文件
并按照如下结构组织文件:
文件结构
6.2 设置ram和rom地址
在工程选项中设置片上itcm的起始地址0x0、大小64k,片上dtcm起始地址0x20000000、大小64k:
ram地址配置
起始地址来源于使用手册中图4-1系统内存地址映射,可以看到其中itcm和dtcm的起始地址:
itcm和dtcm起始地址
大小是我们在cortex-m3内核配置中设置的大小:
itcm和dtcm大小
设置完成之后,新建main.c文件,输入以下内容,编译工程,应该无错误输出。
#include ds_cm3.h#include system_ds_cm3.hint main(void){ while(1) { }}
6.3 gpio输入输出控制
通过查看axi gpio的使用手册,通道1的数据寄存器偏移地址为0,通道2的数据寄存器偏移地址为0x08,根据vivado中的连接,led连接到通道1,按键连接到通道2上,所以只需要对这两个寄存器地址进行读写,就可以实现led的控制和拨码开关状态的读取。
axi gpio寄存器定义
在vivado地址分配界面,可以看到gpio和uart的基地址分别为:0x4000_0000和0x4060_0000。
外设基地址
lel控制和拨码开关读取:
*(volatile uint32_t *) (0x40000000+0x0) = 0x0f; //gpio通道1低4位写1*(volatile uint32_t *) (0x40000000+0x0) = 0x00; //gpio通道1低4位写0uint32_t sw = 0;sw = *(uint32_t *) (0x40000000+0x08); //获取gpio通道2的32位输入状态
6.4 串口数据发送和接收
向串口发送fifo写入一字节数据:
while((*(volatile uint32_t *)(0x40600000 + 0x08)) & 0x08 != 0x08); //等待发送fifo不满*(volatile uint32_t *) (0x40600000+0x04) = 0x41; //向串口发送fifo写入字符'a'=0x41
从串口接收一字节数据:
uint8_t dat = 0;if((*(volatile uint32_t *)(0x40600000 + 0x08)) & 0x01 == 1) //串口接收fifo中有数据 dat = (*(volatile uint32_t *)(0x40600000 + 0x00)); //从接收fifo中读取1字节数据。
关于axi gpio和axi uart寄存器的详细说明,可以查看官方文档:
pg144-axi-gpio.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf
pg142-axi-uartlite.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_uartlite/v2_0/pg142-axi-uartlite.pdf
6.5 延时函数实现
为了让led的变化,可以被人眼所看到,需要使用延时函数对亮灭进行延时。
使用系统滴答定时器实现一个延时函数:
volatile uint32_t cnt = 0; //volatile类型void systick_handler(void){ cnt++;}void delay_ms(uint32_t t){ cnt = 0; while(cnt-t>0);}
为了让延时函数准确延时,我们还需要更改工程中的系统时钟频率,和fpga中配置的内核时钟保持一致。
系统时钟
完成的main.c文件内容:
#include ds_cm3.h#include system_ds_cm3.h//c库#include #include #include #define baseaddr_led 0x40000000#define baseaddr_uart 0x40600000#define channel_led 1#define channel_sw 2#define xgpio_chan_offset 8#define xgpio_writereg(baseaddress, regoffset, data) xil_out32((baseaddress) + (regoffset), (uint32_t)(data))#define xgpio_readreg(baseaddress, regoffset) xgpio_in32((baseaddress) + (regoffset))#define xul_tx_fifo_offset 4 /* transmit fifo, write only */#define xul_status_reg_offset 8 /* status register, read only */#define xul_sr_tx_fifo_full 0x08 /* transmit fifo full */#define xuartlite_getstatusreg(baseaddress) xuartlite_readreg((baseaddress), xul_status_reg_offset)#define xuartlite_readreg(baseaddress, regoffset) xgpio_in32((baseaddress) + (regoffset))#define xuartlite_istransmitfull(baseaddress) ((xuartlite_getstatusreg((baseaddress)) & xul_sr_tx_fifo_full) == xul_sr_tx_fifo_full)#define xuartlite_writereg(baseaddress, regoffset, data) xil_out32((baseaddress) + (regoffset), (uint32_t)(data))volatile uint32_t cnt = 0;void systick_handler(void){ cnt++;}void delay_ms(uint32_t t){ cnt = 0; while(cnt-t>0);}uint32_t xgpio_in32(uint32_t addr){ return *(volatile uint32_t *) addr;}void xil_out32(uint32_t addr, uint32_t value){ volatile uint32_t *localaddr = (volatile uint32_t *)addr; *localaddr = value;}uint32_t xgpio_discreteread(uint32_t addr, uint8_t channel){ return xgpio_readreg(addr, (channel-1)*xgpio_chan_offset);}void xgpio_discretewrite(uint32_t addr, uint8_t channel, uint32_t data){ xgpio_writereg(addr, (channel-1)*xgpio_chan_offset, data);}void xuartlite_sendbyte(uint32_t baseaddress, uint8_t data){ while (xuartlite_istransmitfull(baseaddress)); xuartlite_writereg(baseaddress, xul_tx_fifo_offset, data);}void cm3_print(const char *ptr){ while (*ptr != (char)0) { xuartlite_sendbyte(baseaddr_uart, *ptr); ptr++; }}void myuartprintf(char *fmt,...){ unsigned char usartprintfbuf[296]; va_list ap; unsigned char *pstr = usartprintfbuf; va_start(ap, fmt); vsnprintf((char *)usartprintfbuf, sizeof(usartprintfbuf), (const char *)fmt, ap); va_end(ap); while(*pstr != 0) { xuartlite_sendbyte(baseaddr_uart, *pstr); pstr++; }}void led_blink(void){ xgpio_discretewrite(baseaddr_led, channel_led, 0); delay_ms(500); xgpio_discretewrite(baseaddr_led, channel_led, 0xf); delay_ms(500);}int main(void){ uint32_t sw = 0; systemcoreclockupdate(); systick_config(systemcoreclock/1000); cm3_print(hello designstart arm cortex-m3 on fpga xilnx artix-7 xc7a100t ); myuartprintf(systemcoreclock = %ld, systemcoreclock); while(1) { led_blink(); sw = xgpio_discreteread(baseaddr_led, channel_sw); myuartprintf(key state = %d-%d-%d-%d, sw>>3, sw>>2&1, sw>>1&1, sw&1); }}
实现的功能是,4颗led每100ms闪烁一次,同时串口输出此时拨码开关的实时状态。
编译无误后,就可以进行程序下载了。
6.6 flash编程算法生成
使用jlink下载程序需要指定flash编程算法,但是keil自带的算法中并没有我们所需要的:
下载算法
所以我们需要定制一份flash编程算法,打开keil安装目录下的armflash文件夹,将_template文件夹复制出一份,并命名为ds_cm3,
复制模板
打开其中的keil工程:
下载算法
这个工程可以自己设置要编程的flash起始地址、大小,擦除大小等。
flashdev.c文件填入以下内容,和我们之前itcm的配置保持一致,起始地址0x0,大小64k:
#include ..flashos.h // flashos structuresstruct flashdevice const flashdevice = { flash_drv_vers, // driver version, do not modify! mycm3onfpga, // device name onchip, // device type 0x00000000, // device start address 0x00010000, // 修改为64kb 1024, // programming page size 0, // reserved, must be 0 0xff, // initial content of erased memory 100, // program page timeout 100 msec 3000, // erase sector timeout 3000 msec// specify size and address of sectors 0x010000, 0x000000, // 只有一个扇区,起始地址为0 sector_end};
flashprg.c文件,实现一些存储区擦除的函数:
#include ..flashos.h // flashos structures#include string.hint init (unsigned long adr, unsigned long clk, unsigned long fnc) { return (0); // finished without errors}int uninit (unsigned long fnc) { return (0); // finished without errors}int erasechip (void) { memset((unsigned char *)0, 0, 0x10000); return (0); // finished without errors}int erasesector (unsigned long adr) { memset((unsigned char *)adr, 0, 1024); return (0); // finished without errors}int programpage (unsigned long adr, unsigned long sz, unsigned char *buf) { memcpy((unsigned char *)adr, buf, sz); return (0); // finished without errors}编译无误后,会在工程目录下生成一个flm文件。新生成的下载算法
将它复制到上一级目录:
新生成的下载算法
6.7 编译下载运行
再打开我们的arm核keil工程,添加ds_cm3 flash编程算法:
添加flash编程算法
点击下载按钮,把arm程序下载到arm核:
43
可以看到led每500ms闪烁一次,串口数据每1s输出一次,同时按下按键,串口输出按键的状态。
43
和其他arm内核芯片一样,也是支持在线调试的:
43
由于arm程序是下载到cortex-m3软核内的ram存储区,所以掉电后程序会丢失。如何将程序下载到片外的spi flash中,我还没有成功实现。
7.开源地址
本篇文章的pdf文件,vivado工程,keil工程,keil器件支持 包,flash编程算法文件,外设ip的参考文档,arm m3软核ip资料包等资料我已经开源到github和gitee,地址如下:
gitee
git clone https://gitee.com/whik/cortex_m3_on_xc7a100t.git
github
git clone https://github.com/whik/cortex_m3_on_xc7a100t.git
黑芝麻智能携手伙伴打造自动驾驶核心芯片 推动中国智能驾驶发展领先全球
5G的发展将为移动转售产业赋予新动能提供新商机
“半导体战争”必然激化 “中国制造”稳步发展
永磁同步电机数字交流伺服控制技术
一起来学5G终端射频标准(DMRS的EVM)
如何定制一颗ARM Cortex-M3 SoC软核
光通信和云计算大数据展
华为鸿蒙操作系统大曝光
奇偶校验码,奇偶校验码原理是什么?
小米再次回购股份,两此共计约1.6亿港元
芯源微取得晶圆用热处理设备专利
不同时间的AI行业的发展有什么不同
集成温度传感器的产品分类及发展趋势
贸泽开售Analog Devices LTC6228和LTC6229运算放大器
厚物科技PXIe机箱PXI机箱PXIe笔记本HW-1363
医疗设备对电子元器件(连接器)的技术要求
颜值与性能并存,一款非常适合侧透机箱的显卡
MQTT边缘计算网关实现多个工业设备的数据传输和通信
LFP-JJY5RR-305T4无铅高温锡膏的特点有哪些?
多个APP将增设老年人“一键叫车”功能