当前位置: 首页 > wzjs >正文

英文网站模板cms网络服务费计入什么科目

英文网站模板cms,网络服务费计入什么科目,化妆品网站设计系统需求的策划书,wordpress登陆错误博主在做SoC芯片级验证编写c语言测试激励的时候发现printf函数都被一个自定义的打印函数给替代了,所以就学习了一下这些函数的实现过程及为什么要在SoC仿真环境中自定义打印函数而不是直接调用c标准库的printf函数。下面先介绍我做过的一个arm核和一个riscv核两个So…

博主在做SoC芯片级验证编写c语言测试激励的时候发现printf函数都被一个自定义的打印函数给替代了,所以就学习了一下这些函数的实现过程及为什么要在SoC仿真环境中自定义打印函数而不是直接调用c标准库的printf函数。下面先介绍我做过的一个arm核和一个riscv核两个SoC项目中自定义printf函数的实现方式。

1.arm核SoC仿真环境中的自定义a_printf函数

a_printf 函数使用了可变参数(...)来模拟标准库的 printf 功能,其中va_list,va_endva_start是处理​​可变参数函数​​(类似printf)的核心宏,用于访问不确定数量的函数参数。

va_list​:声明一个指针,用于遍历可变参数列表

va_start​:初始化va_list,使其指向​第一个可变参数​​

va_end :​​​​用于清理 va_list 指针,结束可变参数的访问​​,防止内存或资源泄漏(必须和 va_start 配对使用)

a_printf函数的核心是将格式化逻辑委托给 vprintfmt 函数(下面会讲),并通过强制转换的 putchar_custom 回调函数实现​​输出目标定制​(例如输出到仿真器控制台、串口或内存缓冲区)。

void a_printf(const char* fmt, ...) 
{// 1. 初始化可变参数列表va_list ap;       // 定义可变参数指针(通常实现为char*)va_start(ap, fmt); // 将ap指向fmt之后的第一个参数(通过栈指针或寄存器定位)// 2. 调用核心格式化引擎// 参数说明://   (void*)putchar_custom - 将字符输出函数强制转换为泛型指针//   0                     - 输出设备的上下文(未使用)//   fmt                   - 格式化字符串//   ap                    - 可变参数列表vprintfmt((void*)putchar_custom, 0, fmt, ap);// 3. 清理可变参数列表va_end(ap); 
}

 vprintfmt 函数实现了一个​​轻量级格式化输出引擎​​,其核心功能是解析类似printf的格式字符串(如%d%s),通过回调函数putch逐字符输出结果,支持整数(十进制/十六进制/八进制)、字符、字符串等基础格式化,并允许控制对齐、填充、宽度等格式,专为​​嵌入式或仿真环境​​设计,避免了标准库printf的资源开销和硬件依赖,可直接重定向输出到自定义设备(如仿真器控制台或内存映射IO),vprintfmt 函数代码比较复杂,大致了解作用就行。

