#!/usr/bin/awk -f
#
#  File: verilog.awk
#
#  This file is part of XSCHEM,
#  a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit
#  simulation.
#  Copyright (C) 1998-2024 Stefan Frederik Schippers
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


BEGIN{
 bitblast = 0
 while( (ARGV[1] ~ /^[-]/) || (ARGV[1] ~ /^$/) ) {
   if(ARGV[1] == "-bitblast") bitblast = 1
   for(i=2; i<= ARGC;i++) {
     ARGV[i-1] = ARGV[i]
   }
   ARGC--
 }

 net_types["wire"]=1
 net_types["tri"]=1
 net_types["wor"]=1
 net_types["trior"]=1
 net_types["wand"]=1
 net_types["triand"]=1
 net_types["tri0"]=1
 net_types["tri1"]=1
 net_types["supply1"]=1
 net_types["supply0"]=1
 net_types["trireg"]=1
 net_types["reg"]=1
 net_types["integer"]=1
 net_types["time"]=1
 net_types["real"]=1
 net_types["signed"]=1
 net_types["logic"]=1
 net_types["bool"]=1
 direction["input"]=1
 direction["inout"]=1
 direction["output"]=1
}

/^---- start primitive/{primitive_line=""; primitive_mult=$4;primitive=1; next}
/^---- end primitive/{
  primitive=0
  $0 = primitive_line
  gsub(/----pin\(/, " ----pin(",$0)
  gsub(/----name\(/, " ----name(",$0)
  split($0, primitive_line_sep,  /[^ \n\t]+/)
  for(j=1;j<= primitive_mult; j++) {
    prefix=""
    # print $0 > "/dev/stderr"
    for(i=1;i<=NF;i++) {
      if($i ~/^#[a-zA-Z_]+#$/) {
         prefix=$i
         sub(/^#/,"", prefix)
         sub(/#$/,"", prefix)
         continue
      }
      prim_field=$i
      if($i ~ /^----pin\(.*\)/) {
        sub(/----pin\(/,"",prim_field)
        sub(/\)$/,"",prim_field)
        pport_mult = split(prim_field, prim_field_array,/,/)

        # 20060919 BEGIN
        # if bussed port connected to primitive and primitive mult==1 print only basename of bus
        if(primitive_mult==1 && pport_mult>1) {
          if(check2(prim_field_array, pport_mult))
           printf "%s[%s:%s] ", prefix s_b(prim_field_array[1]),
              s_i(prim_field_array[1]), s_i(prim_field_array[pport_mult])
          else {
            printf " { "
            for(s=1;s<= pport_mult; s++) {
              printf "%s", prefix prim_field_array[s]
              if(s<pport_mult) printf ","
            }
            printf "}%s", primitive_line_sep[i+1]
         }
        }
        else
        # 20060919 end

        printf "%s", prim_field_array[1+(j-1) % pport_mult]   #  20140401 1+(j-1) % pport_mult instead of j
        printf "%s", primitive_line_sep[i+1]
      }
      else if($i ~ /^----name\(.*\)/) {
        sub(/----name\(/,"",prim_field)
        sub(/\)$/,"",prim_field)
        sub(/:/, "_slot_", prim_field)
        split(prim_field, prim_field_array,/,/)
        sub(/\[/,"_", prim_field_array[j])
        sub(/\]/,"", prim_field_array[j])
        printf "%s%s", prefix prim_field_array[j], primitive_line_sep[i+1]
      }
      else {
        printf "%s%s", prim_field, primitive_line_sep[i+1]
      }
      prefix=""
    } # end for i
    printf "\n"
  } # end for j
  next
}

primitive==1{primitive_line=primitive_line "\n" $0; next  }

# print signals/regs/variables
/---- end signal list/{
 for(ii = 0 ; ii < signal_n; ii++) { # used to preserve order of signals
  i = signal_num[ii] # used to preserve order of signals
  n=signal_basename[i]
  split(signal_index[i],tmp)
  hsort(tmp,n)
  printf "%s ", signal_type[i]

  # 20161118
  ntypes = split(signal_type[i], sigtype_arr)
  if(ntypes==2) {
    if( i in signal_delay) printf "%s ", signal_delay[i]
    printf "%s", i
    if(signal_index[i] !~ /no_index/)
    {
     if(tmp[1] ~ /:/) printf "[%s] ",tmp[1]
     else if(n==1) printf "[%s:%s] ",tmp[1], tmp[1]
     else printf "[%s:%s] ",tmp[1], tmp[n]
    }
    if(i in signal_value) printf " = %s ", signal_value[i]
    printf " ;\n"
  } else {
    if( i in signal_delay) printf "%s ", signal_delay[i]
    if(signal_index[i] !~ /no_index/)
    {
     if(tmp[1] ~ /:/) printf "[%s] ",tmp[1]
     else if(n==1) printf "[%s:%s] ",tmp[1], tmp[1]
     else printf "[%s:%s] ",tmp[1], tmp[n]
    }
    printf "%s ", i
    if(i in signal_value) printf " = %s ", signal_value[i]
    printf ";\n"
  }
 }
 # /20161118

 siglist=0;
 print ""
 netlist=1
 next
}

# store signals
siglist==1 && ($1 in net_types) {
 # 20070525 recognize "reg real", "wire signed"  types and similar
 if($2 in net_types) {
   if($3 ~ /^#/) basename=s_b($4)
   else basename=s_b($3)
   signal_type[basename]=$1 " " $2
   if($3 ~ /^#/) {
    signal_delay[basename]=$3
    $3=""; $0=$0;
   }
   if($4=="="){
    val=$0
    sub(/.*=/,"",val)
    sub(/;.*/,"",val)
    signal_value[basename]=val
   }
   if(!(basename in signal_basename)) signal_num[signal_n++] = basename # used to preserve order of signals
   signal_basename[basename]++
   if($3 ~ /\[.*\]/) {
    signal_index[basename]=signal_index[basename] " " s_i($3)
   }
   else signal_index[basename]="no_index"
 }
 # /20070525
 else {
   if($2 ~ /^#/) basename=s_b($3)
   else basename=s_b($2)
   signal_type[basename]=$1
   if($2 ~ /^#/) {     # handle declarations like: wire #20 w_tmp;
    signal_delay[basename]=$2
    $2=""; $0=$0;
   }
   if($3=="="){
    val=$0
    sub(/.*=/,"",val)
    sub(/;.*/,"",val)
    signal_value[basename]=val
   }
   if(!(basename in signal_basename)) signal_num[signal_n++] = basename # used to preserve order of signals
   signal_basename[basename]++
   if($2 ~ /\[.*\]/) {
    signal_index[basename]=signal_index[basename] " " s_i($2)
   }
   else signal_index[basename]="no_index"
 }
 next
}

/---- begin signal list/{
 siglist=1;
 next
}

# print module ports
begin_module==1 {
 $1=s_b($1)
}

# new module declaration
NF==3 && $3=="(" && $1=="module" {
 begin_module=1
 delete signal_basename
 delete signal_index
 delete signal_value
 delete signal_type
 delete signal_delay
 delete signal_num # used to preserve order of signals
 signal_n = 0 # used to preserve order of signals
}

begin_module && $1 ~/^\);$/ {
 begin_module=0
}
 netlist==0 && $0 ~ /^ *parameter +[^ ]+ += +/{ $4=get_number($4) }

/^---- instance / {
  on_single_line()
## ---- instance xsl[0],xsl[1],xsl[2],xsl[3],xsl[4],xsl[5],xsl[6],xsl[7] ( ?32 I[0:31] DEL[0],DEL[1] ...
  num=split($3,name,",")
  for(j=7;j<=NF-1;j+=3)
  {
    arg_num[j]=split($j,tmp,",")
    for(k=1;k<=arg_num[j]; k++) {
     arg_name[j,k]=tmp[k]
    }
  }
  for(i=1;i<=num;i++)
  {
   gsub(/[\[]/,"_",name[i])
   gsub(/[\]]/,"",name[i])
   printf "%s", previous  # print module name (and params)
   printf "%s ( ", name[i]

   for(j=5;j<=NF-1;j++)
   {
    pin=""
    if(j>7) {printf ","}
    if($j !~ /^\?-?[0-9]+$/)
    {
      pin=pin $j # if not a node just print it
    }
    else
    {
     nmult=$(j++);j++

     sub(/\?/,"",nmult)
     nmult=nmult+0
     if(nmult==-1) nmult=arg_num[j]
     for(l=0;l<nmult;l++)
     {
      ii=(l+nmult*(i-1))%arg_num[j]+1
      if(pin!="") pin=pin ","
      pin=pin  arg_name[j,ii]
     }
    }
    if(nmult==1) printf "\n .%s( %s )" ,s_b($(j-1)),pin
    else {
      # old code
      if(!bitblast) {
        split(pin,pin_array,",")
        basename=s_b(pin_array[1])
        if(check2(pin_array,nmult) && net_ascending==0) {  ## 20140416 if ascending nets print single bits
         if(nmult==signal_basename[basename] )
           printf "\n .%s( %s )", s_b($(j-1)),basename
         else
           printf "\n .%s( %s[%s:%s] )",s_b($(j-1)),basename,s_i(pin_array[1]),s_i(pin_array[nmult])
        }
        else printf "\n .%s( %s )", s_b($(j-1)), (nmult>1? "{" pin "}" : pin)
      # new code
      } else {
        printf "\n .%s( %s )" ,s_b($(j-1)),compact_label(pin)
      }
    }
   }
   printf "\n);\n\n"
  }
 previous=""
 next
}

# types of in/out/inout ports of module
netlist==0 && architecture==0 && ($1 in net_types) && ($2 in net_types) {
 if($3 ~ /\[.*\]/) {$3= s_b($3)  "[" s_i($3) "] "; $1="  " $1 }
}

netlist==0 && architecture==0 && ($1 in net_types) && !($2 in net_types) {
 if($2 ~ /\[.*\]/) {$2= "[" s_i($2) "] " s_b($2);  $1="  " $1 }
}

# module port directions
NF==3 && netlist==0 && architecture==0 && ($1 in direction)  {
 if($2 ~ /\[.*\]/) {$2= "[" s_i($2) "] " s_b($2);  $1="  " $1 } ## 20080412 print range also in port decl as req by standard
 #### $2= s_b($2); $1="  " $1
}

/^---- begin user architecture code/ {
 netlist=0
 architecture=1
 next
}

/^---- end user architecture code/ {
 architecture=0
 next
}

/^---- start parameters/{
 parameters=1
 next
}

/^---- end parameters/{
 parameters=0
 next
}


$NF=="," && parameters==1 {
  if($1 ~ /^[+-]?(([0-9]+\.?)|([0-9]*\.[0-9]+))(([aAfFpPnNuUgGtT])|([mM][eE]][gG]))/){
    $1="   " get_number($1)
  }
}

# parameters assignments on instance line
/ *\.[a-zA-Z_0-9]+ \( [^ ]+ \)/ && parameters==1{ $3=get_number($3) }
/ *\.[a-zA-Z_0-9]+\( [^ ]+ \)/ && parameters==1{ $2=get_number($2) }

netlist==1 {
 previous=previous   $0 "\n"
 next
}



{
 print
}


function get_number(n)   # follows SPICE conventions
{
 if(n !~ /^[-.0-9]/) return n   # not a number
 n=toupper(n)
 if(n ~ /A/) return n*1e-18
 if(n ~ /F/) return n*1e-15
 if(n ~ /P/) return n*1e-12
 if(n ~ /N/) return n*1e-9
 if(n ~ /U/) return n*1e-6
 if(n ~ /M[^E][^G]/) return n*1e-3
 if(n ~ /K/) return n*1e3
 if(n ~ /MEG/) return n*1e6
 if(n ~ /G/) return n*1e9
 if(n ~ /T/) return n*1e12
 return n+0.0 # force a numeric ret. value
}


# heap sort, this is a N*log2(N) routine
function hsort(ra,n,    l,j,ir,i,rra)
{
 if(n<=1) return    # bug fix: if only one element in array never returns
 l=int(n/2)+1
 ir=n
 for(;;) {
  if(l>1) {
   rra=ra[--l]
  }
  else {
   rra=ra[ir]
   ra[ir]=ra[1]
   if(--ir==1) {
    ra[1]=rra
    return
   }
  }
  i=l
  j=l*2
  while(j<=ir) {
   if(j<ir && ra[j] >ra[j+1]) ++j
   if(rra>ra[j]) {
    ra[i]=ra[j]
    j+=(i=j)
   }
   else j=ir+1
  }
  ra[i]=rra
 }
}
# check if an array of indexes (sig[3]) arr[1],arr[2].....
# is contigous and decreeasing
# 20140409 handle ascending order as well as descending
function check2(arr,n     ,decreasing,a,name,i,start,ok)
{
 decreasing=""
 net_ascending=0  # 20140416 must be global var !
 name=s_b(arr[1])
 start=s_i(arr[1])
 if(arr[1] !~ /[0-9]+/)
 {
  if(n>1) return 0
  else return 1
 }
 for(i=2;i<=n;i++)
 {
  if(s_b(arr[i]) != name) return 0
  if(arr[1] !~ /[0-9]+/) return 0
  a= s_i(arr[i])+0
  if(a+1 == start)
  {
   if(decreasing=="") {
     start=a
     decreasing=1
   } else if(decreasing==1) {
     start=a
   } else {
     return 0
   }
  }
  else if(a-1 == start)
  {
   if(decreasing=="") {
     start=a
     decreasing=0
   } else if(decreasing==0) {
     start=a
   } else {
     return 0
   }
  }
  else
  {
   return 0
  }
 }

 if(decreasing==0) net_ascending=1  # net_ascending is global 20140416
 else net_ascending=0               # net_ascending is global 20140416
 return 1
}

# check if an array of numbers arr[1],arr[2]..... is contigous and decreeasing
function check(arr,n      ,i,start,ok)
{
 start=arr[1]
 for(i=2;i<=n;i++)
 {
  if(arr[i]+1 == start)
  {
   start=arr[i]
  }
  else
  {
   return 0
  }
 }
 return 1
}


# globals: newlab, startbus, startsig
function compact_label(lab,      n, i, last, lab_array, bus_current, name, busname)
{
  n=split(lab,lab_array,",")
  startbus=-1
  startsig=-1
  newlab=""
  for(i=1;i<=n;i++) {
   if(lab_array[i] ~ /\[/) {
     bus_current=s_i(lab_array[i])

     if( startsig!=-1 &&  lab_array[i] != name )
         print_signal(name)
     if( (startbus!=-1) && (s_b(lab_array[i]) != busname) )
         print_bus(busname, last)
     # 09032004 fix for errors on buses like D[5],D[4],D[2],D[1]
     if( (startbus!=-1) && (s_b(lab_array[i]) == busname) && ((bus_current+1!=last) && (bus_current-1!=last)) )
         print_bus(busname, last)

     if(startbus==-1) { busname=s_b(lab_array[i]); startbus=bus_current; last=bus_current }
     else if(s_b(lab_array[i])==busname && ((bus_current+1==last) || (bus_current-1==last)) ) { last=bus_current }

     if( (startbus!=-1) && i==n )
         print_bus(busname, last)
   }
   else {
     if(s_b(lab_array[i]) != busname&& startbus!=-1)
       print_bus(busname, last)
     if( (startsig!=-1) && ( lab_array[i] != name) )
       print_signal(name)

     if(startsig==-1) { name=lab_array[i] ; startsig++ }
     else if(lab_array[i] == name) { startsig++ }

     if( (startsig!=-1) && i==n )
       print_signal(name)
   }
  }
  sub(/,$/,"",newlab)
  if(newlab ~ /,/) newlab="{" newlab "}"
  return newlab
}

function print_bus(busname, last) {
   if(startbus!=last)
       newlab=newlab busname "[" startbus ":" last "],"
   else
       newlab=newlab busname "[" startbus "],"
   startbus=-1
}

function print_signal(name,       i) {
   if(startsig>0) {
      # newlab = newlab startsig+1 "*" name ","
      for(i = 0; i <= startsig; i++)  newlab = newlab name ","
   } else
      newlab = newlab name ","
   startsig=-1
}


function s_b(n)
{
 sub(/\[.*/,"",n)
 return n;
}

function s_i(n)
{
 if(n ~ /\[/)  {
  sub(/.*\[/,"",n)
  sub(/\]/,"",n)
  return n
 } else return ""
}

function cleanup()
{
 gsub(/\);/," );")
}

function on_single_line(     i,a,count)
{
 cleanup()
 count=0
 i=1
 a=""
 while(1) {
  if(i>NF) { getline;cleanup();i=1 }
  if(count) a=a " "
  a=a $i
  count++
  if($i==");") break
  i++
 }
 $0= a
}

