Skip to content

Commit cd0f79b

Browse files
Merge pull request #5 from phoeniX-Digital-Design/phoeniX-V0.4.1
phoeniX V0.4.1
2 parents 86817ff + 7bfca54 commit cd0f79b

File tree

13 files changed

+1516
-140
lines changed

13 files changed

+1516
-140
lines changed

AssembleX.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# AssembleX V3.0
2+
# RISC-V Assembly Software Assistant for the phoeniX project (https://github.com/phoeniX-Digital-Design/phoeniX)
3+
4+
# Description: AssembleX main code
5+
# Copyright 2024 Iran University of Science and Technology. <phoenix.digital.electronics@gmail.com>
6+
7+
# Permission to use, copy, modify, and/or distribute this software for any
8+
# purpose with or without fee is hereby granted, provided that the above
9+
# copyright notice and this permission notice appear in all copies.
10+
11+
import sys
12+
import os
13+
import glob
14+
15+
from Software.AssembleX.variables import *
16+
from Software.AssembleX.assembler import assembler
17+
from Software.AssembleX.address_mapping import address_mapping
18+
from Software.AssembleX.address_mapping import label_mapping
19+
from Software.AssembleX.address_mapping import define_reset_address
20+
from Software.AssembleX.data_conversion import binary_to_hex
21+
from Software.AssembleX.data_conversion import ascii_to_hex
22+
23+
testbench_file = "phoeniX_Testbench.v"
24+
option = sys.argv[1]
25+
project_name = sys.argv[2]
26+
output_name = project_name + "_firmware" + ".hex"
27+
28+
if option == 'sample':
29+
directory = "Sample_Assembly_Codes"
30+
elif option == 'code':
31+
directory = "User_Codes"
32+
else:
33+
raise ValueError("Options are: sample, code")
34+
35+
print("\nAssembleX V3.0 - RV32IM Assembly Code Executant Software")
36+
print("Iran University of Science and Technology - Summer 2024")
37+
print("--------------------------------------------------------")
38+
39+
try:
40+
source_path = list(glob.iglob(os.path.join("Software", directory, project_name, '*' + ".s")))[0]
41+
firmware_hex_path = os.path.join("Software", directory, project_name, output_name)
42+
print(source_path)
43+
print(firmware_hex_path)
44+
#source_path = sys.argv[1]
45+
#firmware_hex_path = sys.argv[1].rstrip('.s') + '_firmware.hex'
46+
except:
47+
print('INFO: No arguments/unsupported arguments\n')
48+
49+
try:
50+
source_file = open(source_path, "r")
51+
source_code_unformatted = source_file.read().splitlines()
52+
print("INFO: Source file opened successfully\n")
53+
except:
54+
print("FATAL ERROR: Unable to open source file\n")
55+
exit(1)
56+
57+
print('Assembly code pre-processing')
58+
print('----------------------------')
59+
source_code = []
60+
for line in source_code_unformatted:
61+
instruction_format_space = " ".join(line.split())
62+
instruction_format_comments = " #".join(instruction_format_space.split('#', 2))
63+
instruction_format_leadspace = instruction_format_comments.lstrip()
64+
source_code.append(instruction_format_leadspace)
65+
66+
# Re-format immediate expressions
67+
processed_code_1 = []
68+
for line in source_code:
69+
arguments = line.split(',')
70+
try:
71+
if arguments[0][0] == '#':
72+
processed_code_1.append(line)
73+
continue
74+
elif arguments[0] == '.RESET_ADDRESS':
75+
processed_code_1.append(line)
76+
continue
77+
except:
78+
processed_code_1.append(line)
79+
continue
80+
# Check for 2 argument immediate expressions: 'x(y)' -> parse: 'y x'
81+
# Check for ASCII: char -> hex
82+
parse_ascii = [False]
83+
try:
84+
arguement_2_wc = arguments[1]
85+
arguement_2_wc = arguement_2_wc.split('#', 2)
86+
len_arguement_2_wc = len(arguement_2_wc)
87+
arguement_2 = [arguement_2_wc[0]]
88+
ascii_to_hex(arguement_2)
89+
arguement_2_pp = arguement_2[0].replace(')', '(')
90+
arguement_2_pp = "".join(arguement_2_pp.split())
91+
arguement_2_list = arguement_2_pp.split('(')
92+
if len_arguement_2_wc == 2:
93+
arguments[1] = ' ' + arguement_2_list[1] + ', ' + arguement_2_list[0] + ' ' + '#' + arguement_2_wc[1] # Modified expression
94+
else: # No inline comment
95+
arguments[1] = ' ' + arguement_2_list[1] + ', ' + arguement_2_list[0] # Modified expression
96+
processed_code_1.append(",".join(arguments))
97+
except:
98+
if parse_ascii[0]:
99+
if len_arguement_2_wc == 2:
100+
arguments[1] = arguement_2[0] + ' ' + '#' + arguement_2_wc[1] # Modified expression
101+
else:
102+
arguments[1] = arguement_2[0] # Modified expression
103+
processed_code_1.append(",".join(arguments))
104+
else:
105+
processed_code_1.append(line)
106+
continue
107+
108+
# Remove "," change "," -> " "
109+
processed_code_2 = []
110+
for line in processed_code_1:
111+
line = line.replace(',', ' ')
112+
processed_code_2.append(" ".join(line.split()))
113+
114+
lines_of_code = len(processed_code_2)
115+
print('Lines of code =', lines_of_code, '\n')
116+
117+
start_address = define_reset_address(processed_code_2[0])
118+
119+
for line in source_code:
120+
label_state = label_mapping(start_address, line, instruction_counter,
121+
expected_instructions_count, lable_counter,
122+
label_list, label_address_list)
123+
address_mapping(lable_counter, label_list, label_address_list)
124+
125+
# Parser
126+
print('')
127+
print('Parser')
128+
print('------')
129+
pc[0] = start_address
130+
for line in processed_code_2:
131+
instruction_sts = assembler(pc, line, line_number, error_flag, error_counter, bin_instruction)
132+
line_number = line_number + 1
133+
134+
# Summary
135+
print('\nSummary')
136+
print('-------')
137+
if error_flag[0] == 0:
138+
print('- Lines of code (source) = ', lines_of_code)
139+
print('- Assembled instructions = ', instruction_counter[0])
140+
print('- Instructions with ERRORS = ', error_counter[0])
141+
binary_to_hex(bin_instruction, hex_instruction)
142+
try:
143+
# HEX firmware file write
144+
file = open(firmware_hex_path, "w")
145+
for line in hex_instruction:
146+
file.write(line + '\n')
147+
print('\nDONE: Successfully created FIRMWARE file\n')
148+
file.close()
149+
except:
150+
print('\nFATAL ERROR: Unable to create FIRMWARE file\n')
151+
else:
152+
print('- Lines of code (source) = ', lines_of_code)
153+
print('- Assembled instructions = ', instruction_counter[0])
154+
print('- Instructions with ERRORS = ', error_counter[0])
155+
print('\nFAIL:Failed to parse the assembly code due to errors')
156+
157+
# Change firmware in the testbench file
158+
with open(testbench_file, 'r') as file:
159+
lines = file.readlines()
160+
# Edit source files of testbench names
161+
with open(testbench_file, 'w') as file:
162+
for line in lines:
163+
# Change instruction memory source file
164+
if line.startswith(" `define FIRMWARE "):
165+
print("Line found!")
166+
# Modify the input file name
167+
firmware_hex_path = firmware_hex_path.replace("\\", "\\\\")
168+
print(firmware_hex_path)
169+
modified_line = line.replace(line,' `define FIRMWARE '+ '"' + firmware_hex_path + '"' +'\n' )
170+
print(modified_line)
171+
file.write(modified_line)
172+
else:
173+
file.write(line)
174+
175+
# OS: cmd commands to execute Verilog simulations:
176+
os.system("iverilog -IModules -o phoeniX.vvp phoeniX_Testbench.v")
177+
os.system("vvp phoeniX.vvp")
178+
os.system("gtkwave phoeniX.gtkw")

