blob: a4015e1de624af7ed7379467869afffa22cdcb6f [file] [log] [blame]
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""Command-line tool to perform LEC on all AES S-Box implementations using Yosys
"""
import glob
import os
import subprocess
import sys
from pathlib import Path
def replace_module_name(file_name, string_search, string_replace):
fin = open(file_name, 'rt')
data = fin.read()
data = data.replace(string_search, string_replace)
fin.close()
fin = open(file_name, 'wt')
fin.write(data)
fin.close()
path_root = str(Path(__file__).resolve().parents[5])
rtl_path = path_root + '/hw/ip/aes/rtl/'
prim_path = path_root + '/hw/ip/prim/rtl/'
prim_xilinx_path = path_root + '/hw/ip/prim_xilinx/rtl/'
# Specify S-Box reference implementation
impl_gold = 'aes_sbox_lut'
file_pkg = 'aes_pkg.sv'
# Specify dependencies
dep_list = [
rtl_path + 'aes_pkg.sv',
rtl_path + 'aes_reg_pkg.sv',
rtl_path + 'aes_sbox_canright_pkg.sv',
prim_xilinx_path + 'prim_xilinx_buf.sv',
prim_xilinx_path + 'prim_xilinx_xor2.sv',
]
# Detect all S-Box implementations to check
impl_list = glob.glob(rtl_path + 'aes_sbox_*.sv')
impl_list = [
impl_dut.replace(rtl_path, '').replace('.sv', '') for impl_dut in impl_list
]
# Remove multicycle implementations, we can't perform LEC for those.
impl_list.remove('aes_sbox_dom')
# Remove reference implementation and package files.
impl_list.remove(impl_gold)
impl_list.remove('aes_sbox_canright_pkg')
# Create workdir
os.makedirs('scratch', exist_ok=True)
# Convert the reference implementation to Verilog
sv2v_cmd = ['sv2v', '-I', prim_path
] + dep_list + [rtl_path + impl_gold + '.sv']
with open('scratch/aes_sbox_ref.v', 'w') as outfile:
subprocess.run(sv2v_cmd, stdout=outfile)
# Change module name
replace_module_name('scratch/aes_sbox_ref.v', impl_gold, 'aes_sbox_ref')
print('Running LEC on ' + str(len(impl_list)) + ' S-Box implementation(s)...')
# Check every implementation separately
num_impl_success = 0
num_impl_failed = 0
for impl_dut in impl_list:
# Prepare Verilog conversion of DUT
sv2v_cmd = ['sv2v', '-I', prim_path
] + dep_list + [rtl_path + impl_dut + '.sv']
# Masked implementations require a wrapper
if 'masked' in impl_dut:
file_wrap = 'aes_sbox_masked_wrapper.sv'
# Copy the wrapper
os.system('cp ' + file_wrap + ' scratch/' + file_wrap)
# Change module name
replace_module_name('scratch/' + file_wrap, 'aes_sbox_masked',
impl_dut)
# Include in conversion
sv2v_cmd.insert(3, 'scratch/' + file_wrap)
# Convert DUT to Verilog
with open('scratch/aes_sbox_dut.v', 'w') as outfile:
subprocess.run(sv2v_cmd, stdout=outfile)
# Change module name
if 'masked' in impl_dut:
replace_module_name('scratch/aes_sbox_dut.v', impl_dut + '_wrapper',
'aes_sbox_dut')
else:
replace_module_name('scratch/aes_sbox_dut.v', impl_dut, 'aes_sbox_dut')
# Use Xilinx implementation for primitives
replace_module_name('scratch/aes_sbox_dut.v', 'prim_xilinx_', 'prim_')
# Perform LEC in Yosys
yosys_cmd = ['yosys', '../aes_sbox_lec.ys']
lec_log = 'scratch/' + impl_dut + '_lec.log'
with open(lec_log, 'w') as outfile:
subprocess.run(yosys_cmd, cwd="scratch", stdout=outfile)
# Get actual LEC output
lec_string = 'Trying to prove $equiv'
lec_lines = []
with open(lec_log, 'rt') as fin:
data = fin.read()
for line in data.split('\n'):
if lec_string in line:
lec_lines.append(line)
# Check for LEC output
num_lec_success = 0
num_lec_failed = 0
for line in lec_lines:
if 'success!' in line:
num_lec_success = num_lec_success + 1
if 'failed.' in line:
num_lec_failed = num_lec_failed + 1
if (len(lec_lines) == 0) or (len(lec_lines) !=
num_lec_success) or (num_lec_failed > 0):
print("LEC failed: \t\t\t" + impl_dut + '.sv\n-> ' + 'Check ' +
lec_log + ' for details.')
num_impl_failed = num_impl_failed + 1
else:
print("LEC completed successfully: \t" + impl_dut + '.sv')
num_impl_success = num_impl_success + 1
# Print output
print('Done.')
if (num_impl_success == len(impl_list)) and not num_impl_failed:
print('SUCCESS!')
else:
print('FAILED for ' + str(num_impl_failed) + ' implementation(s).')
sys.exit(1)