@@ -114,6 +114,132 @@ fn main() {
114114 eprintln ! ( " nrf52840 Nordic nRF52840 (ARM Cortex-M4F, 256KB RAM, 64MHz)" ) ;
115115 eprintln ! ( " rp2040 Raspberry Pi Pico (ARM Cortex-M0+, 264KB RAM, 133MHz)" ) ;
116116 }
117+ "wcet-elf" => {
118+ if args. len ( ) < 3 {
119+ eprintln ! ( "usage: kov wcet-elf <firmware.elf>" ) ;
120+ process:: exit ( 1 ) ;
121+ }
122+ let data = std:: fs:: read ( & args[ 2 ] ) . unwrap_or_else ( |e| die ( & format ! ( "{e}" ) ) ) ;
123+ if data. len ( ) < 84 || & data[ 0 ..4 ] != b"\x7f ELF" {
124+ die ( "not a valid ELF file" ) ;
125+ }
126+ // parse ELF: find .text section
127+ let entry = u32:: from_le_bytes ( [ data[ 24 ] , data[ 25 ] , data[ 26 ] , data[ 27 ] ] ) ;
128+ let phoff = u32:: from_le_bytes ( [ data[ 28 ] , data[ 29 ] , data[ 30 ] , data[ 31 ] ] ) as usize ;
129+ let phnum = u16:: from_le_bytes ( [ data[ 44 ] , data[ 45 ] ] ) as usize ;
130+
131+ // find PT_LOAD segment with execute permission
132+ let mut text_offset = 0usize ;
133+ let mut text_vaddr = 0u32 ;
134+ let mut text_size = 0usize ;
135+ for i in 0 ..phnum {
136+ let off = phoff + i * 32 ;
137+ if off + 32 > data. len ( ) {
138+ break ;
139+ }
140+ let p_type =
141+ u32:: from_le_bytes ( [ data[ off] , data[ off + 1 ] , data[ off + 2 ] , data[ off + 3 ] ] ) ;
142+ let p_offset = u32:: from_le_bytes ( [
143+ data[ off + 4 ] ,
144+ data[ off + 5 ] ,
145+ data[ off + 6 ] ,
146+ data[ off + 7 ] ,
147+ ] ) as usize ;
148+ let p_vaddr = u32:: from_le_bytes ( [
149+ data[ off + 8 ] ,
150+ data[ off + 9 ] ,
151+ data[ off + 10 ] ,
152+ data[ off + 11 ] ,
153+ ] ) ;
154+ let p_filesz = u32:: from_le_bytes ( [
155+ data[ off + 16 ] ,
156+ data[ off + 17 ] ,
157+ data[ off + 18 ] ,
158+ data[ off + 19 ] ,
159+ ] ) as usize ;
160+ let p_flags = u32:: from_le_bytes ( [
161+ data[ off + 24 ] ,
162+ data[ off + 25 ] ,
163+ data[ off + 26 ] ,
164+ data[ off + 27 ] ,
165+ ] ) ;
166+ if p_type == 1 && ( p_flags & 1 ) != 0 {
167+ // PT_LOAD + PF_X
168+ text_offset = p_offset;
169+ text_vaddr = p_vaddr;
170+ text_size = p_filesz;
171+ }
172+ }
173+
174+ if text_size == 0 {
175+ die ( "no executable segment found" ) ;
176+ }
177+
178+ let code = & data[ text_offset..text_offset + text_size] ;
179+ eprintln ! ( " file: {}" , args[ 2 ] ) ;
180+ eprintln ! ( " entry: {:#010X}" , entry) ;
181+ eprintln ! ( " .text: {} bytes at {:#010X}" , text_size, text_vaddr) ;
182+ eprintln ! ( ) ;
183+
184+ // per-function WCET: scan for function prologues (addi sp, sp, -N)
185+ let mut funcs: Vec < ( u32 , usize ) > = Vec :: new ( ) ; // (addr, offset)
186+ for i in ( 0 ..code. len ( ) . saturating_sub ( 3 ) ) . step_by ( 2 ) {
187+ if i + 3 >= code. len ( ) {
188+ break ;
189+ }
190+ let inst = u32:: from_le_bytes ( [ code[ i] , code[ i + 1 ] , code[ i + 2 ] , code[ i + 3 ] ] ) ;
191+ let opcode = inst & 0x7F ;
192+ let rd = ( inst >> 7 ) & 0x1F ;
193+ let f3 = ( inst >> 12 ) & 7 ;
194+ let rs1 = ( inst >> 15 ) & 0x1F ;
195+ let imm = ( inst as i32 ) >> 20 ;
196+ if opcode == 0x13 && f3 == 0 && rd == 2 && rs1 == 2 && imm < 0 {
197+ funcs. push ( ( text_vaddr + i as u32 , i) ) ;
198+ }
199+ }
200+
201+ // analyze each detected function
202+ eprintln ! ( " detected {} function prologues:" , funcs. len( ) ) ;
203+ let mut total_cycles = 0u32 ;
204+ for ( j, & ( addr, start) ) in funcs. iter ( ) . enumerate ( ) {
205+ let end = if j + 1 < funcs. len ( ) {
206+ funcs[ j + 1 ] . 1
207+ } else {
208+ code. len ( )
209+ } ;
210+ if start >= end {
211+ continue ;
212+ }
213+ let func_code = & code[ start..end] ;
214+ let mut cycles = 0u32 ;
215+ let mut k = 0 ;
216+ while k + 3 < func_code. len ( ) {
217+ let inst = u32:: from_le_bytes ( [
218+ func_code[ k] ,
219+ func_code[ k + 1 ] ,
220+ func_code[ k + 2 ] ,
221+ func_code[ k + 3 ] ,
222+ ] ) ;
223+ cycles += estimate_cycles ( inst) ;
224+ k += 4 ;
225+ }
226+ let stack = estimate_stack ( func_code) ;
227+ eprintln ! (
228+ " {:#010X}: ~{} cycles, {} bytes stack, {} instructions" ,
229+ addr,
230+ cycles,
231+ stack,
232+ func_code. len( ) / 4
233+ ) ;
234+ total_cycles += cycles;
235+ }
236+ eprintln ! ( ) ;
237+ eprintln ! (
238+ " total: ~{} cycles worst-case across {} functions" ,
239+ total_cycles,
240+ funcs. len( )
241+ ) ;
242+ }
117243 "svd" => {
118244 if args. len ( ) < 3 {
119245 eprintln ! ( "usage: kov svd <file.svd> [--name <board>]" ) ;
@@ -723,6 +849,42 @@ fn find_flag(args: &[String], flag: &str) -> Option<String> {
723849 args. windows ( 2 ) . find ( |w| w[ 0 ] == flag) . map ( |w| w[ 1 ] . clone ( ) )
724850}
725851
852+ fn estimate_cycles ( inst : u32 ) -> u32 {
853+ let opcode = inst & 0x7F ;
854+ let f3 = ( inst >> 12 ) & 7 ;
855+ let f7 = inst >> 25 ;
856+ match opcode {
857+ 0x37 | 0x17 => 1 , // LUI, AUIPC
858+ 0x6F | 0x67 => 1 , // JAL, JALR
859+ 0x63 => 1 , // branches
860+ 0x03 => 2 , // loads
861+ 0x23 => 2 , // stores
862+ 0x13 => 1 , // immediate ALU
863+ 0x33 => match ( f3, f7) {
864+ ( 0 , 0x01 ) => 5 , // MUL
865+ ( 4 , 0x01 ) | ( 5 , 0x01 ) => 33 , // DIV, DIVU
866+ ( 6 , 0x01 ) | ( 7 , 0x01 ) => 33 , // REM, REMU
867+ _ => 1 ,
868+ } ,
869+ 0x73 => 1 , // SYSTEM
870+ _ => 1 ,
871+ }
872+ }
873+
874+ fn estimate_stack ( code : & [ u8 ] ) -> u32 {
875+ if code. len ( ) < 4 {
876+ return 0 ;
877+ }
878+ let first = u32:: from_le_bytes ( [ code[ 0 ] , code[ 1 ] , code[ 2 ] , code[ 3 ] ] ) ;
879+ // addi sp, sp, -N: opcode=0x13, rd=sp(2), rs1=sp(2), funct3=0
880+ if first & 0x000FFFFF == 0x00010113 {
881+ let imm = ( first as i32 ) >> 20 ;
882+ ( -imm) as u32
883+ } else {
884+ 0
885+ }
886+ }
887+
726888fn die ( msg : & str ) -> ! {
727889 eprintln ! ( "error: {msg}" ) ;
728890 process:: exit ( 1 ) ;
0 commit comments