AssembleX_V1.0.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
![License](https://img.shields.io/github/license/phoeniX-Digital-Design/AssembleX?color=dark-green)
22
![GCC Test](https://img.shields.io/badge/GCC_tests-passed-dark_green)
3-
![Version](https://img.shields.io/badge/Version-0.4-blue)
3+
![Version](https://img.shields.io/badge/Version-0.4.1-blue)
44
![ISA](https://img.shields.io/badge/RV32-IEM_extension-blue)
55

66
<picture>
@@ -34,6 +34,7 @@ Publications:
3434

3535
- A. Delavari, F. Ghoreishy, H. S. Shahhoseini and S. Mirzakuchaki (2023), “phoeniX: A RISC-V Platform for Approximate Computing Technical Specifications,” [Online]. Available: http://www.iust.ac.ir/content/76158/phoeniX-POINTS--A-RISC-V-Platform-for-Approximate-Computing
3636

37+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
3738

3839
- Designed By: [Arvin Delavari](https://github.com/ArvinDelavari) and [Faraz Ghoreishy](https://github.com/FarazGhoreishy)
3940
- Contact us: arvin_delavari@elec.iust.ac.ir - faraz_ghoreishy@elec.iust.ac.ir
@@ -271,15 +272,14 @@ We have meticulously developed a lightweight and user-friendly software solution
271272

272273
This tool enhances the efficiency of the code execution process, offering a streamlined experience for users seeking to enter the realm of assembly programming on pheoniX processor in a very simple and user-friendly way.
273274

274-
Before running the script, note that the assembly output of the Venus Simulator for the code must be also saved in the project directory.
275-
To run any of these sample projects simply run python `AssembleX_V1.0.py sample` followed by the name of the project passed as a variable named project to the Python script.
275+
To run any of these sample projects simply run python `AssembleX.py sample` followed by the name of the project passed as a variable named project to the Python script.
276276
The input command format for the terminal follows the structure illustrated below:
277277
```shell
278-
python AssembleX_V1.0.py sample {project_name}
278+
python AssembleX.py sample {project_name}
279279
```
280280
For example:
281281
```shell
282-
python AssembleX_V1.0.py sample fibonacci
282+
python AssembleX.py sample fibonacci
283283
```
284284
After execution of this script, firmware file will be generated and this final file can be directly fed to our Verilog testbench. AssembleX automatically runs the testbench and calls upon gtkwave to display the selected signals in the waveform viewer application, gtkwave.
285285
</div>
@@ -290,7 +290,7 @@ After execution of this script, firmware file will be generated and this final f
290290
In order to run your own code on phoeniX, create a directory named to your project such as `/my_project` in `/Software/User_Codes`. Put all your `user_code.s` files in my_project and run the following command from the main directory:
291291

292292
```shell
293-
python AssembleX_V1.0.py code my_project
293+
python AssembleX.py code my_project
294294
```
295295

296296
Provided that you name your project sub-directory correctly the AssembleX software will create `my_project_firmware.hex` and fed it directly to the testbench of phoeniX processor. After that, iverilog and GTKWave are used to compile the design and view the selected waveforms.

Software/AssembleX/address_mapping.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# AssembleX V3.0
2+
# RISC-V Assembly Software Assistant for the phoeniX project (https://github.com/phoeniX-Digital-Design/phoeniX)
3+
4+
# Description: Address mapping and translation functions
5+
# Copyright 2024 Iran University of Science and Technology. <phoenix.digital.electronics@gmail.com>
6+
7+
# Permission to use, copy, modify, and/or distribute this software for any
8+
# purpose with or without fee is hereby granted, provided that the above
9+
# copyright notice and this permission notice appear in all copies.
10+
11+
def define_reset_address(first_line):
12+
start_address = first_line.split()
13+
try:
14+
if start_address[0] == '.RESET_ADDRESS':
15+
address_pre = start_address[1]
16+
if address_pre[0:2] == '0x' or address_pre[0:2] == '0X':
17+
address = (int(address_pre, base=16) >> 2) * 4
18+
else:
19+
address = (int(address_pre) >> 2) * 4
20+
print('INFO: .RESET_ADDRESS set to', "0x{:08x}".format(address))
21+
return address
22+
else:
23+
print('WARNING: .RESET_ADDRESS not defined. -> .RESET_ADDRESS overridden to 0x00000000\n')
24+
return 0
25+
except:
26+
print('WARNING: .RESET_ADDRESS not defined. -> .RESET_ADDRESS overridden to 0x00000000\n')
27+
return 0
28+
29+
def label_mapping(base_address, line, instrcnt, expected_instructions_count, label_counter, label_list, label_address_list):
30+
words = line.split()
31+
# Check if blank line or comment or start_address
32+
try:
33+
if words[0][0] == '#':
34+
# Ignore comment and move on
35+
return 0
36+
elif words[0] == '.RESET_ADDRESS':
37+
# Ignore start_address
38+
return 0
39+
except:
40+
# Ignore blank line and move on
41+
return 0
42+
43+
if len(words) == 1 and line[-1] == ':':
44+
label = line.split(':')[0]
45+
label_counter[0] = label_counter[0] + 1
46+
label_list.append(label)
47+
label_address_list.append(base_address + int(expected_instructions_count[0]) * 4)
48+
elif len(words) > 1 and words[0][-1] == ':' and words[1][0] == '#': # Decodes labels with comments
49+
label = line.split(':')[0]
50+
label_counter[0] = label_counter[0] + 1
51+
label_list.append(label)
52+
label_address_list.append(base_address + int(expected_instructions_count[0]) * 4)
53+
else:
54+
# It is an instruction
55+
instrcnt[0] = instrcnt[0] + 1
56+
if words[0] == 'LI' or words[0] == 'li' or words[0] == 'LA' or words[0] == 'la':
57+
offset = 2 # Because LI = expands to two instructions
58+
else:
59+
offset = 1
60+
expected_instructions_count[0] = expected_instructions_count[0] + offset
61+
62+
def address_mapping(label_counter, label_list, label_address_list):
63+
print('Address Mapping')
64+
print('---------------')
65+
for i in range(label_counter[0]):
66+
print('%+-8s' % label_list[i], ": 0x{:08x}".format(label_address_list[i]))

0 commit comments

Comments
 (0)