/*** 格式化输出核心函数(类似标准库的vprintf)* @param putch  字符输出回调函数,参数为(int字符, void*用户数据)* @param putdat 传递给putch的额外数据* @param fmt    格式字符串(如"%d %s")* @param ap     可变参数列表*/
static void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
{register const char* p;         // 临时字符串指针const char* last_fmt;           // 记录格式字符串回溯位置register int ch, err;           // 当前字符和错误码(寄存器优化)unsigned long long num;         // 存储数值参数int base, lflag, width, precision, altflag; // 格式控制参数char padc;                      // 填充字符(空格或0)// 主循环:处理整个格式字符串while (1) {// 处理普通字符(非'%'部分)while ((ch = *(unsigned char *) fmt) != '%') {if (ch == '\0') return;     // 遇到字符串结束符则退出fmt++;                      // 移动格式字符串指针putch(ch, putdat);          // 输出当前字符}fmt++; // 跳过'%'字符/* 开始处理格式化指令(如%-08d中的符号、宽度等)*/last_fmt = fmt;               // 记录当前位置(用于错误回溯)padc = ' ';                   // 默认填充空格width = -1;                   // 默认无宽度限制precision = -1;               // 默认无精度限制lflag = 0;                    // 默认非long类型altflag = 0;                  // 默认无替代格式(如0x前缀)reswitch:// 解析格式控制字符switch (ch = *(unsigned char *) fmt++) {/* 标志位处理('-', '0', '#'等)*/case '-': padc = '-'; goto reswitch; // 左对齐case '0': padc = '0'; goto reswitch; // 用0填充case '#': altflag = 1; goto reswitch; // 替代格式(如0x)/* 宽度处理(数字或*)*/case '1'...'9': // 数字宽度(如%10d)for (precision = 0; ; ++fmt) {precision = precision * 10 + ch - '0';ch = *fmt;if (ch < '0' || ch > '9') break;}goto process_precision;case '*': // 动态宽度(如%*d)precision = va_arg(ap, int);goto process_precision;case '.': // 精度开始标记if (width < 0) width = 0;goto reswitch;/* 类型长度修饰(l)*/case 'l':lflag++; // long/long long标志goto reswitch;/* 具体数据类型处理 */case 'c': // 字符(%c)putch(va_arg(ap, int), putdat);break;case 's': // 字符串(%s)if ((p = va_arg(ap, char *)) == NULL)p = "(null)"; // NULL保护// 处理对齐和填充if (width > 0 && padc != '-') // 右侧填充for (width -= strnlen(p, precision); width > 0; width--)putch(padc, putdat);// 输出字符串内容for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); p++)putch(ch, putdat);// 左侧填充(左对齐时)for (; width > 0; width--)putch(' ', putdat);break;/* 数值类型处理 */case 'd': // 有符号十进制(%d)num = getint(&ap, lflag);if ((long long)num < 0) { // 处理负数putch('-', putdat);num = -(long long)num;}base = 10;goto signed_number;case 'u': // 无符号十进制(%u)base = 10;goto unsigned_number;case 'o': // 八进制(%o)base = 8;goto unsigned_number;case 'p': // 指针(%p)lflag = 1; // 强制long类型putch('0', putdat); // 输出0x前缀putch('x', putdat);/* 继续执行x的case */case 'x': // 十六进制(%x/%X)base = 16;unsigned_number:num = getuint(&ap, lflag); // 获取无符号数signed_number:printnum(putch, putdat, num, base, width, padc); // 实际数字打印break;case '%': // 转义%%输出%putch(ch, putdat);break;default: // 未知格式指令putch('%', putdat); // 原样输出%fmt = last_fmt;     // 回溯到格式开始位置break;}}
}

 2.riscv核SoC仿真环境中的自定义tb_printf函数

tb_printf函数和上面的a_printf函数类似,代码如下

int tb_printf(char *fmt, ...)
{char    buf[1024];  // 输出缓冲区(栈空间,固定1024字节)char    *p;         // 缓冲区指针va_list args;       // 可变参数列表int     n = 0;      // 返回值(当前未使用,始终返回0)/ /初始化可变参数列表 va_start(args, fmt); // 将 args 指向第一个可变参数// 格式化字符串处理 // 将格式化后的字符串写入 buf,返回实际长度(未使用返回值)ee_vsprintf(buf, fmt, args); //清理可变参数列表va_end(args); // 结束可变参数访问//准备输出p = buf; // 让指针指向格式化后的字符串起始位置//调用汇编级输出函数// 将缓冲区内容通过底层硬件接口输出(如串口/UART)asm_print(p);return n; // 返回0
}

其中调用的asm_print是使用riscv汇编函数写的,代码如下可供参考

