# --------------------------
#   Hex2VL.tcl
# --------------------------
# (c) ALSE.
# http://www.alse-fr.com/english
# Author : B. Cuzeau
# Date   : 2 Dec 2002
#
# Reads in Intel-HEX format and creates a Verilog
# implementation of the Rom.
# Does automatic sizing (Address [1..n] and Data [1..8 bits])


puts "\nHex2VL : (c) 2002 ALSE.\n"

set types {
  {{HEX files}     {.hex} }
  {{All Files}        *    }
}

set fname [tk_getOpenFile -filetypes $types \
                          -title "Hex2VL converter (c) ALSE. Select an Intel-Hex file :"]

if {$fname==""} { puts "No file selected !"; exit }

set fmatch {}

if { [regexp {(.+)\.} [file tail $fname] fmatch]  } {
 # there is a '.' in the file name
  set outfname [pwd]/${fmatch}v  ;# add suffix 'v'
} else {
  # no '.' in the filename : add '.v'
  set outfname ${fname}.v
}

# don't overwrite an existing output file without confirmation...

if { [file exists $outfname] } {
  set Confirm [tk_messageBox -title "Hex2VL converter (c) ALSE." \
                             -message "Are you sure to Overwrite $outfname ?" \
                             -type yesno \
                             -icon question]
  if { $Confirm == "yes" } {
    set outf [open $outfname w]
  } else { exit }
} else {
    set outf [open $outfname w]
}


set inf [open $fname r]


# ---------------
# First pass to determine the Memory Size & Width !

set Line {}
set LineNb 0
set Done false
set LastAdd 0
set MaxData 0

while {(! [eof $inf]) && ! $Done } {
  gets $inf Line
  set Cks 0
  incr LineNb
  set Str {}
  if {[string index $Line 0] == ":"} {
    scan $Line ":%2x%2x%2x%2x%s" Nb AdH AdL Type Str
    set Addr [expr {$AdL + 256 * $AdH}]
    incr Cks [expr {$Nb + $AdH + $AdL + $Type}]

    if { ($Type > 1) } {
      tk_messageBox -message "Unsupported type ($Type) at line #$LineNb." \
                    -type ok -icon error
      # exit

      } elseif {$Type == 1} { # Type=1 means End !
        set Done true

      } else { # Type is 00 (data)
        while {[string length $Str] > 2} {
          scan $Str "%2x%s" Data Str
          set MaxData [expr ($Data>$MaxData) ? $Data : $MaxData ]
          incr Cks $Data
          incr Addr
          }
        scan $Str "%2x" Checksum
        set Ck [expr {$Cks + $Checksum}]
        # puts "Cks = $Cks, in file = $Checksum - Ck=$Ck"
        if { [expr {$Ck % 256}] != 0 } {
          tk_messageBox -message "Checksum error at line #$LineNb." \
                        -type ok \
                        -icon error
          exit
        }
        incr Addr -1
        set LastAdd [expr ($Addr>$LastAdd) ? $Addr : $LastAdd ]
      }
    }
  }

set Nbits  [expr { int (floor ( log ($LastAdd) / log (2) ) + 1 ) } ]
set Dabits [expr { int (floor ( log ($MaxData) / log (2) ) + 1 ) } ]

puts "Last Address = $LastAdd, Nbits set to $Nbits, MaxData=$MaxData, Dabits set to $Dabits."

close $inf

# ---------------
# Second pass to do the Job...

set inf [open $fname r]

set Line {}
set LineNb 0
set Done false

# Write the Verilog preamble

puts $outf "// [file tail $$outfname]"
puts $outf "// --------------------------------"
puts $outf "//   Rom built from [file tail $fname]"
puts $outf "// --------------------------------"

puts $outf {// Verilog file generated automatically by Hex2VL,
// (c) ALSE - B. Cuzeau.
// http://www.alse-fr.com}

puts $outf "// [clock format [clock seconds]]"

puts $outf {//

`timescale 1ns/100ps
module myrom (Addr, Dout);
}

puts $outf "parameter AdWidth = $Nbits;"
puts $outf "parameter DaWidth = $Dabits;"

puts $outf {\

input  [AdWidth-1:0]  Addr;
output [DaWidth-1:0]  Dout;
reg    [DaWidth-1:0]  Dout;

always @(Addr)
begin
  case (Addr)
}


# Loop to write the entire array

while {(! [eof $inf]) && ! $Done } {
  gets $inf Line
  set Cks 0
  incr LineNb
  set Str {}
  if {[string index $Line 0] == ":"} {
    scan $Line ":%2x%2x%2x%2x%s" Nb AdH AdL Type Str
    set Addr [expr {$AdL + 256 * $AdH}]
    incr Cks [expr {$Nb + $AdH + $AdL + $Type}]
    # puts "$Nb $Addr $Type $Str "

    if { ($Type > 1) } {
      tk_messageBox -title "Hex2VL converter (c) ALSE."\
                    -message "Unsupported type ($Type) at line #$LineNb."\
                    -type ok \
                    -icon error
      exit

      } elseif {$Type == 1} { # Type=1 means End !
        set Done true

      } else { # Type is 00 (data)
        while {[string length $Str] > 2} {
          scan $Str "%2x%s" Data Str
          puts $outf "    $Addr : Dout <= $Data;"
          incr Cks $Data
          incr Addr
          }
        scan $Str "%2x" Checksum
        set Ck [expr {$Cks + $Checksum}]
        # puts "Cks = $Cks, in file = $Checksum - Ck=$Ck"
        if { [expr {$Ck % 256}] != 0 } {
          tk_messageBox -title "Hex2VL converter (c) ALSE." \
                        -message "Checksum error at line #$LineNb." \
                        -type ok \
                        -icon error
          exit
        }
      }
    }
  }

# Write the Verilog postamble

puts $outf {
  endcase
end

endmodule // myrom
}

# -- Job is done --

puts "Nb of lines : $LineNb."

close $outf
close $inf

tk_messageBox -title "Hex2VL converter (c) ALSE." \
              -message "Conversion done !" \
              -type ok \
              -icon info

exit
