# Adapt [QR code generation](https://www.nayuki.io/page/creating-a-qr-code-step-by-step) program on [lab3](https://github.com/sysprog21/ca2023-lab3) contributed by < [`KuanYuan0530`](https://github.com/KuanYuan0530/Computer_Architecture_2023/tree/hw1/ca2023-final%20term) > ## Summarize I'm adapt qrcode.c on the MyCPU created in Lab3, primarily adding print functionality in Lab3, and converting qrcode.c to .asmbin to make it executable on MyCPU. However, I encountered some issues caused by printf, so I need to modify qrcode.c and the printing method. ## Construct MyCPU > Reference from [Lab3: Construct a single-cycle RISC-V CPU with Chisel](https://hackmd.io/@sysprog/r1mlr3I7p#Chisel-Tutorial) Follow Lab3 to implement a single-cycle RISC-V CPU, we have already implemented it in assignment3. ## Implement ECALL (a7=64) > Reference from [eeeXun](https://hackmd.io/@KXkA4u0LQuyNTwOorDw2RA/HyNM1bP4p) To enable MyCPU to encode ECALL instructions, we need to modify the `InstructionDecode.scala` file by adding an `ecall_flag`. When the opcode of the instruction is the ECALL opcode, the flag should be turn on. :::spoiler **InstructionDecode.scala** ```diff! class InstructionDecode extends Module { val io = IO(new Bundle { + + val ecall_flag = Output(Bool()) }) + + io.ecall_flag := (opcode === InstructionsEnv.ecall) } ``` ::: Reference from [RISC-V calling conventions](https://github.com/sysprog21/rv32emu/blob/master/docs/syscall.md#risc-v-calling-conventions), we can determine that when executing the `ecall` instruction, parameters will be mapped from registers `a0`, `a1`, `a2`, and `a7`. Therefore, we need to add the `ecall_a0`, `ecall_a1`, `ecall_a2`, and `ecall_a7` to the `RegisterFile.scala` files to represent the data in the registers. :::spoiler **RegisterFile.scala** ```diff! class RegisterFile extends Module { val io = IO(new Bundle { + + val ecall_a0 = Output(UInt(Parameters.DataWidth)) + val ecall_a1 = Output(UInt(Parameters.DataWidth)) + val ecall_a2 = Output(UInt(Parameters.DataWidth)) + val ecall_a7 = Output(UInt(Parameters.DataWidth)) }) + + io.ecall_a0 := registers(Registers.a0.id.U) + io.ecall_a1 := registers(Registers.a1.id.U) + io.ecall_a2 := registers(Registers.a2.id.U) + io.ecall_a7 := registers(Registers.a7.id.U) ``` ::: In addition, due to the aforementioned modifications, changes are also required in `CPU.scala`, `CPUBundle.scala` and `Top.scala`. :::spoiler **CPU.scala** ```diff! + +import riscv.core.RegisterFile class CPU extends Module { + + io.ecall_flag := id.io.ecall_flag + io.ecall_a0 := regs.io.ecall_a0 + io.ecall_a1 := regs.io.ecall_a1 + io.ecall_a2 := regs.io.ecall_a2 + io.ecall_a7 := regs.io.ecall_a7 } ``` ::: :::spoiler **CPUBundle.scala** ```diff! class CPUBundle extends Bundle { + + val ecall_flag = Output(Bool()) + val ecall_a0 = Output(UInt(Parameters.DataWidth)) + val ecall_a1 = Output(UInt(Parameters.DataWidth)) + val ecall_a2 = Output(UInt(Parameters.DataWidth)) + val ecall_a7 = Output(UInt(Parameters.DataWidth)) } ``` ::: :::spoiler **Top.scala** ```diff! class Top extends Module { val io = IO(new CPUBundle) val cpu = Module(new CPU) + + io.ecall_flag := cpu.io.ecall_flag + io.ecall_a0 := cpu.io.ecall_a0 + io.ecall_a1 := cpu.io.ecall_a1 + io.ecall_a2 := cpu.io.ecall_a2 + io.ecall_a7 := cpu.io.ecall_a7 } ``` ::: Next, we run the simulation, and from the waveform, it is evident that MyCPU is indeed capable of decoding ECALL instructions. ![擷取](https://hackmd.io/_uploads/HJS2n1-Fp.jpg) Furthermore, even though MyCPU can decode ECALL instructions, it doesn't automatically print. Therefore, we need to modify `verilog/verilator/sim_main.cpp`. During simulation, when the `ecall_flag` is turned on, we need to check the data in register `a7` to determine the specific ECALL. If `ecall_a7` is equal to 64, printing should be executed. ## Run [QR code generation](https://github.com/sysprog21/rv32emu/blob/master/tests/qrcode.c) code ON MyCPU In order to execute `qrcode.c` on MyCPU, we need to convert `qrcode.c` to `qrcode.asmbin`. First, place qrcode.c in the csrc folder and modify the `Makefile` to include qrcode.asmbin. Then, execute the following commands: ```shell ~/ca2023-lab3/csrc$ make update ``` ### Problem When executing the above command, the following error occurs. The issue arises when we run `riscv-none-elf-ld -o qrcode.elf -T link.lds --oformat=elf32-littleriscv qrcode.o init.o`. The linker needs to link the C standard library, but I cannot find the description for riscv-none-elf-ld. Therefore, I am unable to determine which options to add for linking. ```shell riscv-none-elf-as -R -march=rv32i_zicsr -mabi=ilp32 -o init.o init.S riscv-none-elf-gcc -O0 -Wall -march=rv32i_zicsr -mabi=ilp32 -c -o qrcode.o qrcode.c riscv-none-elf-ld -o qrcode.elf -T link.lds --oformat=elf32-littleriscv qrcode.o init.o riscv-none-elf-ld: qrcode.o: in function `dump_bmp': qrcode.c:(.text+0xdc8): undefined reference to `printf' riscv-none-elf-ld: qrcode.c:(.text+0xdf4): undefined reference to `putchar' riscv-none-elf-ld: qrcode.c:(.text+0xe0c): undefined reference to `printf' riscv-none-elf-ld: qrcode.c:(.text+0xe44): undefined reference to `printf' riscv-none-elf-ld: qrcode.c:(.text+0xe58): undefined reference to `printf' riscv-none-elf-ld: qrcode.c:(.text+0xe88): undefined reference to `puts' riscv-none-elf-ld: qrcode.c:(.text+0xec0): undefined reference to `printf' riscv-none-elf-ld: qrcode.c:(.text+0xeec): undefined reference to `putchar' riscv-none-elf-ld: qrcode.o: in function `main': qrcode.c:(.text+0xf30): undefined reference to `strlen' riscv-none-elf-ld: qrcode.c:(.text+0xf6c): undefined reference to `puts' make: *** [Makefile:19: qrcode.elf] Error 1 rm init.o ``` Therefore, I have modified it to avoid using the `printf` instruction. Reference from [kc71486](https://hackmd.io/@kc71486/computer_architecture_hw3),through the method of writing data to be printed into VRAM, the terminal will subsequently refresh the area based on the data stored in VRAM ## Modify the method of implementing ECALL (a7=64) > Reference from [kc71486](https://hackmd.io/@kc71486/computer_architecture_hw3) A similar process has been implemented with above to enable MyCPU to decode ECALL instructions. The main modifications are in the `sim_main.cpp` file, where an `InterruptHandler` class, `Printer` class, and `VRAM` class have been introduced. The primary process here is that when the instruction is decoded with the ECALL opcode, the InterruptHandler determines the specific type of ECALL. However, at this point, only the functionality of print (a7=64) has been implemented. Subsequently, the Printer can execute different print operations, such as `print`, `newline`, or `clear screen`. Finally, the VRAM writes data and cout. :::spoiler **VRAM class** ```c= class VRAM { private: std::vector<uint32_t> vram; bool vram_dirty = true; public: static constexpr uint32_t ROWS = 50; static constexpr uint32_t COLS = 200/4; static constexpr uint32_t SPACE = 0x20202020; VRAM() : vram(ROWS * COLS, SPACE) { std::cout << std::string(ROWS + 1, '\n'); flush(); vram_dirty = false; } uint32_t* image() { return &(vram[0]); } uint32_t read(size_t address) { address = address / 4; if (address >= vram.size()) return 0; return vram[address]; } void write(size_t address, uint32_t value, bool write_strobe[4]) { address = address / 4; uint32_t write_mask = 0; if (write_strobe[0]) write_mask |= 0x000000FF; if (write_strobe[1]) write_mask |= 0x0000FF00; if (write_strobe[2]) write_mask |= 0x00FF0000; if (write_strobe[3]) write_mask |= 0xFF000000; if (address >= vram.size()) return; vram[address] = (vram[address] & ~write_mask) | (value & write_mask); vram_dirty = true; } void flush() { if (! vram_dirty) { return; } vram_dirty = false; std::cout << "\x1b[A"; for(uint32_t i=0; i<ROWS; i++) { std::cout << "\x1b[A\x1b[2K"; } std::cout << std::flush; for(uint32_t i=0; i<ROWS; i++) { for(uint32_t j=0; j<COLS; j++) { uint32_t val = vram[i * COLS + j]; char ch0 = (char) ((val >> 0) & 0xff); char ch1 = (char) ((val >> 8) & 0xff); char ch2 = (char) ((val >> 16) & 0xff); char ch3 = (char) ((val >> 24) & 0xff); std::cout << ch0 << ch1 << ch2 << ch3; } std::cout << "\n"; } std::cout << "\x1b[B" << std::flush; } }; ``` ::: :::spoiler **Printer class** ```c= class Printer { private: uint32_t s_row = 0; uint32_t s_col = 0; VRAM &vram; void write_char(uint32_t row, uint32_t col, unsigned char ch) { bool strobe[4] = {false, false, false, false}; uint32_t offset = col % 4; strobe[offset] = true; vram.write(row * VRAM::COLS * 4 + col, ch << (offset * 8), strobe); } public: Printer(VRAM &vram) : vram(vram) {} void copy_line(uint32_t dst, uint32_t src) { for (uint32_t i = 0; i < VRAM::COLS * 4; i+=4) { uint32_t val = vram.read(src * VRAM::COLS * 4 + i); bool strobe[4] = {true, true, true, true}; vram.write(dst * VRAM::COLS * 4 + i, val, strobe); } } void new_line() { s_col = 0; if (s_row == VRAM::ROWS - 1) { for (uint32_t i = 0; i < VRAM::ROWS - 1; i++) { copy_line(i, i + 1); } for (uint32_t i = 0; i < VRAM::COLS * 4; i+=4) { bool strobe[4] = {true, true, true, true}; vram.write((VRAM::ROWS - 1) * VRAM::COLS * 4 + i, VRAM::SPACE, strobe); } } else { s_row ++; } } void putch(unsigned char ch) { if (ch == '\n') { new_line(); } else if (ch == '\r') { s_col = 0; } else { if (s_col == VRAM::COLS *4 - 1) new_line(); write_char(s_row, s_col, ch); s_col ++; } } void clear_screen() { s_row = 0; s_col = 0; for (uint32_t i = 0; i < VRAM::ROWS * VRAM::COLS * 4; i+=4) { bool strobe[4] = {true, true, true, true}; vram.write(i, VRAM::SPACE, strobe); } } void print_string(const char *str, size_t size) { const char *strend = str + size; while(str < strend) { putch(*str); str ++; } } }; ``` ::: :::spoiler **InterruptHandler class** ```c= class InterruptHandler { private: Memory &memory; VRAM &vram; Printer printer; public: InterruptHandler(Memory &memory, VRAM &vram) : memory(memory), vram(vram), printer(vram) {} uintptr_t memoryTranslate(uint32_t src) { if(src < 0x20000000) { return (uintptr_t) (((char *) memory.image()) + src); } else if(src < 0x40000000) { return (uintptr_t) (((char *) vram.image()) + src - 0x20000000); } else { return 0; } } void handle(uint32_t type, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4, uint32_t arg5) { if(type == 64) { printer.print_string((char *)memoryTranslate(arg1), arg2); } } }; ``` ::: :::spoiler **Simulator class** ```diff! class Simulator { private: ... public: ... void run() { ... + if (top->io_ecall_en) { + if(ecall_timeout == 0) { + ihandler->handle(top->io_ecall_a7, top->io_ecall_a0, top->io_ecall_a1, top->io_ecall_a2, + top->io_ecall_a3, top->io_ecall_a4, top->io_ecall_a5); + ecall_timeout = 3; + } + else { + ecall_timeout --; + } + } ... + vram->flush(); ... } ``` ::: ## Modify [qrcode.c](https://github.com/sysprog21/rv32emu/blob/master/tests/qrcode.c) to replace printf > Reference from [hello.c](https://github.com/sysprog21/ca2023-lab3/blob/main/csrc/hello.c) Referring to `hello.c`, we are using the same approach to replace `printf`. By writing to VRAM and then using `flush()`to print. Additionally, because hello.c uses `strlen()`, we also need to make modifications by creating a similar function, `cust_strlen()`. :::spoiler **qrcode.c** ```c= #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "mmio.h" #define MUL80(x) (x*200) typedef unsigned uint; /* * QR_OPT: use log/exp LUT-based GF MUL. */ #define QR_OPT 1 #define QR_LINES 29 typedef struct qr_ctx { uint8_t size; // 21, 25 or 29 (ver*4+17) uint8_t len; // length of input data. const uint8_t *data; // input data. void *params; // data and ECC parameters. uint32_t bmp[QR_LINES]; // QR code bitmap, 1 word per line. } qr_ctx; /* * Get dots for display. */ static inline bool qr_getdot(qr_ctx *ctx, uint x, uint y) { return ctx->bmp[y] << x >> 31; } /* * Draw finders, timing pattern, alignment pattern, and the dark dot. * And now the format bits for fixed mask 0. */ static void _init_bmp(uint32_t A[], uint size) { /* Draw top-left finder. */ A[0] = A[6] = 0xFE000000; A[1] = A[5] = 0x82000000; A[2] = A[3] = A[4] = 0xBA000000; /* Replicate to bottom-left then top-right. */ int y; for (y = 0; y < 7; y++) { A[size - 1 - y] = A[y]; A[y] |= A[y] >> (size - 7); } /* Horizontal timing pattern. */ A[6] |= 0xAAA800; /* Vertical timing pattern. */ for (y = 9; y < size - 7; y++) A[y] = ((y + 1) & 1) << 25; /* The dark dot, then some format bits. */ y -= 1; // size-9 for (int i = 0; i < 8; i++) { if (i == 4) continue; A[y + i] |= 0x800000; } /* More format bits. */ A[2] |= 0x800000; A[7] = 0x800000; A[8] = 0xEF800000 | 0x31 << (34 - size); /* Alignment pattern for version 2 & 3. */ if (size > 21) { uint pat = 0x1F << (36 - size); A[size - 9] |= pat; A[size - 5] |= pat; pat = 0x11 << (36 - size); A[size - 8] |= pat; A[size - 6] |= pat; A[size - 7] |= 0x15 << (36 - size); } } typedef struct qr_params { uint8_t capa; /* total capacity in bytes. */ uint8_t eccdeg; /* ECC degree/byte count. */ uint8_t gen[]; /* ECC generator polynomial. */ } qr_params; /* * Check capacity then setup parameters. * * Return false if version number is invalid or input exceeds the capacity of * specified version. * - Parameters are written to the context. * - Must evaluate before encoding. * - Must fail if evaluation fails. * Capacity: V1 17B, V2 32B, V3 53B. */ bool qr_eval(qr_ctx *ctx, uint ver, const uint8_t *data, uint len) { static const uint8_t _params_blob[] = { // 26, 7, 0x7f, 0x7a, 0x9a, 0xa4, 0x0b, 0x44, 0x75, // V1 44, 10, 0xd8, 0xc2, 0x9f, 0x6f, 0xc7, 0x5e, 0x5f, 0x71, 0x9d, 0xc1, // V2 70, 15, 0x1d, 0xc4, 0x6f, 0xa3, 0x70, 0x4a, 0x0a, 0x69, 0x69, 0x8b, // V3 0x84, 0x97, 0x20, 0x86, 0x1a}; if (!ctx) return false; ctx->data = data; ctx->len = len; uintptr_t params = (uintptr_t) _params_blob; /* intentional */ /* Skip-overs, cross check with the blob layout. */ switch (ver) { case 1: break; case 2: params += 9; break; case 3: params += 21; break; default: return false; } uint size = ver * 4 + 17; ctx->params = (void *) params; /* 4b mode, 8b count, 4b terminator. */ uint usable = ((qr_params *) params)->capa - ((qr_params *) params)->eccdeg - 2; if (usable < len) return false; ctx->size = size; _init_bmp(ctx->bmp, ctx->size); return true; } /* * Prepare all the data bits before ECC. */ static void _serialize_data(qr_ctx *ctx, uint8_t *buf) { /* Mode bits and length bits. */ uint b = 4 << 8 | ctx->len; buf[0] = b >> 4; uint i = 0; while (i < ctx->len) { b <<= 8; b |= ctx->data[i++]; buf[i] = b >> 4; } /* Final 4 bits with terminator. */ i++; buf[i++] = b << 4; /* Byte padding. */ b = 0xEC; qr_params *para = (qr_params *) ctx->params; while (i < para->capa - para->eccdeg) { buf[i++] = b; b ^= 0xFD; /* alternating EC, 11. */ } /* Clear out the rest bytes for ECC; also clear 1 extra byte for the spare * bits (v2 & v3 have 7 unused). */ while (i <= para->capa) buf[i++] = 0; } #if defined(QR_OPT) /* * The GF(2^8, 285) finite field element multiplication. * Basic iterative, unrolled, and log/exp LUT versions are implemented. */ static inline uint _rs_mul(uint x, uint y) { static const uint8_t _luts[2][256] = { // Log table. {0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175}, // Exponential table. {1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1}, }; if (!x || !y) return 0; uint xp = _luts[0][x] + _luts[0][y]; if (xp > 255) xp -= 255; return _luts[1][xp]; } #else /* use iterative GF MUL */ static inline uint _rs_mul(uint x, uint y) { uint z = 0; /* This is called (133, 340, 825) times in V(1, 2, 3) ECC calculation. */ for (int i = 7; i >= 0; i--) { /* And this body is run 1064, 2720, 6600 times. */ z = (z << 1) ^ ((z >> 7) * 0x11D); z ^= ((y >> i) & 1) * x; } return z; } #endif /* * Calculate the ECC bytes. */ static void _reed_solomon(qr_ctx *ctx, uint8_t *buf) { qr_params *para = (qr_params *) ctx->params; uint deg = para->eccdeg; uint8_t *gen = para->gen; uint len = para->capa - para->eccdeg; uint8_t *res = buf + len; for (uint i = 0; i < len; i++) { uint factor = buf[i] ^ res[0]; for (uint j = 1; j < deg; j++) res[j - 1] = res[j]; res[deg - 1] = 0; for (uint j = 0; j < deg; j++) res[j] ^= _rs_mul(gen[j], factor); } } /* * Return if dot (x,y) is for data (i.e. not function patterns). */ static inline bool _is_data(uint x, uint y, uint size_m1) { if (x == 6 || y == 6) return false; if (y <= 8) return x >= 9 && x <= size_m1 - 8; if (y >= size_m1 - 7 && x <= 8) return false; if (size_m1 > 20 && x >= size_m1 - 8 && x <= size_m1 - 4 && y >= size_m1 - 8 && y <= size_m1 - 4) return false; return true; } typedef union poly64_t { uint64_t bits; uint32_t u[2]; struct { uint32_t u0; uint32_t u1; }; } poly64_t; /* * The QR zig-zag sequence generator. * * To iterate through all valid data positions: call with initial x and y = * size-1, feed back the result as input, and repeat the call-feed cycle. */ static uint64_t zigzag_step(uint x, uint y, uint size_m1) { poly64_t r; while (true) { switch ((x - (x > 6)) & 3) { case 0: if (y < size_m1) x += 1, y += 1; else x -= 1; break; case 1: x -= 1; break; case 2: if (y > 0) x += 1, y -= 1; else { x -= 1; if (x == 6) x = 5; } break; default: x -= 1; } if (_is_data(x, y, size_m1)) break; } r.u0 = x, r.u1 = y; return r.bits; } /* * Put data bits to the QR bitmap. * Fixed masking (0) is applied on the fly. */ static void _place_data(qr_ctx *ctx, const uint8_t *buf) { uint size_m1 = ctx->size - 1; poly64_t xy = {.u0 = size_m1, .u1 = size_m1}; qr_params *para = (qr_params *) ctx->params; uint nbits = para->capa * 8; /* NB: count in the unused bits in V2 and V3. */ if (size_m1 > 20) nbits += 7; for (int i = 0; i < nbits; i++) { bool mask0 = (xy.u0 + xy.u1) % 2 == 0; bool dot = buf[i / 8] & (0x80u >> i % 8); if (dot ^ mask0) ctx->bmp[xy.u1] |= 0x80000000u >> xy.u0; xy.bits = zigzag_step(xy.u0, xy.u1, size_m1); } } /* * The actual encoding. */ void qr_encode(qr_ctx *ctx) { if (!ctx) return; uint8_t dbuf[72]; // V3 capacity 70 and extra 2. _serialize_data(ctx, dbuf); _reed_solomon(ctx, dbuf); _place_data(ctx, dbuf); } struct { unsigned char row, col; } screen; static void copy_line(int prev, int cur) { int *prev_vram_start = ((int *) (MUL80(prev) + VRAM_BASE)); int *cur_vram_start = ((int *) (MUL80(cur) + VRAM_BASE)); for (int i = 0; i < 200; ++i) prev_vram_start[i] = cur_vram_start[i]; } static void write_char(int row, int col, unsigned char ch) { VRAM[MUL80(row) + col] = ch; } static void new_line() { screen.col = 0; if (screen.row == 49) { for (int i = 0; i < 49; ++i) copy_line(i, i + 1); int *vram = (int *) (MUL80(49) + VRAM_BASE); for (int i = 0; i < 50; ++i) vram[i] = 0x20202020; } else { ++screen.row; } } static void putch(unsigned char ch) { if (ch == '\n') { new_line(); } else if (ch == '\r') { screen.col = 0; } else { if (screen.col == 199) new_line(); write_char(screen.row, screen.col, ch); ++screen.col; } } static void clear_screen() { screen.row = 0; screen.col = 0; int *vram = ((int *) VRAM_BASE); for (int i = 0; i < 6400; i+=4) vram[i] = 0x20202020; } static void putstr(const char *s) { while (*s) putch(*(s++)); } void dump_bmp(qr_ctx *ctx) { clear_screen(); for (int i = 0; i < ctx->size + 2; i++) putstr("██"); putstr("\n"); for (int y = 0; y < ctx->size; y++) { putstr("██"); for (int x = 0; x < ctx->size; x++) if (qr_getdot(ctx, x, y)) putstr(" "); else putstr("██"); putstr("██\n"); } for (int i = 0; i < ctx->size + 2; i++) putstr("██"); putstr("\n"); } int cust_strlen(const char *str) { int length =0; while (*str !='\0') { length++; str++; } return length; } int main(int argc, const char *argv[]) { qr_ctx ctx[1]; const char *str = "https://github.com/sysprog21/rv32emu"; if (!qr_eval(ctx, /* version */ 3, (const uint8_t *) str, cust_strlen(str))) { putstr("Evaluation failed. Version invalid or data too long?\n"); return -2; } qr_encode(ctx); dump_bmp(ctx); return 0; } ``` ::: ### Problem ![image](https://hackmd.io/_uploads/r1yLCrWY6.png) The first time running, garbled characters may appear. Upon debugging, it was found that there is an automatic newline function in putch(), causing the qrcode's order to become disordered. When removing the automatic newline feature, the qrcode can be displayed, but only in the top-left corner. After debugging, it was discovered that the threshold for automatic newline is determined by the ROWS and COLS of VRAM class. Therefore, after adjusting `ROW`, `COL` in `sim_main.cpp`, and `MUX80()`, `threshold` in `qrcode.c`, the program can execute correctly. :::spoiler **sim_main.cpp** ```diff! class VRAM { private: ... public: + static constexpr uint32_t ROWS = 50; + static constexpr uint32_t COLS = 200/4; static constexpr uint32_t SPACE = 0x20202020; ... }; ``` ::: ### Result After we modify the qrcode.c, we need to execute the following command: ```shell ~/ca2023-lab3/csrc$ make update ``` After we modify the chisel code, we need to execute the following command: ```shell ~/ca2023-lab3$ make verilator ``` Run qrcode.asmbin by following command: ```shell ~/ca2023-lab3$ ./run-verilator.sh -memory 0x8000 -instruction src/main/resources/qrcode.asmbin -time 5000000 -signature 0x00000000 0x00000300 mem.txt -halt 0xfffc -vcd dump.vcd ``` :::warning Lack of measurements for testbench. ::: ![Ubuntu 20.04.6 [執行中] - Oracle VM VirtualBox 2024-01-14 19-31-51 (2)](https://hackmd.io/_uploads/r1pdtBbY6.gif) ![image](https://hackmd.io/_uploads/HkI6tXZt6.png) ## Reference [Lab3: Construct a single-cycle RISC-V CPU with Chisel](https://hackmd.io/@sysprog/r1mlr3I7p#Chisel-Tutorial) Assignment3 from [eeeXun](https://hackmd.io/@KXkA4u0LQuyNTwOorDw2RA/HyNM1bP4p) Assignment3 from [kc71486](https://hackmd.io/@kc71486/computer_architecture_hw3) [QR code generation](https://github.com/sysprog21/rv32emu/blob/master/tests/qrcode.c) [hello.c](https://github.com/sysprog21/ca2023-lab3/blob/main/csrc/hello.c)