.globl asm_print                //伪指令​​,用于声明一个符号(通常是函数或变量名)为​​全局可见​​的,使得该符号可以被其他文件或模块访问
asm_print:nop                     // 对齐或调试占位
asm_contex_save:ASM_CONTEX_SAVE()       // 保存调用者寄存器上下文mv      s1, a0          // s1 = 字符串起始地址(a0为第一个参数)
asm_print_body:lw      s2, 0x0(s1)     // 加载32位字到s2li      s3, chip_base | print_addr  // s3 = 硬件输出地址sw      s2, 0x0(s3)     // 将32位字写入硬件addi    s1, s1, 4       // 指针后移4字节andi    s4, s2, 0xff    // 检查字节0(位7:0)beqz    s4, asm_print_endsrli    s2, s2, 8       // 右移8位andi    s4, s2, 0xff    // 检查字节1(位15:8)beqz    s4, asm_print_endsrli    s2, s2, 8       // 右移8位andi    s4, s2, 0xff    // 检查字节2(位23:16)beqz    s4, asm_print_endsrli    s2, s2, 8       // 右移8位andi    s4, s2, 0xff    // 检查字节3(位31:24)beqz    s4, asm_print_endj       asm_print_body  // 继续循环asm_print_end:nop                     // 对齐或调试占位
asm_contex_restore:ASM_CONTEX_RESTORE()    // 恢复调用者寄存器ret                     // 函数返回

asm_print函数 通过32位字批量写入方式将字符串内容输出到指定的硬件地址(如串口,UART)。其特点是每次读取4字节数据写入硬件,并逐字节检测NULL终止符;通过保存/恢复寄存器上下文保证函数安全性,适用于嵌入式系统或内核早期的低层调试输出。

在实际使用中虽然tb_printf函数可以带参数打印信息,而asm_print函数仅能打印字符串,但asm_print函数仿真速度很快,所以我一般用asm_print函数。

3.为什么SoC仿真要自定义printf?​

  1. ​硬件无关性​
    标准printf依赖UART/OS驱动,仿真环境可能没有真实硬件。自定义函数直接输出到仿真器控制台或日志。

  2. ​节省资源​
    标准库printf代码大(含浮点等),自定义版可精简到几百字节,适合Flash/ROM受限的嵌入式场景。

  3. ​确定性输出​
    仿真需要严格时序同步,标准printf可能有缓冲延迟,自定义函数确保即时输出。

  4. ​扩展功能​
    可添加仿真专用功能,如自动打时间戳、非标准格式(%r打印寄存器值)。

  5. ​裸机兼容性​
    无OS环境(如Bootloader)可能无C库支持,自定义printf不依赖运行时。

  6. ​安全可靠​
    避免标准库潜在的缓冲区溢出,自定义实现可严格限制输出长度,防止崩溃。

http://www.dtcms.com/wzjs/233189.html

相关文章:

  • 什么网站可以看到绵阳建设子域名在线查询
  • 建站经验网络营销服务的内容
  • 网站一键备份推广团队
  • 免费快速建站网站大型网站制作
  • 网站建设公司首选华网天下seo推广技巧
  • 海口做网站的公司关联词有哪些关系
  • 电子商务企业网站建设规划方案sem和seo区别与联系
  • 个人备案可以做盈利网站吗自己怎么做百度推广
  • 福州网站建设新闻怎么建自己的网站?
  • 中国百强企业免费网站优化排名
  • 从化定制型网站建设十大技能培训机构排名
  • 做网站可以用.cn域名吗如何自己免费制作网站
  • 平面设计是干什么的工资一般多少快手seo关键词优化
  • 炒币做合约哪个网站最好微信朋友圈广告推广
  • 自助建站软件公司友情链接属于免费推广吗
  • 展会展台搭建服务seo关键词优化公司
  • 河南建设seo工程师
  • 爬虫做网站相关教程app香港账号
  • 销售新网站推广策略新媒体营销案例ppt
  • 上海网站建设做物流一网络营销企业网站推广
  • 淘宝有做钓鱼网站的吗做关键词优化的公司
  • 怎样做汽车之家视频网站网上推广产品哪个网好
  • 济南住房和城乡建设局网站公司宣传软文
  • 大型网站服务器架构威海seo
  • 史上最全设计网站seo刷词工具在线
  • 郑州餐饮网站建设公司排名模板下载网站
  • 专门做衣服的网站有哪些微信推广方案
  • 网站设计书怎么写最近三天的国内新闻
  • 网页升级紧急通知写作全网搜索引擎优化
  • 网站首页动图怎么做搜索seo