cs61c-proj2
Project 2: CS61Classify
cs61c - Great Ideas in Computer Architecture (Machine Structures) proj2 笔记
课程主页:https://inst.eecs.berkeley.edu/~cs61c/fa20/#by-week
个人仓库:https://github.com/2333monster/my-cs61c-proj
注:是sp22 的proj2
Part A: Math Functions
完成
- abs
- dot product
- matrix multiplication
- an element-wise rectifier function (ReLU)
- an argmax function for vectors.
Task 1: Absolute Value (Walkthrough)
熟悉工作流
Running Tests
在linux 环境下,进行操作即可
1 |
|
Using VDB to debug tests via Venus
根据the setup section of the spec 设置venus,注意在linux 下即可
在/src/abs.s 中添加ebreak
1 |
|
在terminal 中,运行
1 |
|
然后再simulator 中debug就好,发现错误后(大于0但是取了相反数),修改
1 |
|
Task 2: ReLU
1 |
|
Task 3: Argmax
1 |
|
Task 4: Dot Product
1 |
|
Task5: Matrix Multiplication
做了好久,刚开始写确实很乱,静下心梳理。
1 |
|
代码思路
- 首先,代码进行维度检查,确保输入矩阵的维度是有效的,如果任何一个维度小于1,则跳转到
exit_38
标签,终止程序。 - 然后,代码初始化一些寄存器和变量,为循环做准备。
s0
用于追踪当前行,s1
用于追踪当前列。 - 进入外层循环
outer_loop_start
,其中s0
追踪矩阵m0
的当前行。然后,进入内层循环inner_loop_start
,其中s1
追踪矩阵m1
的当前列。 - 在内层循环内,首先保存寄存器上下文,然设置
dot
函数的参数。a0
不变,计算a1
的地址(a1
是矩阵m1
当前列的起始地址),a2
不变,设置a3
为 1,将a4
设置为矩阵m1
的列数。 - 调用
dot
函数,将矩阵m0
的当前行和矩阵m1
的当前列进行点积运算,结果保存在t1
中。 - 恢复寄存器上下文,然后将
t1
的值存储在结果矩阵d
中的对应位置。 - 内层循环结束后,增加
s1
的值,如果s1
小于矩阵m1
的列数,则继续内层循环。 - 外层循环结束后,增加
s0
的值,然后将a0
更新为下一行的起始地址。如果s0
小于矩阵m0
的行数,则继续外层循环。 - 循环结束后,恢复寄存器上下文并返回。
- 如果发现维度无效,则跳转到
exit_38
,程序终止。
计算a1地址
1 |
|
计算结果存储在结果矩阵d 的相对位置
1 |
|
mul t2, s0, a5
: 这行代码计算了要存储的元素在结果矩阵d
中的行号。s0
包含了当前正在计算的行号,而a5
包含了矩阵m1
的列数(也是结果矩阵d
的列数)。将这两个值相乘,得到的结果存储在寄存器t2
中。add t2, t2, s1
: 这行代码将刚刚计算的行号t2
与当前正在计算的列号s1
相加。这是为了计算出在结果矩阵d
中的具体位置。addi t4, x0, 4
: 这行代码将立即数 4 存储在寄存器t4
中。这是因为每个元素占用 4 个字节(假设是 32 位整数)。mul t2, t2, t4
: 这行代码将刚刚计算的位置t2
与每个元素的大小相乘,以得到字节偏移量。add t2, a6, t2
: 这行代码将字节偏移量t2
与结果矩阵d
的起始地址相加,从而得到最终要存储的地址。sw t1, 0(t2)
: 这行代码使用sw
指令,将点积运算的结果t1
存储在刚刚计算出的地址上,从而将计算结果存储在结果矩阵d
中的对应位置。
更新矩阵a0 的行指针,以便在下一次循环中处理下一行
1 |
|
addi t4, x0, 4
: 这行代码将立即数 4 存储在寄存器t4
中。这是因为每个元素占用 4 个字节(假设是 32 位整数)。mul t0, t4, a2
: 这行代码将刚刚存储在寄存器t4
中的值(即每个元素的大小)与矩阵m0
的列数a2
相乘,得到的结果存储在寄存器t0
中。这是为了计算当前行中下一个元素的偏移量。add a0, a0, t0
: 这行代码将刚刚计算出的偏移量t0
加到矩阵m0
的起始地址a0
上,以更新行指针。这将使指针指向下一行的开头,以便在下一次循环中处理下一行
Part B: File Operations and Classification [09/14]
Task 7: Read Matrix
Matrices are stored in files as a consecutive sequence of 4-byte integers. The first and second integers in the file indicate the number of rows and columns in the matrix, respectively. The rest of the integers store the elements in the matrix in row-major order.
Fill in the read_matrix
function in src/read_matrix.s
. This function should do the following:
- 以读取权限打开文件。 文件路径作为参数 (a0) 提供。
- 从文件中读取行数和列数(记住:它们是文件中的前两个整数)。 将这些整数存储在内存中提供的指针处(a1 表示行,a2 表示列)。
- 在堆上分配空间来存储矩阵。 (提示:使用上一步中的行数和列数来确定要分配多少空间。)
- 将矩阵从文件中读取到上一步分配的内存中。
- 关闭文件。
- 返回指向内存中矩阵的指针。
参数要求:https://inst.eecs.berkeley.edu/~cs61c/sp22/projects/proj2/part-b/#your-task
c program
1 |
|
risc-v code
1 |
|
Task 8: Write Matrix
Fill in the write_matrix
function in src/write_matrix.s
. This function should do the following:
- 以写权限打开文件。 文件路径作为参数提供。
- 将行数和列数写入文件。 (提示:fwrite 函数需要一个指向内存中数据的指针,因此您应该首先将数据存储到内存,然后将指向数据的指针传递给 fwrite。)
- 将数据写入文件。
- 关闭文件。
参数要求:https://inst.eecs.berkeley.edu/~cs61c/sp22/projects/proj2/part-b/#task-8-write-matrix
c program
1 |
|
risc-v code
1 |
|
Task 9: Classify
最后一部分,写了两个晚上,将近8个小时左右
Fill in the classify
function in src/classify.s
. This function should do the following:
- 从文件中读取三个矩阵 m0、m1 和输入。 它们的文件路径作为参数提供。 您需要为 read_matrix 的指针参数分配空间,因为该函数需要一个指向已分配内存的指针。
- 计算 h = matmul(m0, 输入)。 您可能需要 malloc 空间以适合 h。
- 计算 h = relu(h)。 请记住,relu 是就地执行的。
- 计算 o = matmul(m1, h) 并将结果矩阵写入输出文件。 输出文件路径作为参数提供。
- 计算并返回argmax(o)。 如果 print 参数设置为 0,则还打印出 argmax(o) 和换行符。
- 释放使用 malloc 分配的所有数据。 这包括通过调用 read_matrix 分配的任何堆块。
- 请记住在返回之前将返回值 argmax(o) 放入适当的寄存器中。
参数要求:https://inst.eecs.berkeley.edu/~cs61c/sp22/projects/proj2/part-b/#task-9-classify
c program,大体的一些,为了满足汇编函数的参数有些写得有些模糊
1 |
|
risc-v code
1 |
|