2018-06-05 17:24:31 -07:00
|
|
|
require "option_parser"
|
|
|
|
|
|
|
|
input = ""
|
|
|
|
output = ""
|
|
|
|
unit = "ns"
|
|
|
|
cycle = 20
|
|
|
|
start = 20
|
|
|
|
|
|
|
|
prog_pin = "{sim:/cpu/prog}"
|
|
|
|
inst_pin = "{sim:/cpu/pinst}"
|
|
|
|
addr_pin = "{sim:/cpu/paddr}"
|
|
|
|
|
|
|
|
OptionParser.parse! do |parser|
|
|
|
|
parser.banner = "Usage: assembler [arguments]"
|
|
|
|
parser.on("-f NAME", "--from=NAME", "Select the input file.") do |file|
|
|
|
|
input = file
|
|
|
|
end
|
|
|
|
parser.on("-t NAME", "--to=NAME", "Select the output file.") do |file|
|
|
|
|
output = file
|
|
|
|
end
|
|
|
|
parser.on("-u UNIT", "--unit=UNIT", "select the time units.") do |u|
|
|
|
|
unit = u
|
|
|
|
end
|
|
|
|
parser.on("-c CYCLE", "--cycle=CYCLE", "select the clock cycle speed") do |c|
|
|
|
|
cycle = c.to_i? || 20
|
|
|
|
end
|
|
|
|
parser.on("-b BEGIN", "--begin=BEGIN", "select the beginning of the programming mode") do |b|
|
|
|
|
start = b.to_i? || 20
|
|
|
|
end
|
|
|
|
parser.on("-p PPIN", "--prog-pin=PPIN", "select the programming enable pin") do |pp|
|
|
|
|
prog_pin = pp
|
|
|
|
end
|
|
|
|
parser.on("-a APIN", "--addr-pin=APIN", "select address pin") do |ap|
|
|
|
|
addr_pin = ap
|
|
|
|
end
|
|
|
|
parser.on("-i IPIN", "--inst-pin=IPIN", "select instruction pin") do |ip|
|
|
|
|
inst_pin = ip
|
|
|
|
end
|
2018-06-05 17:28:24 -07:00
|
|
|
parser.on("-h", "--help") { puts parser }
|
2018-06-05 17:24:31 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
enum InstType
|
|
|
|
RInst,
|
|
|
|
IInst,
|
|
|
|
JInst,
|
|
|
|
SInst,
|
|
|
|
DInst
|
|
|
|
end
|
|
|
|
|
|
|
|
# w c j s o p
|
|
|
|
# 0 0 0 0 0 0
|
|
|
|
|
|
|
|
INSTS = {
|
|
|
|
"add" => { "100010", InstType::RInst },
|
|
|
|
"sub" => { "100110", InstType::RInst },
|
|
|
|
"and" => { "100000", InstType::RInst },
|
|
|
|
"or" => { "100001", InstType::RInst },
|
|
|
|
"slt" => { "100011", InstType::RInst },
|
|
|
|
"addc" => { "110010", InstType::IInst },
|
|
|
|
"subc" => { "110110", InstType::IInst },
|
|
|
|
"andc" => { "110000", InstType::IInst },
|
|
|
|
"orc" => { "110001", InstType::IInst },
|
|
|
|
"sltc" => { "110011", InstType::IInst },
|
|
|
|
"jmp" => { "001100", InstType::JInst },
|
|
|
|
"jez" => { "001000", InstType::JInst },
|
|
|
|
"jnz" => { "001001", InstType::JInst },
|
|
|
|
"disp" => { "000000", InstType::DInst},
|
|
|
|
"read" => { "100011", InstType::SInst},
|
|
|
|
}
|
|
|
|
|
|
|
|
def decode_register(reg)
|
|
|
|
if reg.size != 2
|
|
|
|
puts "Invalid register operand size."
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if reg[0] != 'r'
|
|
|
|
puts "Expected register parameter."
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if val = reg[1].to_i?
|
|
|
|
if val <= 7
|
|
|
|
return val
|
|
|
|
else
|
|
|
|
puts "Out of bounds register number"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
puts "Register must be a number."
|
|
|
|
end
|
|
|
|
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def decode_int(param)
|
|
|
|
if param.starts_with? "0x"
|
|
|
|
return param[2..-1].to_i?(16)
|
|
|
|
elsif param.starts_with? "0b"
|
|
|
|
return param[2..-1].to_i?(2)
|
|
|
|
else
|
|
|
|
return param.to_i?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def decode_params(params, string): Array(Int32|String)?
|
|
|
|
params = params.dup
|
|
|
|
|
|
|
|
if params.size != string.size
|
|
|
|
puts "Invalid number of parameters."
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
output = string.chars.map do |char|
|
|
|
|
param = params.shift
|
|
|
|
case char
|
|
|
|
when 'r'
|
|
|
|
decode_register(param)
|
|
|
|
when 'c'
|
|
|
|
decode_int(param)
|
|
|
|
when 's'
|
|
|
|
param
|
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if output.includes? nil
|
|
|
|
return nil
|
|
|
|
else
|
|
|
|
return output.compact
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def decode_inst(labels, params)
|
|
|
|
inst = params.shift
|
|
|
|
if inst_data = INSTS[inst]?
|
|
|
|
code, type = inst_data
|
|
|
|
case type
|
|
|
|
when InstType::RInst
|
|
|
|
data = decode_params(params, "rrr")
|
|
|
|
return nil unless data
|
|
|
|
return code +
|
|
|
|
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
data[1].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
data[2].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
"00000000000000000"
|
|
|
|
when InstType::IInst
|
|
|
|
data = decode_params(params, "rrc")
|
|
|
|
return nil unless data
|
|
|
|
return code +
|
|
|
|
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
data[1].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
"0000" +
|
|
|
|
data[2].as(Int32).to_s(2).rjust(16, '0')
|
|
|
|
when InstType::JInst
|
|
|
|
if inst == "jmp"
|
|
|
|
data = decode_params(params, "s")
|
|
|
|
return nil unless data
|
|
|
|
return code +
|
|
|
|
"0000000000" + labels[data[0].as(String)].to_s(2).rjust(16, '0')
|
|
|
|
else
|
|
|
|
data = decode_params(params, "rs")
|
|
|
|
return nil unless data
|
|
|
|
return code +
|
|
|
|
"000" +
|
|
|
|
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
"0000" +
|
|
|
|
labels[data[1].as(String)].to_s(2).rjust(16, '0')
|
|
|
|
end
|
|
|
|
when InstType::DInst
|
|
|
|
data = decode_params(params, "r")
|
|
|
|
return nil unless data
|
|
|
|
return code +
|
|
|
|
"000" +
|
|
|
|
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
"000" +
|
|
|
|
"00000000000000000"
|
|
|
|
when InstType::SInst
|
|
|
|
data = decode_params(params, "r")
|
|
|
|
return nil unless data
|
|
|
|
return code +
|
|
|
|
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
|
|
|
"000" +
|
|
|
|
"000" +
|
|
|
|
"00000000000000000"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def decode_insts(labels, insts)
|
|
|
|
decoded = insts.map {|inst| decode_inst(labels, inst) }
|
|
|
|
if decoded.includes? nil
|
|
|
|
return nil
|
|
|
|
else
|
|
|
|
return decoded.compact
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if input == ""
|
|
|
|
puts "Please select an input file."
|
|
|
|
elsif output == ""
|
|
|
|
puts "Please select an output file."
|
|
|
|
elsif !File.exists?(input) || File.directory?(input)
|
|
|
|
puts "Please select a valid input file."
|
|
|
|
else
|
|
|
|
labels = {} of String => Int32
|
|
|
|
raw_instructions = [] of Array(String)
|
|
|
|
lines = File.read_lines(input)
|
|
|
|
lines.map_with_index do |line, index|
|
|
|
|
beforecomment = line.split(/#/).first.strip.split /\s+/
|
|
|
|
if beforecomment[0].ends_with? ":"
|
|
|
|
labels[beforecomment.shift.split(/:/).first] = index
|
|
|
|
end
|
|
|
|
raw_instructions.push(beforecomment) unless beforecomment.empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
if instructions = decode_insts(labels, raw_instructions)
|
|
|
|
dofile = File.open(output, "w")
|
|
|
|
dofile << "force " << prog_pin << " 1 @ " << start << unit << "\n"
|
|
|
|
instructions.each_with_index do |instruction, index|
|
|
|
|
timestamp = (start + index * cycle)
|
|
|
|
dofile << "force " << inst_pin << " {16'b" << instruction << "} @ " << timestamp << unit << "\n"
|
|
|
|
dofile << "force " << addr_pin << " " << index.to_s << " @ " << timestamp << unit << "\n"
|
|
|
|
end
|
|
|
|
dofile << "force " << prog_pin << " 0 @ " << (start + instructions.size * cycle) << unit << "\n"
|
|
|
|
dofile.close
|
|
|
|
else
|
|
|
|
end
|
|
|
|
end
|