Skip to content

CS107E Lab2

实验1,查看把C编译成汇编的样子

没有 codegen.c , 搞不了

搞到了旧版 https://gcc.godbolt.org/z/7YYczMjes

%hi(global) 是什么?

%lo(global) 和 hi 差不多?

  • 是的
  • 获取全局符号的地址的低12位部分
  • 即获相对于 %hi(global) 这个地址的偏移量
  • 合在一起 %lo(global)(a3) 就是指向 global 的地址

为什么要这么写?

  • 教材使用的是 32bit 的 RISC-V,地址长度为 32bit
  • global 地址下存储的是4个字节的0,即一个 int 类型,大小是一个 word
  • 从一个内存地址中读一个 word 使用指令 LW rd, imm12(rs1)
  • 需要将以上 im12(rs1)global 对应起来

所以就是套路?

  • 是的
  • 都这么读全局变量

num = -num;怎么没了

  • 在下面
  • neg a5, a5sub a5, zero, a5
  • 顺序变了不影响结果
  • 只能认为,编译器觉得这样比较好

num = 5 * num; 怎么变了

  • num 逻辑左移 2 位,即 num * 2^2
  • 再相加 num * 4 + num = num * 5
  • 结果是一样的
  • 只能认为,编译器觉得这样比较好

下一条好长

  • 一步一步来
  • -1 << 4 即 0xffffffff << 4 即 0xfffffff0
  • 取反得 0x0000000f 即 15
  • num 是 0 , num & 15 等价于 num + 15
  • (12*18 - 1)/2 = 107.5,因为int类型,编译器省略的小数点,得 107
  • 最后用了乘法指令 mul

那个 & 等价 + 过于奇怪了

  • 只能认为,编译器就是这么神奇

修改成 int num = 3; 怎么样

  • 编译器直接把值算出来了
  • (-15 & 15) * (107 - 15) = (0xfffffff1 & 0x0000000f) * 92 = 92

partb 里面,if else 在不同优化条件下汇编指令不一样

  • -Og 更符合直觉
  • -O2 跳转更少,可以理解为性能更好

partc里面,-O2 就啥都没了?

  • 啊,不理解,往下看吧

partd 指针,脑袋要爆炸

  • 一步一步来
  • int n = global;
    • a4 存 global 高位地址
    • a5 存 global 值 g1
  • *prt = val
    • global 地址里值更改为 g2 = 107
    • a2 存 107
  • *(char *)ptr = val
    • 107 在 char 范围 -128 ~ 127 之间,忽略
  • ptr[10] = val
    • 因为 prt 是 int * 类型,所以地址偏移量是 10 * 4
    • a3 存 global 完整地址
  • cptr[10] = val
    • 因为 cptr 是 char * 类型,所以偏移量 10 * 1
    • sb , Store byte
    • cptr 存储的指针指向地址0,所以使用 10(zero)
  • n = *(ptr + n);
    • a5 存 n 的值
    • ptr 偏移 n,需要地址 + 4 * n,即 a3 + a5 << 2
    • a5 存从内存拿到的 n 的值
  • n = ptr[n]
    • 和上一句汇编内容相同

main

  • 对 sp 和 ra 的操作是用来调函数用的
  • O2 里面把 part a 和 b 直接放到 main 里面了
  • O2 用到了 tail 尾调用

到最后都没看到 partc

  • -Og 里面也没有调用 partc
  • 被优化掉了
  • 这种延时写法不符合规范

🤯

  • 休息一会儿

实验2,makefile

当自己会了,不搞

实验3,测试用例

  • 头文件里有assert.h,但需要串口。暂时不能用
  • 没有 testing 文件夹搞不了

芒果派ACT LED 连接的是 LCD_PWM,对应GPIO_PD18,见 https://mangopi.org/_media/mq-pro-sch-v12.pdf

写个 gpio_set_output

  • GPIO offset 从 0x0030 开始,
  • PB 偏移量是 0x0030,PB到PC到PD 每次 多 0x30
  • CFG0 从 0x00 开始,DAT 是 CFG0 + 0x10
  • 课程代码 gpio_id 是 0xNnn 格式的
    • 选择组是 0x0030 + N * 0x30
    • 选择CFG 的地址偏移量是 (nn / 8) * 4 + 上面地址
    • 设置CFG 的值0x1 << ((nn % 8) * 4)
    • 例如 GPIO_PD18 = 0x212
      • 组地址偏移量是 0x0030 + (GPIO_PD18 >> 8) * 0x30 = 0x90PD
      • CFG地址偏移量是 (0xff & GPIO_PD18) / 8 * 4 + 0x90 = 0x98PD_CFG2
      • CFG 寄存器设置的值是 0x1 << (((0xff & GPIO_PD18) % 8) * 4)= 0x100PD18 设置为 output
c
void gpio_set_output(gpio_id_t pin) {
    unsigned int base_address = 0x30 + (pin >> 8) * 0x30 + GPIO;
    volatile unsigned int *CFG = (unsigned int *) ( ( 0xff & pin ) / 8 * 4 + base_address );
    unsigned int offset = ((0xff & pin) % 8) * 4;
    *CFG = (*CFG & ~(0xf << offset)) | (1 << offset);
}

实验4,显示数字的电路

先连一路看数字LED是否正正常

image-20240126140129870

连上电路手动做测试

lab2-1

连上Pi

image-20240126140637065

Pi编程测试

按灯的顺序

GPIO
D1PB4
D2PB3
D3PB2
D4PC0
APD17
BPB6
CPB12
DPB11
EPB10
FPE17
GPB0
DPPD22

按GPIO的顺序

GPIO
PB0G
PB2D3
PB3D2
PB4D1
PB6B
PB10E
PB11D
PB12C
PC0D4
PD17A
PD22DP
PE17F

将以上管脚都设置为输出高电平

bash
# PB_CFG0 0000 0001 0000 0001 0001 0001 0000 0001
xfel write32 0x02000030 0x1011101
# PB_CFG1 0000 0000 0000 0001 0001 0001 0000 0000
xfel write32 0x02000034 0x11100
# PB_DAT  0000 0000 0000 0000 0001 1100 0101 1101
xfel write32 0x02000040 0x1c5d
# PC_CFG0 0000 0000 0000 0000 0000 0000 0000 0001
xfel write32 0x02000060 0x1
# PC_DAT  0000 0000 0000 0000 0000 0000 0000 0001
xfel write32 0x02000070 0x1
# PD_CFG2 0000 0001 0000 0000 0000 0000 0001 0000
xfel write32 0x02000098 0x1000010
# PD_DAT  0000 0000 0100 0010 0000 0000 0000 0000
xfel write32 0x020000a0 0x420000
# PE_CFG2 0000 0000 0000 0000 0000 0000 0001 0000
xfel write32 0x020000c8 0x10
# PE_DAT  0000 0000 0000 0010 0000 0000 0000 0000
xfel write32 0x020000d0 0x20000

没问题的话就是全亮

image-20240126141345394