diff --git a/.vscode/launch.json b/.vscode/launch.json index a50dd28d..af6eff06 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -29,6 +29,31 @@ ], "cwd": "${workspaceFolder}" }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'ziskemu hello_world'", + "cargo": { + "args": [ + "build", + "--bin=ziskemu" + ], + "filter": { + "name": "ziskemu", + "kind": "bin" + } + }, + "args": [ + "-e", + "/Users/jbaylina/git/zisk/hello_world/target/riscv64ima-polygon-ziskos-elf/debug/sha_hasher", + "-i", + "/Users/jbaylina/git/zisk/hello_world/build/input.bin", + "-c", + "-m", + "-x", + ], + "cwd": "${workspaceFolder}" + }, { "type": "lldb", "request": "launch", diff --git a/core/src/zisk_inst.rs b/core/src/zisk_inst.rs index d9d8e93c..9254eefe 100644 --- a/core/src/zisk_inst.rs +++ b/core/src/zisk_inst.rs @@ -9,6 +9,7 @@ pub enum ZiskOperationType { Binary, BinaryE, Keccak, + PubOut, } pub const ZISK_OPERATION_TYPE_VARIANTS: usize = 6; diff --git a/core/src/zisk_ops.rs b/core/src/zisk_ops.rs index c2ca35a9..329b6e91 100644 --- a/core/src/zisk_ops.rs +++ b/core/src/zisk_ops.rs @@ -22,6 +22,7 @@ pub enum OpType { Binary, BinaryE, Keccak, + PubOut, } impl From for ZiskOperationType { @@ -32,6 +33,7 @@ impl From for ZiskOperationType { OpType::Binary => ZiskOperationType::Binary, OpType::BinaryE => ZiskOperationType::BinaryE, OpType::Keccak => ZiskOperationType::Keccak, + OpType::PubOut => ZiskOperationType::PubOut, } } } @@ -46,6 +48,7 @@ impl Display for OpType { Self::Binary => write!(f, "b"), Self::BinaryE => write!(f, "BinaryE"), Self::Keccak => write!(f, "Keccak"), + Self::PubOut => write!(f, "PubOut"), } } } @@ -266,6 +269,7 @@ define_ops! { (MaxuW, "maxu_w", Binary, 77, 0x1b, opc_maxu_w, op_maxu_w), (MaxW, "max_w", Binary, 77, 0x1c, opc_max_w, op_max_w), (Keccak, "keccak", Keccak, 77, 0xf1, opc_keccak, op_keccak), + (PubOut, "pubout", PubOut, 77, 0x30, opc_pubout, op_pubout), // TODO: New type } // Constant values used in operation functions @@ -877,3 +881,14 @@ impl From for ZiskOp { ZiskOp::try_from_code(value.opcode).unwrap() } } + +/// Copies register b into c as a public output data record, where a contains the data index +#[inline(always)] +pub const fn op_pubout(a: u64, b: u64) -> (u64, bool) { + (b, false) +} +#[inline(always)] +pub fn opc_pubout(ctx: &mut InstContext) { + (ctx.c, ctx.flag) = op_pubout(ctx.a, ctx.b); + //println!("public ${} = {:#010x}", ctx.a, ctx.b); +} diff --git a/core/src/zv2zisk.rs b/core/src/zv2zisk.rs index 3982ae10..ac17d344 100644 --- a/core/src/zv2zisk.rs +++ b/core/src/zv2zisk.rs @@ -1357,7 +1357,7 @@ pub fn add_zisk_init_data(rom: &mut ZiskRom, addr: u64, data: &[u8]) { /// Add the entry/exit jump program section pub fn add_entry_exit_jmp(rom: &mut ZiskRom, addr: u64) { //print!("add_entry_exit_jmp() rom.next_init_inst_addr={}\n", rom.next_init_inst_addr); - let trap_handler: u64 = rom.next_init_inst_addr + 0x18; + let trap_handler: u64 = rom.next_init_inst_addr + 0x14; // :0000 let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); @@ -1420,7 +1420,120 @@ pub fn add_entry_exit_jmp(rom: &mut ZiskRom, addr: u64) { rom.insts.insert(rom.next_init_inst_addr, zib); rom.next_init_inst_addr += 4; - // :0014 + /* Read output length located at first 64 bits of output data, + then read output data in chunks of 64 bits: + + loadw: c(reg1) = b(mem=OUTPUT_ADDR), a=0 // TODO: check that Nx4 < OUTPUT_SIZE + copyb: c(reg2)=b=0, a=0 + copyb: c(reg3)=b=OUTPUT_ADDR+4, a=0 + + eq: if reg2==reg1 jump to end + pubout: c=b.mem(reg3), a = reg2 + add: reg3 = reg3 + 4 // Increment memory address + add: reg2 = reg2 + 1, jump -12 // Increment index, goto eq + + end + */ + + // :0014 -> copyb: reg1 = c = b = mem(OUTPUT_ADDR,4), a=0 + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("imm", OUTPUT_ADDR, false); + zib.src_b("ind", 0, false); + zib.ind_width(4); + zib.op("copyb").unwrap(); + zib.store("reg", 1, false, false); + zib.j(0, 4); + zib.verbose("Set reg1 to output data length read at OUTPUT_ADDR"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :0018 -> copyb: copyb: c(reg2)=b=0, a=0 + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("imm", 0, false); + zib.src_b("imm", 0, false); + zib.op("copyb").unwrap(); + zib.store("reg", 2, false, false); + zib.j(0, 4); + zib.verbose("Set reg2 to 0"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :001c -> copyb: c(reg3)=b=OUTPUT_ADDR, a=0 + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("imm", 0, false); + zib.src_b("imm", OUTPUT_ADDR + 4, false); + zib.op("copyb").unwrap(); + zib.store("reg", 3, false, false); + zib.j(0, 4); + zib.verbose("Set reg3 to OUTPUT_ADDR + 4"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :0020 -> eq: if reg2==reg1 jump to end + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("reg", 1, false); + zib.src_b("reg", 2, false); + zib.op("eq").unwrap(); + zib.store("none", 0, false, false); + zib.j(20, 4); + zib.verbose("If reg1==reg2 jumpt to end"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :0024 -> copyb: c = b = mem(reg3, 4) + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("reg", 3, false); + zib.src_b("ind", 0, false); + zib.ind_width(4); + zib.op("copyb").unwrap(); + zib.store("none", 0, false, false); + zib.j(0, 4); + zib.verbose("Set c to mem(output_data[index]), a=index"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :0028 -> pubout: c = last_c = mem(reg3, 4), a = reg2 = index + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("reg", 2, false); + zib.src_b("lastc", 0, false); + zib.op("pubout").unwrap(); + zib.store("none", 0, false, false); + zib.j(0, 4); + zib.verbose("Public output, set c to output_data[index], a=index"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :002c -> add: reg3 = reg3 + 4 + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("reg", 3, false); + zib.src_b("imm", 4, false); + zib.op("add").unwrap(); + zib.store("reg", 3, false, false); + zib.j(0, 4); + zib.verbose("Set reg3 to reg3 + 4"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :0030 -> add: reg2 = reg2 + 1, jump -16 + let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); + zib.src_a("reg", 2, false); + zib.src_b("imm", 1, false); + zib.op("add").unwrap(); + zib.store("reg", 2, false, false); + zib.j(4, -16); + zib.verbose("Set reg2 to reg2 + 1"); + zib.build(); + rom.insts.insert(rom.next_init_inst_addr, zib); + rom.next_init_inst_addr += 4; + + // :0034 let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); zib.src_a("imm", 0, false); zib.src_b("imm", 0, false); @@ -1432,7 +1545,7 @@ pub fn add_entry_exit_jmp(rom: &mut ZiskRom, addr: u64) { rom.insts.insert(rom.next_init_inst_addr, zib); rom.next_init_inst_addr += 4; - // :0018 trap_handle + // :0038 trap_handle // If register a7==CAUSE_EXIT, end the program let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); zib.src_a("reg", 17, false); @@ -1444,7 +1557,7 @@ pub fn add_entry_exit_jmp(rom: &mut ZiskRom, addr: u64) { rom.insts.insert(rom.next_init_inst_addr, zib); rom.next_init_inst_addr += 4; - // :001c + // :003c let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); zib.src_a("imm", 0, false); zib.src_b("imm", 0, false); @@ -1456,7 +1569,7 @@ pub fn add_entry_exit_jmp(rom: &mut ZiskRom, addr: u64) { rom.insts.insert(rom.next_init_inst_addr, zib); rom.next_init_inst_addr += 4; - // :0020 trap_handle + // :0040 trap_handle // If register a7==CAUSE_KECCAK, call the keccak opcode and return let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); zib.src_a("reg", 17, false); @@ -1468,7 +1581,7 @@ pub fn add_entry_exit_jmp(rom: &mut ZiskRom, addr: u64) { rom.insts.insert(rom.next_init_inst_addr, zib); rom.next_init_inst_addr += 4; - // :0024 + // :0044 let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); zib.src_a("reg", 11, false); zib.src_b("imm", 0, false); @@ -1479,7 +1592,7 @@ pub fn add_entry_exit_jmp(rom: &mut ZiskRom, addr: u64) { rom.insts.insert(rom.next_init_inst_addr, zib); rom.next_init_inst_addr += 4; - // :0028 + // :0048 let mut zib = ZiskInstBuilder::new(rom.next_init_inst_addr); zib.src_a("imm", 0, false); zib.src_b("reg", 1, false); diff --git a/emulator/src/emu_options.rs b/emulator/src/emu_options.rs index 13691d34..d90e23bb 100644 --- a/emulator/src/emu_options.rs +++ b/emulator/src/emu_options.rs @@ -42,7 +42,8 @@ pub struct EmuOptions { /// Sets the log step mode #[clap(short, long, value_name = "LOG_STEP", default_value = "false")] pub log_step: bool, - /// Log the output to console + /// Log the output to console. This option is set by default to true as a requirement to pass + /// the riscof GHA tests #[clap(short = 'c', long, value_name = "LOG_OUTPUT", default_value = "true")] pub log_output: bool, /// Trace every this number of steps diff --git a/ziskos/entrypoint/src/lib.rs b/ziskos/entrypoint/src/lib.rs index c7f8f472..ec5d5385 100644 --- a/ziskos/entrypoint/src/lib.rs +++ b/ziskos/entrypoint/src/lib.rs @@ -44,10 +44,11 @@ pub fn read_input() -> Vec { } #[cfg(target_os = "ziskos")] -pub fn write_output(write_ptr: &[u8], nbytes: usize) { +pub fn set_output(id: usize, value: u32) { use std::arch::asm; + let addr_n: *mut u32; + let addr_v: *mut u32; let arch_id_zisk: usize; - let mut addr: *mut u8 = 0x1000_0000 as *mut u8; unsafe { asm!( @@ -55,33 +56,33 @@ pub fn write_output(write_ptr: &[u8], nbytes: usize) { out(reg) arch_id_zisk, ) }; + + assert!(id < 64, "Maximum number of public outputs: 64"); + if arch_id_zisk == ARCH_ID_ZISK as usize { - addr = 0xa000_0200 as *mut u8; + addr_n = OUTPUT_ADDR as *mut u32; + addr_v = (OUTPUT_ADDR + 4 + 4 * (id as u64)) as *mut u32; + } else { + addr_n = 0x1000_0000 as *mut u32; + addr_v = (0x1000_0000 + 4 + 4 * (id as u64)) as *mut u32; } - for i in 0..nbytes { - unsafe { - core::ptr::write_volatile(addr, write_ptr[i]); - } + let n; + + unsafe { + n = core::ptr::read(addr_n) as usize; } -} -#[cfg(not(target_os = "ziskos"))] -pub fn write_output(write_ptr: &[u8], _nbytes: usize) { - // Convert write_ptr to string - let write_str = match std::str::from_utf8(write_ptr) { - Ok(v) => v, - Err(e) => { - println!("Error converting write_ptr to string: {}", e); - return; - } - }; + if id + 1 > n { + unsafe { core::ptr::write_volatile(addr_n, (id + 1) as u32) }; + } - // Create the output string - let output = write_str.to_string(); + unsafe { core::ptr::write_volatile(addr_v, value) }; +} - // Print the output string - print!("{}", output); +#[cfg(not(target_os = "ziskos"))] +pub fn set_output(id: usize, value: u32) { + println!("public {}: {:#010x}", id, value); } #[cfg(target_os = "ziskos")]