From 326bda000e182864723ea28455bf62364b08c7fb Mon Sep 17 00:00:00 2001 From: jens Date: Thu, 30 Jun 2022 16:56:54 +0200 Subject: [PATCH] - added tools folder --- tools/jcpu/eval_scan.rb | 42 ++ tools/jcpu/insrom.rb | 46 ++ tools/jcpu/irom.vhd.tpl | 32 ++ tools/jcpu/irom_ld.vhd.tpl | 151 +++++++ tools/jcpu/jasm.bak.rb | 494 +++++++++++++++++++++ tools/jcpu/jasm.rb | 811 ++++++++++++++++++++++++++++++++++ tools/jcpu/lib/cg.inc.jsm | 72 +++ tools/jcpu/lib/cregs.inc.jsm | 7 + tools/jcpu/lib/delays.inc.jsm | 64 +++ tools/jcpu/lib/lcd.inc.jsm | 208 +++++++++ tools/jcpu/lib/mul8x8.inc.jsm | 42 ++ tools/jcpu/lib/uart.inc.jsm | 58 +++ tools/jcpu/lib/utils.inc.jsm | 32 ++ tools/jcpu/opc.list | 64 +++ tools/jcpu/regx.rb | 20 + tools/jcpu/rom.tcl.tpl | 61 +++ tools/jcpu/templates.rb | 44 ++ tools/jcpu/xrom.vhd.tpl | 32 ++ tools/jcpu/xrom_ld.vhd.tpl | 151 +++++++ tools/mips/flashgen | Bin 0 -> 17544 bytes tools/mips/flashgen_tlb | Bin 0 -> 17544 bytes tools/mips/packhex | Bin 0 -> 18816 bytes tools/mips/ramgen | Bin 0 -> 25728 bytes tools/mips/romgen | Bin 0 -> 25928 bytes 24 files changed, 2431 insertions(+) create mode 100755 tools/jcpu/eval_scan.rb create mode 100755 tools/jcpu/insrom.rb create mode 100644 tools/jcpu/irom.vhd.tpl create mode 100644 tools/jcpu/irom_ld.vhd.tpl create mode 100755 tools/jcpu/jasm.bak.rb create mode 100755 tools/jcpu/jasm.rb create mode 100644 tools/jcpu/lib/cg.inc.jsm create mode 100644 tools/jcpu/lib/cregs.inc.jsm create mode 100644 tools/jcpu/lib/delays.inc.jsm create mode 100644 tools/jcpu/lib/lcd.inc.jsm create mode 100644 tools/jcpu/lib/mul8x8.inc.jsm create mode 100644 tools/jcpu/lib/uart.inc.jsm create mode 100644 tools/jcpu/lib/utils.inc.jsm create mode 100644 tools/jcpu/opc.list create mode 100755 tools/jcpu/regx.rb create mode 100644 tools/jcpu/rom.tcl.tpl create mode 100755 tools/jcpu/templates.rb create mode 100644 tools/jcpu/xrom.vhd.tpl create mode 100644 tools/jcpu/xrom_ld.vhd.tpl create mode 100755 tools/mips/flashgen create mode 100755 tools/mips/flashgen_tlb create mode 100755 tools/mips/packhex create mode 100755 tools/mips/ramgen create mode 100755 tools/mips/romgen diff --git a/tools/jcpu/eval_scan.rb b/tools/jcpu/eval_scan.rb new file mode 100755 index 0000000..0c38397 --- /dev/null +++ b/tools/jcpu/eval_scan.rb @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby + +str1 = %Q!lbl: dc "Hallo", ", ", 0, 1, " der","Jens"! + +i=0; + +def get_stri_lit (str) + res = [""]; + state = "out" + + j = 0; + k = 0; + for i in (0..str.length-1) + c = str[i]; + nstate = state + case state + when "out" + if c == "\""[0] + nstate = "in" + end + when "in" + if c == "\""[0] + nstate = "out" + if j > 0 + k = k + 1; + res[k] = "" + end + else + res[k] = res[k] + format("%c", c); + j = j + 1; + end + end + state = nstate + end; + return res +end + +get_stri_lit(str1).each do |word| + puts word +end + + \ No newline at end of file diff --git a/tools/jcpu/insrom.rb b/tools/jcpu/insrom.rb new file mode 100755 index 0000000..6713f9f --- /dev/null +++ b/tools/jcpu/insrom.rb @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby + +# ---------------------------------------------------------------------- +# Project: JCPU, a portable 8-bit RISC CPU written in VHDL +# This file: Insertion of code fragments into templates +# +# Copyright (C) 2007 J. Ahrensfeld +# +# 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 3 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, see . +# +# For questions and ideas, please contact the author at jens@jayfield.org +# +# --------------------------------------------------------------------- + +arg = $* +rom_filename = arg[0].to_s +tpl_filename = arg[1].to_s + +subst_pattern = "JASM_ROM_INSERT_HERE" + +# -------------------------------------------------------- +# Open file +# -------------------------------------------------------- +romfile = File.open(rom_filename, "r") +tplfile = File.open(tpl_filename, "r") +re = Regexp.new(subst_pattern) + +while (line = tplfile.gets) + puts line + line.scan(re).each do |word| + romfile.each do |raw_line| + puts raw_line + end + end +end diff --git a/tools/jcpu/irom.vhd.tpl b/tools/jcpu/irom.vhd.tpl new file mode 100644 index 0000000..5f11f86 --- /dev/null +++ b/tools/jcpu/irom.vhd.tpl @@ -0,0 +1,32 @@ +------------------------------------------------------------------------- +-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL +-- This file: The ROM file for use in your VHDL design +-- +-- Copyright (C) 2007 J. Ahrensfeld +-- +-- 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 3 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, see . +-- +-- For questions and ideas, please contact the author at jens@jayfield.org +-- +-------------------------------------------------------------------------- + +LIBRARY ieee; +use IEEE.STD_LOGIC_1164.ALL; +USE ieee.numeric_std.ALL; + +library work; +use work.cpu_pkg.all; + +-- JASM_ROM_INSERT_HERE + diff --git a/tools/jcpu/irom_ld.vhd.tpl b/tools/jcpu/irom_ld.vhd.tpl new file mode 100644 index 0000000..eaa41f3 --- /dev/null +++ b/tools/jcpu/irom_ld.vhd.tpl @@ -0,0 +1,151 @@ +------------------------------------------------------------------------- +-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL +-- This file: loadable ROM +-- +-- Copyright (C) 2007 J. Ahrensfeld +-- +-- 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 3 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, see . +-- +-- For questions and ideas, please contact the author at jens@jayfield.org +-- +-------------------------------------------------------------------------- + +LIBRARY ieee; +use IEEE.STD_LOGIC_1164.ALL; +USE ieee.numeric_std.ALL; + +library UNISIM; +use UNISIM.VComponents.all; + +library work; +use work.cpu_pkg.all; + +ENTITY irom IS + Port ( + clk : in STD_LOGIC; + ce : in STD_LOGIC; + addr : in inst_addr_t; + dout : out inst_t + ); + +END irom; + +ARCHITECTURE loadable OF irom IS + + -- JASM_ROM_INSERT_HERE + + signal jtag_ld_clk : STD_LOGIC; + signal jtag_ld_we : STD_LOGIC; + signal jtag_ld_addr : inst_addr_t; + signal jtag_ld_dout : inst_t; + signal jtag_ld_din : inst_t; + signal bs_rst, bs_sel, bs_shift, bs_tdi, bs_tdo : std_logic; + signal bs_capture, bs_clk0, bs_clk1, bs_update0, bs_update1 : std_logic; + signal user_regi, user_rego : unsigned (31 downto 0); + constant id : unsigned (31 downto 0) := X"BABE" & X"BEEF"; + +begin + +-------------------------------------------------------------------------- +-- Virtex-4: JTAG Loader +-------------------------------------------------------------------------- + i00_BUFG : BUFG + port map + ( + O => bs_clk1, + I => bs_clk0 + ); + + i01_BUFG : BUFG + port map + ( + O => bs_update1, + I => bs_update0 + ); + + BSCAN_VIRTEX4_inst1 : BSCAN_VIRTEX4 + generic map + ( + JTAG_CHAIN => 1 -- Value to set BSCAN site of device. Possible values: (1,2,3 or 4) + ) + port map + ( + CAPTURE => bs_capture, -- CAPTURE output from TAP controller + DRCK => bs_clk0, -- Data register output for USER functions + RESET => bs_rst, -- Reset output from TAP controller + SEL => bs_sel, -- USER active output + SHIFT => bs_shift, -- SHIFT output from TAP controller + TDI => bs_tdi, -- TDI output from TAP controller + UPDATE => bs_update0, -- UPDATE output from TAP controller + TDO => bs_tdo -- Data input for USER function + ); + + jtag_ld_addr <= user_regi(user_regi'left downto user_regi'left-inst_addr_t'length+1); + jtag_ld_din <= user_regi(inst_t'length-1 downto 0); + jtag_ld_clk <= bs_update1; + jtag_ld_we <= bs_sel; + +sipo: + process (bs_rst, bs_clk1, bs_tdi, bs_shift) + begin + if bs_rst = '1' then + user_regi <= (others => '0'); + elsif rising_edge(bs_clk1) then + if bs_shift = '1' then + user_regi <= bs_tdi & user_regi(user_regi'left downto 1); + end if; + end if; + end process; + +piso: + process (bs_rst, bs_clk1, bs_shift, user_rego) + begin + bs_tdo <= user_rego(0); + if bs_rst = '1' then + user_rego <= (others => '0'); + elsif rising_edge(bs_clk1) then + if bs_shift = '1' then + user_rego <= user_rego(0) & user_rego(user_rego'left downto 1); + else + user_rego <= (user_rego'left downto inst_t'length => '0') & jtag_ld_dout; +-- user_rego <= id; + end if; + end if; + end process; + +-------------------------------------------------------------------------- +-- ROM Read/Write +-------------------------------------------------------------------------- +PROM_READ: + process(clk, ce) + begin + if rising_edge(clk) and ce = '1' then + dout <= imem_rom(to_integer(addr)); + end if; + end process; + +PROM_WRITE: + process(jtag_ld_clk, jtag_ld_we) + begin + if rising_edge(jtag_ld_clk) then + if jtag_ld_we = '1' then + imem_rom(to_integer(jtag_ld_addr)) <= jtag_ld_din; + else + jtag_ld_dout <= imem_rom(to_integer(jtag_ld_addr)); + end if; + end if; + end process; + +-------------------------------------------------------------------------- +end loadable; diff --git a/tools/jcpu/jasm.bak.rb b/tools/jcpu/jasm.bak.rb new file mode 100755 index 0000000..4c9feb0 --- /dev/null +++ b/tools/jcpu/jasm.bak.rb @@ -0,0 +1,494 @@ +#!/usr/bin/env ruby + +# ---------------------------------------------------------------------- +# Project: JCPU, a portable 8-bit RISC CPU written in VHDL +# This file: A small assembler for the JCPU + +# Copyright (C) 2007 J. Ahrensfeld + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# For questions and ideas, please contact the author at jens@jayfield.org + +# --------------------------------------------------------------------- + +arg = $* +MAX_SIZE_ROM = 1024 +PROJECT_NAME = arg.to_s.gsub(/\..*/, "") +ENTITY_NAME = "rom" +ENTITY_DATA_TYPE = "inst_t" +ENTITY_ADDR_TYPE = "inst_addr_t" +OPC_LIST_NAME = "/cygdrive/w/vhdl/lib/CPUs/JCpu/tools/opc.list" + +# -------------------------------------------------------- +# Open file +# -------------------------------------------------------- +filename = arg.to_s +if !File.exists?(filename) + STDERR.puts "#{filename} does not exist" + exit 1 +end + +# -------------------------------------------------------- +# Pass 1 +# -------------------------------------------------------- +lc = 0 # Line counter +pc = 0 # Program counter +chip_dc = [0,0,0,0] # chip data counter +xmem_dc = 0 # xmem data counter +curr_segment = "UNDEF"; +page = 0 + +label_list=[[],[]] +instr_list=[nil] +file = File.open(filename, "r") +file.each do |raw_line| + lc += 1 + + # Make all upper case + iline = "#{raw_line}".strip.upcase + + # Remove comments + iline = iline.gsub(/;.*/, "") + + # Split instruction at spaces + iline_member = iline.split(" ") + + if iline_member[0] == nil + next + end + + mc = 0; + label = nil + clean_line = nil + if (iline_member[0] =~ /\w+:/) + mc += 1 + label = iline_member[0].delete(":") + if label_list.rindex(label) != nil + STDERR.puts format("Error in line %d: Duplicate label \"%s\" found", lc, label) + exit 1 + end + end + + case iline_member[mc] + when nil + value = format("0x%3.3X",pc) + when "EQU" + if 0 == (iline_member[mc+1] =~ /\d+/) + value = format("0x%2.2X",eval(iline_member[mc+1..iline_member.nitems].to_s)) + else + value = iline_member[mc+1] + end + when "ORG", "CODE" + pc = eval(iline_member[mc+1..iline_member.nitems].to_s) + value = format("0x%3.3X",pc) + when "CDATA", "CMEM" + if iline_member[mc+1] == nil + STDERR.puts format("Error in line %d: CDATA needs argument!", lc) + exit 1 + end + curr_segment = "CMEM"; + page = iline_member[mc+1].to_i(16) + when "XDATA", "XMEM" + curr_segment = "XMEM"; + when "DC" + len = 0; + iline.split("DC")[1].delete("0x").scan(/[0-9A-Fa-f]+/).each do |byte| + len += 1 + end + if curr_segment == "CMEM" + STDERR.puts format("Error in line %d: CDATA cannot be initialized!", lc) + exit 1 + else + value = format("0x%2.2X",xmem_dc) + puts format("Found DC defining %d bytes at 0x%4.4X in %s", len, xmem_dc, curr_segment); + xmem_dc += len + end + when "DB" + len = iline_member[mc+1].to_i + if curr_segment == "CMEM" + value = format("0x%2.2X",chip_dc[page]) + puts format("Found DB reserving %d bytes at 0x%4.4X in %s(%d)", len, chip_dc[page], curr_segment, page); + chip_dc[page] += len + else + value = format("0x%2.2X",xmem_dc) + puts format("Found DB reserving %d bytes at 0x%4.4X in %s", len, xmem_dc, curr_segment); + xmem_dc += len + end + else + op_line = iline_member[mc+1..iline_member.nitems].to_s + value = format("0x%3.3X",pc) + opc = iline_member[mc] + if iline_member[mc+1] != nil + op1 = op_line.gsub(/,+.*$/, "") + end + if iline_member[mc+2] != nil + op2 = op_line.scan(/,.+$/).to_s.delete(", ") + end + if instr_list[pc] == nil + instr_list[pc] = [lc, pc, opc , op1 , op2] + pc += 1 + else + STDERR.puts format("Error in line %d: Overlapping sections at %3.3X", lc, pc) + exit 1 + end + end; + + # Push labels + if label + label_list = label_list + [label, value] + end +end +file.close + +# -------------------------------------------------------- +def instr(opcode) + return format("\"%6.6B\" & X\"%3.3X\"",opcode ,0) +end + +def tcl_bits(pc, opcode) + return format("%10.10B%6.6B%12.12B",pc, opcode ,0) +end + +def instr_k(opcode, k) + return format("\"%6.6B\" & X\"%3.3X\"",opcode ,k) +end + +def tcl_bits_k(pc, opcode, k) + return format("%10.10B%6.6B%12.12B",pc, opcode ,k) +end + +def instr_r(opcode, r) + return format("\"%6.6B\" & X\"%2.2X%X\"",opcode ,0, r) +end + +def tcl_bits_r(pc, opcode, r) + return format("%10.10B%6.6B%8.8B%4.4B",pc, opcode ,0,r) +end + +def instr_kr(opcode, k, r) + return format("\"%6.6B\" & X\"%2.2X%X\"",opcode,k, r) +end + +def tcl_bits_kr(pc, opcode, k, r) + return format("%10.10B%6.6B%8.8B%4.4B",pc, opcode, k, r) +end + +def instr_rk(opcode, r, k) + return format("\"%6.6B\" & X\"%2.2X%X\"",opcode,k, r) +end + +def tcl_bits_rk(pc, opcode, r, k) + return format("%10.10B%6.6B%8.8B%4.4B",pc, opcode, k, r) +end + +def instr_rr(opcode, ra, rb) + return format("\"%6.6B\" & X\"%X%X%X\"",opcode,0, rb, ra) +end + +def tcl_bits_rr(pc, opcode, ra, rb) + return format("%10.10B%6.6B%4.4B%4.4B%4.4B",pc, opcode, 0, rb, ra) +end + +# -------------------------------------------------------- +# Read mnemonic set +# -------------------------------------------------------- +filename = OPC_LIST_NAME +if !File.exists?(filename) + STDERR.puts "#{filename} does not exist" + exit 1 +end + +mnemo_list = [] +num_opcodes = 0 +file = File.open(filename, "r") +file.each do |raw_line| + mnemo_list[num_opcodes] = raw_line.split(" ")[1] + num_opcodes += 1 +end + +# -------------------------------------------------------- +# Pass 2 +# -------------------------------------------------------- +pc = 0 +opc_start = 2 +num_errors = 0 +vhdl_trans_list = [nil] +vhdl_comment_list = [nil] +vhdl_tcl_list = [nil] +instr_list.each do |instruct| + + # puts instruct + if instruct == nil + vhdl_trans_list[pc] = format("\"%6.6B\" & X\"%3.3X\"",0 ,0) + vhdl_comment_list[pc] = format("0x%3.3X:", pc) + vhdl_tcl_list[pc] = tcl_bits(pc, 0) + pc += 1 + next + end + + # determine arglen of instruction + ilen = 0 + if instruct[opc_start+1] == nil + arglen = 0 + elsif instruct[opc_start+2] == nil + arglen = 1 + elsif instruct[opc_start+3] == nil + arglen = 2 + end + + # Symbol substitution + error = false + for i in (1..3) + subst_pos = opc_start+i + if instruct[subst_pos] == nil + next + end + expr_token = "" + expr_subst = nil + is_direct = true; + +# instruct[subst_pos] = instruct[subst_pos].delete(" ") + + # checks R0 .. R16, Mnemonics + instr_str = instruct[subst_pos] + if 0 == (instr_str =~ /\(.+\)/) + instr_str = instr_str.delete("()") + is_direct = false; + end + + expr_token = instr_str.split(/\+|\-|\*|\//)[0] + if (0 != (expr_token =~ /R[0-9]+/)) && (0 != (expr_token =~ /\d+/)) + idx = label_list.rindex(expr_token) + if idx != nil + expr_subst = label_list[idx+1] + instruct[subst_pos] = instruct[subst_pos].gsub(expr_token, expr_subst) + else + STDERR.puts format("Error in line %d: Symbol \"%s\" not found", instruct[0], expr_token) + error = true + end + end + + if expr_subst != nil + if 0 == (expr_subst =~ /\d+/) + if is_direct == true + instruct[subst_pos] = format("0x%2.2X", eval(instruct[subst_pos])) + else + instruct[subst_pos] = format("(0x%2.2X)", eval(instruct[subst_pos])) + end + else + if 0 != (expr_subst =~ /R[0-9]+/) + STDERR.puts format("Error in line %d: Invalid expression \"%s\"", instruct[0], expr_subst) + error = true + end + end + end + end + + # Parse operands + reg = [0,0] + kk = 0 + format = instruct[opc_start] + for i in (0..1) + if instruct[opc_start+i+1] =~ Regexp.new("R[0-9]+") + # Check indirect addressing + reg[i] = instruct[opc_start+i+1].delete("R()").to_i + format += "|R" + if reg[i] > 15 + STDERR.puts format("Error in line %d: Invalid register \"R%d\" specified", instruct[0], reg[i]) + error = true + end + else + if instruct[opc_start+i+1] =~ Regexp.new("[0-9A-F]+") + kk = instruct[opc_start+i+1].delete("()").to_i(16) + format += "|K" + end + end + if instruct[opc_start+i+1] =~ Regexp.new("\\(.+\\)") + format += "i"; + end + end + + # Output instruction + opcode = mnemo_list.rindex(format).to_i + mnemo = instruct[2]; + + case format + when "NOP", "HALT", "RET", "RETI" + vhdl_trans_list[pc] = instr(opcode) + vhdl_tcl_list[pc] = tcl_bits(pc, opcode) + minstr_comment = format("0x%3.3X: %s", pc, mnemo) + when "MOVC|R|Ri", "MOVX|R|Ri" + vhdl_trans_list[pc] = instr_rr(opcode, reg[0], reg[1]); + vhdl_tcl_list[pc] = tcl_bits_rr(pc, opcode, reg[0], reg[1]) + minstr_comment = format("0x%3.3X: %s R%2.2d, (R%2.2d)", pc, mnemo, reg[0], reg[1]) + when "MOV|R|R", "SUB|R|R", "ADD|R|R", "AND|R|R", "OR|R|R", "XOR|R|R", "CMP|R|R" + vhdl_trans_list[pc] = instr_rr(opcode, reg[0], reg[1]); + vhdl_tcl_list[pc] = tcl_bits_rr(pc, opcode, reg[0], reg[1]) + minstr_comment = format("0x%3.3X: %s R%2.2d, R%2.2d", pc, mnemo, reg[0], reg[1]) + when "MOVC|Ri|R", "MOVX|Ri|R" + vhdl_trans_list[pc] = instr_rr(opcode, reg[1], reg[0]); + vhdl_tcl_list[pc] = tcl_bits_rr(pc, opcode, reg[1], reg[0]) + minstr_comment = format("0x%3.3X: %s (R%2.2d), R%2.2d", pc, mnemo, reg[1], reg[0]) + when "MOVC|Ri|K", "MOVX|Ri|K", "COUT|Ri|K", "XOUT|Ri|K" + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + minstr_comment = format("0x%3.3X: %s (R%2.2d), 0x%2.2X", pc, mnemo, reg[0], kk) + when "MOVC|R|Ki", "MOVX|R|Ki", "CIN|R|Ki", "XIN|R|Ki" + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + minstr_comment = format("0x%3.3X: %s R%2.2d, (0x%2.2X)", pc, mnemo, reg[0], kk) + when "MOV|R|K", "SUB|R|K", "ADD|R|K", "AND|R|K", "OR|R|K", "XOR|R|K", "CMP|R|K" + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + minstr_comment = format("0x%3.3X: %s R%2.2d, 0x%2.2X", pc, mnemo, reg[0], kk) + when "MOVC|Ki|R", "MOVC|Ki|R", "MOVX|Ki|R", "MOVX|Ki|R", "COUT|Ki|R", "XOUT|Ki|R" + vhdl_trans_list[pc] = instr_kr(opcode, kk, reg[1]); + vhdl_tcl_list[pc] = tcl_bits_kr(pc, opcode, kk, reg[1]) + minstr_comment = format("0x%3.3X: %s (0x%2.2X), R%2.2d", pc, mnemo, kk, reg[1]) + when "CALL|K", "JMP|K", "JNZ|K", "JZ|K","JNC|K", "JC|K", "JLT|K", "JGT|K", "JLE|K", "JGE|K", "JEQ|K", "JNE|K" + vhdl_trans_list[pc] = instr_k(opcode, kk) + vhdl_tcl_list[pc] = tcl_bits_k(pc, opcode, kk) + minstr_comment = format("0x%3.3X: %s 0x%3.3X", pc, mnemo, kk) + when "PUSH|R", "POP|R", "SHR|R", "SHL|R", "ROR|R", "ROL|R", "RORC|R", "ROLC|R" + vhdl_trans_list[pc] = instr_r(opcode, reg[0]) + vhdl_tcl_list[pc] = tcl_bits_r(pc, opcode, reg[0]) + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + when "TST|R" + kk = 0 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("CMP|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + when "INC|R" + kk = 1 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("ADD|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + when "DEC|R" + kk = 1 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("SUB|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + else + STDERR.puts format("Error in line %d: Not yet implemented %s", instruct[0], format) + error = true + end + + if error + num_errors += 1 + break + end + vhdl_comment_list[pc] = minstr_comment + + puts vhdl_trans_list[pc] + " -- " + minstr_comment + + pc += 1 +end + +# -------------------------------------------------------- +# Output ROM file +# -------------------------------------------------------- +if num_errors == 0 + + filename = format("rom_%s.vhdl.snip", PROJECT_NAME) + file = File.open(filename, "w") + + file.puts format("ENTITY %s IS\n", ENTITY_NAME) + file.puts format("\tPort (\n") + file.puts format("\tclk\t\t\t: in STD_LOGIC;\n") + file.puts format("\tce\t\t\t: in STD_LOGIC;\n") + file.puts format("\taddr\t\t: in %s;\n", ENTITY_ADDR_TYPE) + file.puts format("\tdout\t\t: out %s\n", ENTITY_DATA_TYPE) + file.puts format("\t);\n") + file.puts format("END %s;\n", ENTITY_NAME) + file.puts format("\n") + file.puts format("ARCHITECTURE %s OF %s IS \n", PROJECT_NAME, ENTITY_NAME) + file.puts format("\n") + file.puts format("\ttype instruction_rom_t is array (0 to %d) of %s;\n", pc-1, ENTITY_DATA_TYPE) + file.puts format("\n") + file.puts format("\tconstant instruction_rom : instruction_rom_t :=\n") + file.puts format("\t(\n"); + for i in (0..pc-2) + file.puts "\t\t" + vhdl_trans_list[i] + ", -- " + vhdl_comment_list[i] + "\n" + end; + file.puts "\t\t" + vhdl_trans_list[pc-1] + " -- " + vhdl_comment_list[pc-1] + "\n" + file.puts format("\t);\n") + file.puts format("\n") + file.puts format("begin") + file.puts format("\n") + file.puts format("PROM_READ:") + file.puts format("\tprocess(clk, ce)\n") + file.puts format("\tbegin\n") + file.puts format("\t\tif rising_edge(clk) and ce = '1' then\n") + file.puts format("\t\t\tdout <= instruction_rom(to_integer(addr));\n") + file.puts format("\t\tend if;\n") + file.puts format("\tend process;\n") + file.puts format("\n") + file.puts format("end %s;\n", PROJECT_NAME) + puts format("ROM file written to \"%s\"\n",filename) + file.close +end +if num_errors == 0 + + filename = format("preinit_%s.vhdl.snip", PROJECT_NAME) + file = File.open(filename, "w") + + file.puts format("\tsignal instruction_rom : instruction_rom_t :=\n") + file.puts format("\t(\n"); + for i in (0..pc-2) + file.puts "\t\t" + vhdl_trans_list[i] + ", -- " + vhdl_comment_list[i] + "\n" + end; + if pc < (MAX_SIZE_ROM-1) + file.puts "\t\t" + vhdl_trans_list[pc-1] + ", -- " + vhdl_comment_list[pc-1] + "\n" + for i in (pc..(MAX_SIZE_ROM-2)) + file.puts "\t\t" + instr(0) + ",\n" + end; + file.puts "\t\t" + instr(0) + "\n" + else + file.puts "\t\t" + vhdl_trans_list[pc-1] + " -- " + vhdl_comment_list[pc-1] + "\n" + end + file.puts format("\t);\n") + puts format("PREINIT file written to \"%s\"\n",filename) + file.close +end +if num_errors == 0 + filename = format("rom_%s.tcl.snip", PROJECT_NAME) + file = File.open(filename, "w") + for i in (0..pc-1) + # Chipscope 8.1 +# file.puts format("jtag_shiftdr $handle -buffer \"%s\" -endstate RTI -device $devid", vhdl_tcl_list[i].reverse); + + # Chipscope 9.1 + file.puts format("\# %s", vhdl_comment_list[i]) + file.puts format("::chipscope::csejtag_tap shift_device_dr $handle $devid $CSEJTAG_SHIFT_READWRITE $CSEJTAG_RUN_TEST_IDLE 0 %d \"%8.8X\"\n\n", vhdl_tcl_list[i].length, vhdl_tcl_list[i].to_i(2)); + end; + puts format("TCL snippet written to \"%s\"\n",filename) + file.close +end + +# -------------------------------------------------------- +# Output final statistics +# -------------------------------------------------------- +if num_errors == 0 + puts "Program uses #{pc} instruction addresses" + puts "No errors found" + exit 0 +else + puts "#{num_errors} errors found" + exit 1 +end diff --git a/tools/jcpu/jasm.rb b/tools/jcpu/jasm.rb new file mode 100755 index 0000000..1d8d758 --- /dev/null +++ b/tools/jcpu/jasm.rb @@ -0,0 +1,811 @@ +#!/usr/bin/env ruby + +# ---------------------------------------------------------------------- +# Project: JCPU, a portable 8-bit RISC CPU written in VHDL +# This file: A small assembler for the JCPU +# +# Copyright (C) 2007 J. Ahrensfeld +# +# 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 3 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, see . +# +# For questions and ideas, please contact the author at jens@jayfield.org +# +# --------------------------------------------------------------------- + +arg = $* +FILE_SUFFIX = "jsm" +IROM_MAX_SIZE = 1024 +IROM_ENTITY_NAME = "irom" +IROM_DATA_TYPE = "inst_t" +IROM_ADDR_TYPE = "inst_addr_t" +XROM_MAX_SIZE = 256 +XROM_DATA_TYPE = "dmem_data_t" +XROM_ADDR_TYPE = "dmem_addr_t" +XROM_ENTITY_NAME = "xrom" +OPC_LIST_NAME = __dir__ + "/opc.list" + +# -------------------------------------------------------- +class Parser + def curr_segment + @curr_segment + end + def curr_segment=(arg) + @curr_segment=arg + end + def add_label(labelname, addr) + @label_list = @label_list + [labelname, addr] + end + def getLabelIdByName(label) + return @label_list.rindex(label) + end + def getLabelNameById(id) + return @label_list[id] + end + def put_instr(name, lc, opc , op1 , op2) + if get_instr != nil + STDERR.puts format("Error in line %d: Overlapping sections at %3.3X", lc, @pc) + exit 1 + end + @instr_list[@pc] = [lc, @pc, opc , op1 , op2, name] + end + def get_instr + return @instr_list[@pc] + end + def put_xmem(value, lc) + if get_xmem != nil + STDERR.puts format("Error in line %d: Overlapping sections at %3.3X (%s)", lc, xmem_dc, curr_segment) + exit 1 + end + @xmem[xmem_dc] = value + @xmem_dc = xmem_dc + 1 + end + def get_xmem + return @xmem[xmem_dc] + end + def xmem + return @xmem + end + def put_cmem(value, lc) + if get_cmem(page) != nil + STDERR.puts format("Error in line %d: Overlapping sections at %3.3X (%s)", lc, cmem_dc, curr_segment) + exit 1 + end + @cmem[page][cmem_dc] = value + @cmem_dc[page] = cmem_dc + 1 + end + def get_cmem(pg) + return @cmem[pg][cmem_dc] + end + def cmem(page) + return @cmem[page] + end + def cmem_dc=(addr) + @cmem_dc[page] = addr + end + def cmem_dc + @cmem_dc[page] + end + def xmem_dc=(addr) + @xmem_dc = addr + end + def xmem_dc + @xmem_dc + end + def page=(page) + @page = page; + end + def page + @page; + end + def pc=(pc) + @pc = pc; + if (pc > @pc_max) + @pc_max = pc + end + end + def pc + @pc; + end + def pc_max + @pc_max; + end + def instr_list + @instr_list + end + def label_list + @label_list + end + + def initialize() + @pc_max = 0 + @pc = 0 # Program counter + @cmem_dc = [0,0,0,0] # chip data counter + @xmem_dc = 0 # xmem data counter + @curr_segment = "UNDEF"; + @page = 0 + @label_list=[[],[]] + @instr_list=[nil] + @cmem = [[],[]]; + @xmem = []; + end + def method_missing(name, *args) + puts "I don't know the method #{name}" + end +end + +# -------------------------------------------------------- +def parse(name, obj) + +lc = 0 # Line counter +file = File.open(name, "r") + +file.each do |raw_line| + lc += 1 + + # Make all upper case + iline = "#{raw_line}".strip + + # Remove comments + iline = iline.gsub(/;.*/, "") + + # Split instruction at spaces + iline_member = iline.split(" ") + + if iline_member[0] == nil + next + end + + mc = 0; + label = nil + clean_line = nil + if (iline_member[0] =~ /\w+:/) + mc += 1 + label = iline_member[0].delete(":") + if obj.getLabelIdByName(label) != nil + STDERR.puts format("Error in line %d: Duplicate label \"%s\" found", lc, label) + exit 1 + end + end + + token = iline_member[mc] + mcnt = iline_member.count{|x| !x.nil?} + if token + token = token.upcase + end + + case token + when nil + if obj.curr_segment == "CMEM" + curr_addr = format("0x%3.3X",obj.cmem_dc) + elsif obj.curr_segment == "XMEM" + curr_addr = format("0x%3.3X",obj.xmem_dc) + elsif obj.curr_segment == "CODE" + curr_addr = format("0x%3.3X",obj.pc) + else + STDERR.puts format("Error in line %d: Undefined segment", lc) + exit 1 + end + when "INCLUDE" + if iline_member[mc+1] == nil + STDERR.puts format("Error in line %d: %s needs argument!", lc, token) + exit 1 + end + inc_name = iline_member[mc+1].delete("\"") + puts format("Inserting \"%s\" into \"%s\" at line %d", inc_name, name, lc); + parse(inc_name, obj) + when "EQU" + if iline_member[mc+1] == nil + STDERR.puts format("Error in line %d: %s needs argument!", lc, token) + exit 1 + end + if 0 == (iline_member[mc+1] =~ /\d+/) + curr_addr = format("0x%2.2X",eval(iline_member[mc+1..mcnt][0].to_s)) + else + curr_addr = iline_member[mc+1] + end + when "ORG" + if iline_member[mc+1] == nil + STDERR.puts format("Error in line %d: %s needs argument!", lc, token) + exit 1 + end + offset = eval(iline_member[mc+1..mcnt][0].to_s) + case obj.curr_segment + when "CODE" + obj.pc = offset + curr_addr = format("0x%3.3X",obj.pc) + when "CMEM" + obj.cmem_dc = offset + when "XMEM" + obj.xmem_dc = offset + + end + when "CODE" + curr_addr = format("0x%3.3X",obj.pc) + obj.curr_segment = "CODE"; + when "CDATA", "CMEM" + if iline_member[mc+1] == nil + STDERR.puts format("Error in line %d: %s needs argument!", lc, token) + exit 1 + end + obj.curr_segment = "CMEM"; + obj.page = iline_member[mc+1].to_i(16) + when "XDATA", "XMEM" + obj.curr_segment = "XMEM"; + when "DC" + len = 0; + if iline_member[mc+1] == nil + STDERR.puts format("Error in line %d: %s needs argument!", lc, token) + exit 1 + end + if obj.curr_segment == "CMEM" + STDERR.puts format("Error in line %d: %s cannot be initialized!", lc, obj.curr_segment) + exit 1 + elsif obj.curr_segment == "XMEM" + curr_addr = format("0x%2.2X",obj.xmem_dc) + dc_old = obj.xmem_dc; + val_array = raw_line.split(/dc|DC|Dc|dC/)[1].delete("\t").gsub(/;.*/, "").split(",") + + # Parse string literals + val_array.each do |item| + if 0 == (item =~ /.*\".*\"/) + str = item.delete("\"") + for nn in (0..str.length-1) + value = str[nn].ord + obj.put_xmem(value, lc); + len += 1 + end; + else + argument = item + obj.put_xmem(eval(argument), lc); + len += 1 + end + end + puts format("Found DC defining %d bytes at 0x%4.4X in %s", len, dc_old, obj.curr_segment); + else + STDERR.puts format("Error in line %d: Invalid segment %s for \"%s\"", lc, obj.curr_segment, token) + exit 1 + end + when "DB" + if iline_member[mc+1] == nil + STDERR.puts format("Error in line %d: %s needs argument!", lc, token) + exit 1 + end + len = eval(iline_member[mc+1]).to_i + if obj.curr_segment == "CMEM" + curr_addr = format("0x%2.2X",obj.cmem_dc) + puts format("Found DB reserving %d bytes at 0x%4.4X in %s(%d)", len, obj.cmem_dc, obj.curr_segment, obj.page); + for nn in (1..len) + obj.put_cmem(0, lc) + end + elsif obj.curr_segment == "XMEM" + curr_addr = format("0x%2.2X",obj.xmem_dc) + puts format("Found DB reserving %d bytes at 0x%4.4X in %s", len, obj.xmem_dc, obj.curr_segment); + for nn in (1..len) + obj.put_xmem(0, lc) + end + else + STDERR.puts format("Error in line %d: Invalid segment %s for \"%s\"", lc, obj.curr_segment, token) + exit 1 + end + else + if obj.curr_segment != "CODE" + STDERR.puts format("Error in line %d: Invalid segment %s for \"%s\"", lc, obj.curr_segment, token) + exit 1 + end + + op_line = iline_member[mc+1..mcnt] + curr_addr = format("0x%3.3X",obj.pc) + opc = token + if iline_member[mc+1] != nil + op1 = op_line[0].to_s.gsub(/,+.*$/, "") + end + if iline_member[mc+2] != nil + op2 = op_line[1].to_s.gsub(/,+.*$/, "") + end + obj.put_instr(name, lc, opc, op1, op2) + obj.pc = obj.pc + 1 + end; + + # Push labels + if label + obj.add_label(label, curr_addr) + end +end +file.close +return obj +end + +# -------------------------------------------------------- +# Instruction formatting +# -------------------------------------------------------- +def instr(opcode) + return format("\"%6.6B\" & X\"%3.3X\"",opcode ,0) +end + +def tcl_bits(pc, opcode) + return format("%12.12B%8.8B%12.12B",pc, opcode ,0) +end + +def instr_k(opcode, k) + return format("\"%6.6B\" & X\"%3.3X\"",opcode ,k) +end + +def tcl_bits_k(pc, opcode, k) + return format("%12.12B%8.8B%12.12B",pc, opcode ,k) +end + +def instr_r(opcode, r) + return format("\"%6.6B\" & X\"%2.2X%X\"",opcode ,0, r) +end + +def tcl_bits_r(pc, opcode, r) + return format("%12.12B%8.8B%8.8B%4.4B",pc, opcode ,0,r) +end + +def instr_kr(opcode, k, r) + return format("\"%6.6B\" & X\"%2.2X%X\"",opcode,k, r) +end + +def tcl_bits_kr(pc, opcode, k, r) + return format("%12.12B%8.8B%8.8B%4.4B",pc, opcode, k, r) +end + +def instr_rk(opcode, r, k) + return format("\"%6.6B\" & X\"%2.2X%X\"",opcode,k, r) +end + +def tcl_bits_rk(pc, opcode, r, k) + return format("%12.12B%8.8B%8.8B%4.4B",pc, opcode, k, r) +end + +def instr_rr(opcode, ra, rb) + return format("\"%6.6B\" & X\"%X%X%X\"",opcode,0, rb, ra) +end + +def tcl_bits_rr(pc, opcode, ra, rb) + return format("%12.12B%8.8B%4.4B%4.4B%4.4B",pc, opcode, 0, rb, ra) +end + +# -------------------------------------------------------- +# Open file +# -------------------------------------------------------- +filename = arg[0].to_s +if !File.exists?(filename) + STDERR.puts "#{filename} does not exist" + exit 1 +end +PROJECT_NAME = File.basename(filename, "." + FILE_SUFFIX) + +# -------------------------------------------------------- +# Pass 1 +# -------------------------------------------------------- +first_pass = Parser.new(); +parse(filename, first_pass) + +# -------------------------------------------------------- +# Read mnemonic set +# -------------------------------------------------------- +filename = OPC_LIST_NAME +if !File.exists?(filename) + STDERR.puts "#{filename} does not exist" + exit 1 +end + +mnemo_list = [] +num_opcodes = 0 +file = File.open(filename, "r") +file.each do |raw_line| + mnemo_list[num_opcodes] = raw_line.split(" ")[1] + num_opcodes += 1 +end + +# -------------------------------------------------------- +# Pass 2 +# -------------------------------------------------------- +pc = 0 +opc_start = 2 +num_errors = 0 +vhdl_trans_list = [nil] +vhdl_comment_list = [nil] +vhdl_tcl_list = [nil] + +# ToDo: Use class-methods +label_list = first_pass.label_list +instr_list = first_pass.instr_list + +instr_list.each do |instruct| + + # puts instruct + if instruct == nil + vhdl_trans_list[pc] = format("\"%6.6B\" & X\"%3.3X\"",0 ,0) + vhdl_comment_list[pc] = format("0x%3.3X:", pc) + vhdl_tcl_list[pc] = tcl_bits(pc, 0) + pc += 1 + next + end + + # determine arglen of instruction + ilen = 0 + if instruct[opc_start+1] == nil + arglen = 0 + elsif instruct[opc_start+2] == nil + arglen = 1 + elsif instruct[opc_start+3] == nil + arglen = 2 + end + + # Symbol substitution + error = false + for i in (1..2) + subst_pos = opc_start+i + if instruct[subst_pos] == nil + next + end + expr_token = "" + expr_subst = nil + is_direct = true; + + # checks R0 .. R16, Mnemonics + instruct[subst_pos] = instruct[subst_pos].gsub(/'low/, "") + if (nil != (instruct[subst_pos] =~ /'high/)) + instruct[subst_pos]= format("%s/256", instruct[subst_pos].gsub(/'high/, "")) + end + instr_str = instruct[subst_pos] + if 0 == (instr_str =~ /\(.+\)/) + instr_str = instr_str.delete("()") + is_direct = false; + end + + expr_token = instr_str.split(/\+|\-|\*|\/|\||\&/)[0] + if (0 != (expr_token =~ /R[0-9]+/)) && (0 != (expr_token =~ /\d+/)) + idx = label_list.rindex(expr_token) + if idx != nil + expr_subst = label_list[idx+1] + instruct[subst_pos] = instruct[subst_pos].gsub(expr_token, expr_subst) + else + STDERR.puts format("%s: Error in line %d: Symbol \"%s\" not found", instruct[5], instruct[0], expr_token) + error = true + end + end + + if expr_subst != nil + if 0 == (expr_subst =~ /\d+/) + if is_direct == true + instruct[subst_pos] = format("0x%2.2X", eval(instruct[subst_pos])) + else + instruct[subst_pos] = format("(0x%2.2X)", eval(instruct[subst_pos])) + end + else + if 0 != (expr_subst =~ /R[0-9]+/) + STDERR.puts format("%s: Error in line %d: Invalid expression \"%s\"", instruct[5], instruct[0], expr_subst) + error = true + end + end + end + end + + # Parse operands + reg = [0,0] + kk = 0 + format = instruct[opc_start] + for i in (0..1) + if instruct[opc_start+i+1] =~ Regexp.new("R[0-9]+") + # Check indirect addressing + reg[i] = instruct[opc_start+i+1].delete("R()").to_i + format += "|R" + if reg[i] > 15 + STDERR.puts format("%s: Error in line %d: Invalid register \"R%d\" specified", instruct[5], instruct[0], reg[i]) + error = true + end + else + if instruct[opc_start+i+1] =~ Regexp.new("[0-9A-F]+") + kk = eval(instruct[opc_start+i+1].delete("()")) + format += "|K" + end + end + if instruct[opc_start+i+1] =~ Regexp.new("\\(.+\\)") + format += "i"; + end + end + + if error + num_errors += 1 + break + end + + # Output instruction + opcode = mnemo_list.rindex(format).to_i + mnemo = instruct[2]; + + case format + # Native instructions + when "NOP", "HALT", "RET", "RETI", "SETC" + vhdl_trans_list[pc] = instr(opcode) + vhdl_tcl_list[pc] = tcl_bits(pc, opcode) + minstr_comment = format("0x%3.3X: %s", pc, mnemo) + when "MOVC|R|Ri", "MOVX|R|Ri" + vhdl_trans_list[pc] = instr_rr(opcode, reg[0], reg[1]); + vhdl_tcl_list[pc] = tcl_bits_rr(pc, opcode, reg[0], reg[1]) + minstr_comment = format("0x%3.3X: %s R%2.2d, (R%2.2d)", pc, mnemo, reg[0], reg[1]) + when "MOV|R|R", "SUB|R|R", "SUBC|R|R", "ADD|R|R", "ADDC|R|R", "AND|R|R", "OR|R|R", "XOR|R|R", "CMP|R|R" + vhdl_trans_list[pc] = instr_rr(opcode, reg[0], reg[1]); + vhdl_tcl_list[pc] = tcl_bits_rr(pc, opcode, reg[0], reg[1]) + minstr_comment = format("0x%3.3X: %s R%2.2d, R%2.2d", pc, mnemo, reg[0], reg[1]) + when "MOVC|Ri|R", "MOVX|Ri|R" + vhdl_trans_list[pc] = instr_rr(opcode, reg[1], reg[0]); + vhdl_tcl_list[pc] = tcl_bits_rr(pc, opcode, reg[1], reg[0]) + minstr_comment = format("0x%3.3X: %s (R%2.2d), R%2.2d", pc, mnemo, reg[1], reg[0]) + when "MOVC|Ri|K", "MOVX|Ri|K", "COUT|Ri|K", "XOUT|Ri|K" + kk = kk % 256; + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + minstr_comment = format("0x%3.3X: %s (R%2.2d), 0x%2.2X", pc, mnemo, reg[0], kk) + when "MOVC|R|Ki", "MOVX|R|Ki", "CIN|R|Ki", "XIN|R|Ki" + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + minstr_comment = format("0x%3.3X: %s R%2.2d, (0x%2.2X)", pc, mnemo, reg[0], kk) + when "MOV|R|K", "SUB|R|K", "SUBC|R|K", "ADD|R|K", "ADDC|R|K", "AND|R|K", "OR|R|K", "XOR|R|K", "CMP|R|K" + kk = kk % 256; + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + minstr_comment = format("0x%3.3X: %s R%2.2d, 0x%2.2X", pc, mnemo, reg[0], kk) + when "SUB|K|R", "SUBC|K|R" + kk = kk % 256; + vhdl_trans_list[pc] = instr_kr(opcode, kk, reg[1]); + vhdl_tcl_list[pc] = tcl_bits_kr(pc, opcode, kk, reg[1]) + minstr_comment = format("0x%3.3X: %s 0x%2.2d, R%2.2X", pc, mnemo, kk, reg[1]) + when "MOVC|Ki|R", "MOVC|Ki|R", "MOVX|Ki|R", "MOVX|Ki|R", "COUT|Ki|R", "XOUT|Ki|R" + vhdl_trans_list[pc] = instr_kr(opcode, kk, reg[1]); + vhdl_tcl_list[pc] = tcl_bits_kr(pc, opcode, kk, reg[1]) + minstr_comment = format("0x%3.3X: %s (0x%2.2X), R%2.2d", pc, mnemo, kk, reg[1]) + when "CALL|K", "JMP|K", "JNZ|K", "JZ|K","JNC|K", "JC|K", "JLT|K", "JGT|K", "JLE|K", "JGE|K", "JEQ|K", "JNE|K" + vhdl_trans_list[pc] = instr_k(opcode, kk) + vhdl_tcl_list[pc] = tcl_bits_k(pc, opcode, kk) + minstr_comment = format("0x%3.3X: %s 0x%3.3X", pc, mnemo, kk) + when "PUSH|R", "POP|R", "SHR|R", "SHL|R", "ROR|R", "ROL|R", "RORC|R", "ROLC|R", "SWAP|R" + vhdl_trans_list[pc] = instr_r(opcode, reg[0]) + vhdl_tcl_list[pc] = tcl_bits_r(pc, opcode, reg[0]) + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + # Synthetic instructions + when "CLRC" + kk = 0; + minstr_comment = format("0x%3.3X: %s", pc, mnemo) + opcode = mnemo_list.rindex("SUB|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + when "TST|R" + kk = 0 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("CMP|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + when "NEG|R" + kk = 0 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("SUB|K|R").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + when "NOT|R" + kk = 0 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("XOR|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + when "INC|R" + kk = 1 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("ADD|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + when "DEC|R" + kk = 1 + minstr_comment = format("0x%3.3X: %s R%2.2d", pc, mnemo, reg[0]) + opcode = mnemo_list.rindex("SUB|R|K").to_i + vhdl_trans_list[pc] = instr_rk(opcode, reg[0], kk); + vhdl_tcl_list[pc] = tcl_bits_rk(pc, opcode, reg[0], kk) + else + STDERR.puts format("%s: Error in line %d: Not yet implemented %s", instruct[5], instruct[0], format) + error = true + end + + if error + num_errors += 1 + break + end + + vhdl_comment_list[pc] = minstr_comment + + puts vhdl_trans_list[pc] + " -- " + minstr_comment + + pc += 1 +end + +# -------------------------------------------------------- +# Output ROM file +# -------------------------------------------------------- +if num_errors == 0 + + # I-ROM VHDL-ROM + filename = format("%s.irom.vhdl.snip", PROJECT_NAME) + file = File.open(filename, "w") + + file.puts format("ENTITY %s IS\n", IROM_ENTITY_NAME) + file.puts format("\tPort (\n") + file.puts format("\tclk\t\t\t: in STD_LOGIC;\n") + file.puts format("\tce\t\t\t: in STD_LOGIC;\n") + file.puts format("\taddr\t\t: in %s;\n", IROM_ADDR_TYPE) + file.puts format("\tdout\t\t: out %s\n", IROM_DATA_TYPE) + file.puts format("\t);\n") + file.puts format("END %s;\n", IROM_ENTITY_NAME) + file.puts format("\n") + file.puts format("ARCHITECTURE %s OF %s IS \n", PROJECT_NAME, IROM_ENTITY_NAME) + file.puts format("\n") + file.puts format("\ttype imem_rom_t is array (0 to %d) of %s;\n", [IROM_MAX_SIZE, pc].min-1 , IROM_DATA_TYPE) + file.puts format("\n") + file.puts format("\t-- Assembled from %s.%s", PROJECT_NAME, FILE_SUFFIX) + file.puts format("\tconstant imem_rom : imem_rom_t :=\n") + file.puts format("\t(\n"); + for i in (0..[IROM_MAX_SIZE, pc].min-2) + file.puts "\t\t" + vhdl_trans_list[i] + ", -- " + vhdl_comment_list[i] + "\n" + end; + file.puts "\t\t" + vhdl_trans_list[pc-1] + " -- " + vhdl_comment_list[pc-1] + "\n" + file.puts format("\t);\n") + file.puts format("\n") + file.puts format("begin") + file.puts format("\n") + file.puts format("PROM_READ:") + file.puts format("\tprocess(clk, ce)\n") + file.puts format("\tbegin\n") + file.puts format("\t\tif rising_edge(clk) and ce = '1' then\n") + file.puts format("\t\t\tdout <= imem_rom(to_integer(addr));\n") + file.puts format("\t\tend if;\n") + file.puts format("\tend process;\n") + file.puts format("\n") + file.puts format("end %s;\n", PROJECT_NAME) + puts format("I-ROM file written to \"%s\"\n",filename) + file.close + + # I-ROM VHDL-ROM (loadable) + filename = format("%s.irom_ld.vhdl.snip", PROJECT_NAME) + file = File.open(filename, "w") + + file.puts format("\n\t-- Assembled from %s.%s", PROJECT_NAME, FILE_SUFFIX) + file.puts format("\ttype imem_rom_t is array (0 to %d) of %s;\n", IROM_MAX_SIZE-1, IROM_DATA_TYPE) + file.puts format("\tsignal imem_rom : imem_rom_t :=\n") + file.puts format("\t(\n"); + for i in (0..pc-2) + file.puts "\t\t" + vhdl_trans_list[i] + ", -- " + vhdl_comment_list[i] + "\n" + end; + if pc < (IROM_MAX_SIZE-1) + file.puts "\t\t" + vhdl_trans_list[pc-1] + ", -- " + vhdl_comment_list[pc-1] + "\n" + for i in (pc..(IROM_MAX_SIZE-2)) + file.puts "\t\t" + instr(0) + ",\n" + end; + file.puts "\t\t" + instr(0) + "\n" + else + file.puts "\t\t" + vhdl_trans_list[pc-1] + " -- " + vhdl_comment_list[pc-1] + "\n" + end + file.puts format("\t);\n") + puts format("I-ROM (loadable) file written to \"%s\"\n",filename) + file.close + + # I-ROM TCL-Loader + filename = format("%s.irom.tcl.snip", PROJECT_NAME) + file = File.open(filename, "w") + file.puts format("# Assembled from %s.%s", PROJECT_NAME, FILE_SUFFIX) + file.puts format("# ---------------------------------------------------------------"); + file.puts format("# Shift the USER1 Instruction (b1111000010) into the Instruction Register of FPGA"); + file.puts format("# User 1") + file.puts format("set result [::chipscope::csejtag_tap shift_device_ir $handle $devid $CSE_OP $CSE_ES 0 $irlength \"3C2\"]\n\n") + for i in (0..pc-1) + # Chipscope 8.1 +# file.puts format("jtag_shiftdr $handle -buffer \"%s\" -endstate RTI -device $devid", vhdl_tcl_list[i].reverse); + + # Chipscope 9.1 + file.puts format("\# %s", vhdl_comment_list[i]) + file.puts format("::chipscope::csejtag_tap shift_device_dr $handle $devid $CSE_OP $CSE_ES 0 %d \"%8.8X\"\n\n", vhdl_tcl_list[i].length, vhdl_tcl_list[i].to_i(2)); + end; + puts format("I-ROM TCL-loader written to \"%s\"\n",filename) + file.close + + # X-ROM VHDL-ROM + filename = format("%s.xrom.vhdl.snip", PROJECT_NAME) + file = File.open(filename, "w") + + file.puts format("ENTITY %s IS\n", XROM_ENTITY_NAME) + file.puts format("\tPort (\n") + file.puts format("\tclk\t\t\t: in STD_LOGIC;\n") + file.puts format("\tce\t\t\t: in STD_LOGIC;\n") + file.puts format("\taddr\t\t: in %s;\n", XROM_ADDR_TYPE) + file.puts format("\tdout\t\t: out %s\n", XROM_DATA_TYPE) + file.puts format("\t);\n") + file.puts format("END %s;\n", XROM_ENTITY_NAME) + file.puts format("\n") + file.puts format("ARCHITECTURE %s OF %s IS \n", PROJECT_NAME, XROM_ENTITY_NAME) + file.puts format("\n") + file.puts format("\ttype xmem_rom_t is array (0 to %d) of %s;\n", XROM_MAX_SIZE-1, XROM_DATA_TYPE) + file.puts format("\n") + file.puts format("\t-- Assembled from %s.%s", PROJECT_NAME, FILE_SUFFIX) + file.puts format("\tconstant xmem_rom : xmem_rom_t :=\n") + file.puts format("\t(\n"); + for i in (0..XROM_MAX_SIZE-2) + file.puts format("\t\tX\"%2.2X\", -- 0x%2.2X\n", first_pass.xmem[i].to_i(), i) + end; + i = XROM_MAX_SIZE-1 + file.puts format("\t\tX\"%2.2X\" -- 0x%2.2X\n", first_pass.xmem[i].to_i(), i) + file.puts format("\t);\n") + file.puts format("\n") + file.puts format("begin") + file.puts format("\n") + file.puts format("PROM_READ:") + file.puts format("\tprocess(clk, ce)\n") + file.puts format("\tbegin\n") + file.puts format("\t\tif rising_edge(clk) and ce = '1' then\n") + file.puts format("\t\t\tdout <= xmem_rom(to_integer(addr));\n") + file.puts format("\t\tend if;\n") + file.puts format("\tend process;\n") + file.puts format("\n") + file.puts format("end %s;\n", PROJECT_NAME) + puts format("X-ROM file written to \"%s\"\n",filename) + file.close + + # X-ROM VHDL-ROM (loadable) + filename = format("%s.xrom_ld.vhdl.snip", PROJECT_NAME) + file = File.open(filename, "w") + + file.puts format("\n\t-- Assembled from %s.%s", PROJECT_NAME, FILE_SUFFIX) + file.puts format("\ttype xmem_rom_t is array (0 to %d) of %s;\n", XROM_MAX_SIZE-1, XROM_DATA_TYPE) + file.puts format("\tsignal xmem_rom : xmem_rom_t :=\n") + file.puts format("\t(\n"); + for i in (0..XROM_MAX_SIZE-2) + file.puts format("\t\tX\"%2.2X\", -- 0x%2.2X\n", first_pass.xmem[i].to_i(), i) + end; + i = XROM_MAX_SIZE-1 + file.puts format("\t\tX\"%2.2X\" -- 0x%2.2X\n", first_pass.xmem[i].to_i(), i) + file.puts format("\t);\n") + puts format("X-ROM (loadable) file written to \"%s\"\n",filename) + file.close + + # X-ROM JTAG-Loader + filename = format("%s.xrom.tcl.snip", PROJECT_NAME) + file = File.open(filename, "w") + file.puts format("# Assembled from %s.%s", PROJECT_NAME, FILE_SUFFIX) + file.puts format("# ---------------------------------------------------------------"); + file.puts format("# Shift the USER2 Instruction (b1111000011) into the Instruction Register of FPGA"); + file.puts format("# User 2") + file.puts format("set result [::chipscope::csejtag_tap shift_device_ir $handle $devid $CSE_OP $CSE_ES 0 $irlength \"3C3\"]\n\n") + for i in (0..first_pass.xmem.length-1) + # Chipscope 8.1 +# file.puts format("jtag_shiftdr $handle -buffer \"%s\" -endstate RTI -device $devid", vhdl_tcl_list[i].reverse); + + # Chipscope 9.1 + file.puts format("::chipscope::csejtag_tap shift_device_dr $handle $devid $CSE_OP $CSE_ES 0 %d \"%2.2X%2.2X\"\n", 16, i, first_pass.xmem[i]); + end; + puts format("X-ROM TCL-loader written to \"%s\"\n",filename) + file.close + +end + +# -------------------------------------------------------- +# Output final statistics +# -------------------------------------------------------- +if num_errors == 0 + puts "Program uses #{first_pass.pc_max} instruction addresses" + puts "No errors found" + exit 0 +else + puts "#{num_errors} errors found" + exit 1 +end + diff --git a/tools/jcpu/lib/cg.inc.jsm b/tools/jcpu/lib/cg.inc.jsm new file mode 100644 index 0000000..01c36c4 --- /dev/null +++ b/tools/jcpu/lib/cg.inc.jsm @@ -0,0 +1,72 @@ +; ------------------------------------------------- +; lcd.inc.jsm +; ------------------------------------------------- +; needs xio-address equates for +; reg_cg_char_addr: equ 0xXX ; RW +; reg_cg_line_addr: equ 0xXX ; RW +; reg_cg_data: equ 0xXX ; RW +; reg_cg_ctrl: equ 0xXX ; WO + +; needs delay.inc.jsm + +; ------------------------------------------------- +cg_clr_line: push R0 + mov R0, 2 + xout (reg_cg_ctrl), R0 + pop R0 + ret + +; ------------------------------------------------- +cg_clr_screen: push R0 + mov R0, 1 + xout (reg_cg_ctrl), R0 + pop R0 + ret + +; ------------------------------------------------- +; R0 : char address +; R1 : line address +cg_set_addr: call cg_wait_rdy + xout (reg_cg_line_addr), R1 + xout (reg_cg_char_addr), R0 + ret + +; ------------------------------------------------- +; R0 : character +cg_putchar: call cg_wait_rdy + xout (reg_cg_data), R0 + ret + +; ------------------------------------------------- +; R1 : ptr to string +cg_puts: push R0 +cg_p_lp: movx R0, (R1) + inc R1 + tst R0 + jz ex_cgp + call cg_putchar + jmp cg_p_lp +ex_cgp: pop R0 + ret + +; ------------------------------------------------- +; R1 : ptr to string +cg_clr_puts: call cg_clr_line + call cg_puts + ret + +; ------------------------------------------------- +; R1 : ptr to string +cg_puts_clr: call cg_puts + call cg_clr_line + ret + +; ------------------------------------------------- +cg_wait_rdy: push R0 +cg_wr_lp: xin R0, (reg_hwstat) + and R0, 0x02 + jz cg_wr_lp + pop R0 + ret + +; ------------------------------------------------- diff --git a/tools/jcpu/lib/cregs.inc.jsm b/tools/jcpu/lib/cregs.inc.jsm new file mode 100644 index 0000000..06d39d9 --- /dev/null +++ b/tools/jcpu/lib/cregs.inc.jsm @@ -0,0 +1,7 @@ +; JCPU On-Chip register +cpu_revision: equ 0x00 ; RO +cpu_control: equ 0x01 ; R/W +cpu_status: equ 0x02 ; RO +cpu_int_ctrl: equ 0x03 ; RW +cpu_cmem_high: equ 0x04 ; RW +cpu_stack_high: equ 0x05 ; RW diff --git a/tools/jcpu/lib/delays.inc.jsm b/tools/jcpu/lib/delays.inc.jsm new file mode 100644 index 0000000..6f002ee --- /dev/null +++ b/tools/jcpu/lib/delays.inc.jsm @@ -0,0 +1,64 @@ +; ------------------------------------------------- +; Delays +; ------------------------------------------------- +delay1s: push R15 + mov R15, 10 +loop1s: call delay100ms + dec R15 + jnz loop1s + pop R15 + ret + +delay100ms: push R15 + mov R15, 10 +loop100ms: call delay10ms + dec R15 + jnz loop100ms + pop R15 + ret + +delay10ms: push R15 + mov R15, 10 +loop10ms: call delay1ms + dec R15 + jnz loop10ms + pop R15 + ret + +delay1ms: push R15 + mov R15, 10 +loop1ms: call delay100us + dec R15 + jnz loop1ms + pop R15 + ret + +delay100us: push R15 + mov R15, 10 +loop100us: call delay10us + dec R15 + jnz loop100us + pop R15 + ret + +delay10us: push R15 + mov R15, 99 +loop10us: nop + nop + nop + dec R15 + jnz loop10us + pop R15 + ret + +delay1us: push R15 + mov R15, 9 +loop1us: nop + nop + nop + dec R15 + jnz loop1us + pop R15 + ret + +; ------------------------------------------------- diff --git a/tools/jcpu/lib/lcd.inc.jsm b/tools/jcpu/lib/lcd.inc.jsm new file mode 100644 index 0000000..d2883fa --- /dev/null +++ b/tools/jcpu/lib/lcd.inc.jsm @@ -0,0 +1,208 @@ +; ------------------------------------------------- +; lcd.inc.jsm +; ------------------------------------------------- +; needs xmem-address definition for +; - lcd_port (LCD-Base) + +; needs delay.inc.jsm + +; ------------------------------------------------- +lcd_cmd: equ 0x00 +lcd_rd: equ 0x20 +lcd_data: equ 0x40 +lcd_enable: equ 0x80 + +LCD_CMD_Z1: equ 0x80 +LCD_CMD_Z2: equ 0xC0 +LCD_CMD_CLEAR: equ 0x01 + +; ------------------------------------------------- +; Init LCD +; ------------------------------------------------- +LCD_init: mov R2, 0x00 + mov R0, 0x03 + call _write_lcd + call delay10ms + mov R2, 0x00 + mov R0, 0x03 + call _write_lcd + call delay10ms + + mov R2, 0x00 + mov R0, 0x02 + call _write_lcd + call delay1ms + + ; 1. Function set: 4-Bit mode, two display lines + mov R0, 0x28 + call LCD_writecmd + + ; 2. Display on, no cursor + mov R0, 0x0C + call LCD_writecmd + + ; 3. Cursor shift during write + mov R0, 0x06 + call LCD_writecmd + + ; 4. Display clear + mov R0, 0x01 + call LCD_writecmd + + ret + +; ------------------------------------------------- +; LCD_waitbsy +; ------------------------------------------------- +LCD_waitbsy: push R0 +bsy_loop: call LCD_readstat + and R0, 0x80 + jz ex_bsy + call delay1us + jmp bsy_loop +ex_bsy: pop R0 + ret + +; ------------------------------------------------- +; LCD_writecmd +; ------------------------------------------------- +; R0 = cmd +LCD_writeaddr: +LCD_writecmd: push R2 + mov R2, lcd_cmd + call LCD_write + pop R2 + ret + +; ------------------------------------------------- +; LCD_putsx +; ------------------------------------------------- +; R1 = address of null-terminated string in XMEM +LCD_putsx: push R0 +putsx_loop: movx R0, (R1) + inc R1 + tst R0 + jz putsx_ex + call LCD_writechar + jmp putsx_loop +putsx_ex: pop R0 + ret + +; ------------------------------------------------- +; LCD_putsc +; ------------------------------------------------- +; R1 = address of null-terminated string in CMEM +LCD_putsc: push R0 +putsc_loop: movc R0, (R1) + inc R1 + tst R0 + jz putsc_ex + call LCD_writechar + jmp putsc_loop +putsc_ex: pop R0 + ret + +; ------------------------------------------------- +; LCD_writechar +; ------------------------------------------------- +; R0 = char +LCD_writedata: +LCD_writechar: push R2 + mov R2, lcd_data + call LCD_write + pop R2 + ret + +; ------------------------------------------------- +; LCD_write +; ------------------------------------------------- +; R0 = input +; R2 = register +LCD_write: call LCD_waitbsy + push R0 + swap R0 + call _write_lcd + pop R0 + call _write_lcd + ret + +; ------------------------------------------------- +; LCD_readstat +; ------------------------------------------------- +; R0 = status +LCD_readstat: push R2 + mov R2, lcd_cmd + call LCD_read + pop R2 + ret + +; ------------------------------------------------- +; LCD_read +; ------------------------------------------------- +; R0 = output +; R2 = register +LCD_read: call _read_lcd + swap R0 + push R0 + call _read_lcd + pop R2 + or R0, R2 + ret + +; ------------------------------------------------- +; _write +; ------------------------------------------------- +; R0 = input +_write_lcd: push R1 + + and R0, 0x0F + mov R1, R0 + or R1, R2 + xout (lcd_port), R1 + call delay1us + + mov R1, R0 + or R1, R2 + or R1, lcd_enable + xout (lcd_port), R1 + call delay1us + + mov R1, R0 + or R1, R2 + xout (lcd_port), R1 + call delay1us + + or R1, lcd_rd + xout (lcd_port), R1 + + pop R1 + ret + +; ------------------------------------------------- +; _read +; ------------------------------------------------- +; R0 = output +_read_lcd: push R1 + + mov R1, lcd_rd + or R1, R2 + xout (lcd_port), R1 + call delay1us + + mov R1, lcd_rd + or R1, R2 + or R1, lcd_enable + xout (lcd_port), R1 + call delay1us + xin R0, (lcd_port) + and R0, 0x0F + + mov R1, lcd_rd + or R1, R2 + xout (lcd_port), R1 + call delay1us + + pop R1 + ret + + \ No newline at end of file diff --git a/tools/jcpu/lib/mul8x8.inc.jsm b/tools/jcpu/lib/mul8x8.inc.jsm new file mode 100644 index 0000000..2f66e2a --- /dev/null +++ b/tools/jcpu/lib/mul8x8.inc.jsm @@ -0,0 +1,42 @@ +; ------------------------------------------------- +; mul8x8.inc.jsm +; ------------------------------------------------- + +; ------------------------------------------------- +; Parameter: +; R0 : operand 1 +; R1 : operand 2 +; Return: +; R0 : product low +; R1 : product high +mul8x8: push R2 + push R3 + push R4 + + mov R4, R1 + mov R1, 0 + mov R2, 0 + mov R3, 0 + +loop1: cmp R4, 0x01 + jeq ende1 + + shr R4 + jnc next1 + + add R2, R0 + addc R3, R1 + +next1: shl R0 + rolc R1 + jmp loop1 + +ende1: add R0, R2 + addc R1, R3 + + pop R4 + pop R3 + pop R2 + ret + +; ------------------------------------------------- diff --git a/tools/jcpu/lib/uart.inc.jsm b/tools/jcpu/lib/uart.inc.jsm new file mode 100644 index 0000000..789f579 --- /dev/null +++ b/tools/jcpu/lib/uart.inc.jsm @@ -0,0 +1,58 @@ +; ------------------------------------------------- +; uart.inc.jsm +; ------------------------------------------------- +; needs xmem-address definition for +; - uart_status (Status register) +; - uart_data (Data register) +UART_TX_EMPTY: equ 0x01 +UART_TX_FULL: equ 0x02 +UART_TX_CMPL: equ 0x04 +UART_RX_PRESENT: equ 0x10 + +; ------------------------------------------------- +; uart_puts +; ------------------------------------------------- +; R1 : ptr to string +uart_puts: push R0 +u_p_lp: movx R0, (R1) + inc R1 + tst R0 + jz ex_ugp + call uart_putchar + jmp u_p_lp +ex_ugp: pop R0 + ret + + +; ------------------------------------------------- +; uart_putchar +; ------------------------------------------------- +; R0 = input +uart_putchar: jmp sout + +; ------------------------------------------------- +; sout +; ------------------------------------------------- +; R0 = input +sout: push R1 +sout1: xin R1, (uart_status) + and R1, UART_TX_FULL + jnz sout1 + xout (uart_data), R0 + pop R1 + ret + +; ------------------------------------------------- +; sin +; ------------------------------------------------- +; R0 = output +sin: push R1 +sin1: xin R1, (uart_status) + and R1, UART_RX_PRESENT + jnz sin1 + xin R0, (uart_data) + pop R1 + ret + +; ------------------------------------------------- + diff --git a/tools/jcpu/lib/utils.inc.jsm b/tools/jcpu/lib/utils.inc.jsm new file mode 100644 index 0000000..f354d7c --- /dev/null +++ b/tools/jcpu/lib/utils.inc.jsm @@ -0,0 +1,32 @@ +; ------------------------------------------------- +; utils.inc.jsm +; ------------------------------------------------- + + +; ------------------------------------------------- +; Parameter +; R0 : Bin value in lower nibble +; Return +; R0 : Hex character representing lower nibble +nibble2hex: and R0, 0x0F + cmp R0, 0x0A + jlt isNum1 + add R0, 0x07 +isNum1: add R0, 0x30 + ret + +; ------------------------------------------------- +; Parameter +; R0 : Bin value +; Return +; R0 : Hex character high +; R1 : Hex character low +bin2hex: push R0 + call nibble2hex + mov R1, R0 + pop R0 + swap R0 + call nibble2hex + ret + +; ------------------------------------------------- diff --git a/tools/jcpu/opc.list b/tools/jcpu/opc.list new file mode 100644 index 0000000..59b3cfa --- /dev/null +++ b/tools/jcpu/opc.list @@ -0,0 +1,64 @@ +0 NOP +1 HALT +2 MOV|R|R +3 MOV|R|K +4 MOVX|R|Ri +5 MOVX|R|Ki +6 MOVX|Ri|R +7 MOVX|Ri|K +8 MOVX|Ki|R +9 MOVC|R|Ri +10 MOVC|R|Ki +11 MOVC|Ri|R +12 MOVC|Ri|K +13 MOVC|Ki|R +14 CMP|R|R +15 CMP|R|K +16 ADD|R|R +17 ADD|R|K +18 ADDC|R|R +19 ADDC|R|K +20 SUB|R|R +21 SUB|R|K +22 SUBC|R|R +23 SUBC|R|K +24 AND|R|R +25 AND|R|K +26 OR|R|R +27 OR|R|K +28 XOR|R|R +29 XOR|R|K +30 SHL|R +31 SHR|R +32 ROL|R +33 ROR|R +34 ROLC|R +35 RORC|R +36 XOUT|Ki|R +37 XOUT|Ri|K +38 COUT|Ki|R +39 COUT|Ri|K +40 XIN|R|Ki +41 CIN|R|Ki +42 SWAP|R +43 SETC +44 UNDEF +45 UNDEF +46 SUB|K|R +47 SUBC|K|R +48 JMP|K +49 JZ|K +50 JNZ|K +51 JC|K +52 JNC|K +53 JLT|K +54 JGT|K +55 JLE|K +56 JGE|K +57 JEQ|K +58 JNE|K +59 CALL|K +60 PUSH|R +61 POP|R +62 RET +63 RETI diff --git a/tools/jcpu/regx.rb b/tools/jcpu/regx.rb new file mode 100755 index 0000000..47775c9 --- /dev/null +++ b/tools/jcpu/regx.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +st = "\033[7m" +en = "\033[m" + + while TRUE + print "str> " + STDOUT.flush + str = gets + break if not str + str.chop! + print "pat> " + STDOUT.flush + re = gets + break if not re + re.chop! + str.gsub! re, "#{st}\\&#{en}" + print str, "\n" +end +print "\n" diff --git a/tools/jcpu/rom.tcl.tpl b/tools/jcpu/rom.tcl.tpl new file mode 100644 index 0000000..9f5c0dc --- /dev/null +++ b/tools/jcpu/rom.tcl.tpl @@ -0,0 +1,61 @@ +# ---------------------------------------------------------------------- +# Project: JCPU, a portable 8-bit RISC CPU written in VHDL +# This file: The ROM file for upload to target over JTAG +# +# Copyright (C) 2007 J. Ahrensfeld +# +# 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 3 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, see . +# +# For questions and ideas, please contact the author at jens@jayfield.org +# +# --------------------------------------------------------------------- + +# --------------------------------------------------------------------- +# For Chipscope 9.1 +# --------------------------------------------------------------------- +# Source JTAG/TCL frame work +cd $env(CHIPSCOPE)\\bin\\nt +source csejtag.tcl + +namespace import ::chipscope::* + +# Platform USB Cable +set PLATFORM_USB_CABLE_ARGS [list "port=USB2" "frequency=6000000"] +# frequency="24000000 | 12000000 | 6000000 | 3000000 | 1500000 | 750000" + +# Create session +set handle [::chipscope::csejtag_session create 0] + +# Open JTAG and lock +set open_result [::chipscope::csejtag_target open $handle $CSEJTAG_TARGET_PLATFORMUSB 0 $PLATFORM_USB_CABLE_ARGS] +set lock_result [::chipscope::csejtag_target lock $handle 1000] + +set devlist [::chipscope::csejtag_tap autodetect_chain $handle $CSEJTAG_SCAN_DEFAULT] + +# Get Device ID +set devtype "Virtex-4SX" +set devid 2 +set irlength [::chipscope::csejtag_tap get_irlength $handle $devid] +set idcode [::chipscope::csejtag_tap get_device_idcode $handle $devid] + +set CSE_OP $CSEJTAG_SHIFT_READWRITE +set CSE_ES $CSEJTAG_RUN_TEST_IDLE + +# Write Program +# JASM_ROM_INSERT_HERE + +::chipscope::csejtag_target unlock $handle +::chipscope::csejtag_target close $handle +::chipscope::csejtag_session destroy $handle +exit diff --git a/tools/jcpu/templates.rb b/tools/jcpu/templates.rb new file mode 100755 index 0000000..632f11c --- /dev/null +++ b/tools/jcpu/templates.rb @@ -0,0 +1,44 @@ +#!/usr/bin/env ruby + + +template = <<'END' + -- (tv #1342) read output FIFO + ( + cycles => 1, + cm_i => bus_read_init(addr => ADDR_KS_OFIFO_DOUT), + sep => msg(""), + cm_o => bus_no_ack, + irq => (others => '0') + ), + + -- (tv #1343) wait while FIFO is read + ( + cycles => 2, + cm_i => bus_wait_ack, + sep => msg(""), + cm_o => bus_no_ack, + irq => (others => '0') + ), + + -- (tv #1343) wait for ack + ( + cycles => 1, + cm_i => bus_wait_ack, + sep => msg(""), + cm_o => bus_read_ack(dat => #{exp_data}), + irq => (others => '0') + ), + +END + +puts "KS-128 testvectors:\n" +puts "\n" * 3 + + +0.upto(10) { |sk_no| + 3.downto(0) { |sl_no| + exp_data = "slice_32(TC1_SUBKEYS(#{sk_no}), #{sl_no})" + t = template.gsub(/"/, '\\"') + puts eval("format(\"#{t}\")") + } +} diff --git a/tools/jcpu/xrom.vhd.tpl b/tools/jcpu/xrom.vhd.tpl new file mode 100644 index 0000000..5f11f86 --- /dev/null +++ b/tools/jcpu/xrom.vhd.tpl @@ -0,0 +1,32 @@ +------------------------------------------------------------------------- +-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL +-- This file: The ROM file for use in your VHDL design +-- +-- Copyright (C) 2007 J. Ahrensfeld +-- +-- 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 3 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, see . +-- +-- For questions and ideas, please contact the author at jens@jayfield.org +-- +-------------------------------------------------------------------------- + +LIBRARY ieee; +use IEEE.STD_LOGIC_1164.ALL; +USE ieee.numeric_std.ALL; + +library work; +use work.cpu_pkg.all; + +-- JASM_ROM_INSERT_HERE + diff --git a/tools/jcpu/xrom_ld.vhd.tpl b/tools/jcpu/xrom_ld.vhd.tpl new file mode 100644 index 0000000..696487f --- /dev/null +++ b/tools/jcpu/xrom_ld.vhd.tpl @@ -0,0 +1,151 @@ +------------------------------------------------------------------------- +-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL +-- This file: loadable ROM +-- +-- Copyright (C) 2007 J. Ahrensfeld +-- +-- 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 3 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, see . +-- +-- For questions and ideas, please contact the author at jens@jayfield.org +-- +-------------------------------------------------------------------------- + +LIBRARY ieee; +use IEEE.STD_LOGIC_1164.ALL; +USE ieee.numeric_std.ALL; + +library UNISIM; +use UNISIM.VComponents.all; + +library work; +use work.cpu_pkg.all; + +ENTITY xrom IS + Port ( + clk : in STD_LOGIC; + ce : in STD_LOGIC; + addr : in dmem_addr_t; + dout : out dmem_data_t + ); + +END xrom; + +ARCHITECTURE loadable OF xrom IS + + -- JASM_ROM_INSERT_HERE + + signal jtag_ld_clk : STD_LOGIC; + signal jtag_ld_we : STD_LOGIC; + signal jtag_ld_addr : dmem_addr_t; + signal jtag_ld_dout : dmem_data_t; + signal jtag_ld_din : dmem_data_t; + signal bs_rst, bs_sel, bs_shift, bs_tdi, bs_tdo : std_logic; + signal bs_capture, bs_clk0, bs_clk1, bs_update0, bs_update1 : std_logic; + signal user_regi, user_rego : unsigned (15 downto 0); + constant id : unsigned (15 downto 0) := X"BEEF"; + +begin + +-------------------------------------------------------------------------- +-- Virtex-4: JTAG Loader +-------------------------------------------------------------------------- + i00_BUFG : BUFG + port map + ( + O => bs_clk1, + I => bs_clk0 + ); + + i01_BUFG : BUFG + port map + ( + O => bs_update1, + I => bs_update0 + ); + + BSCAN_VIRTEX4_inst2 : BSCAN_VIRTEX4 + generic map + ( + JTAG_CHAIN => 2 -- Value to set BSCAN site of device. Possible values: (1,2,3 or 4) + ) + port map + ( + CAPTURE => bs_capture, -- CAPTURE output from TAP controller + DRCK => bs_clk0, -- Data register output for USER functions + RESET => bs_rst, -- Reset output from TAP controller + SEL => bs_sel, -- USER active output + SHIFT => bs_shift, -- SHIFT output from TAP controller + TDI => bs_tdi, -- TDI output from TAP controller + UPDATE => bs_update0, -- UPDATE output from TAP controller + TDO => bs_tdo -- Data input for USER function + ); + + jtag_ld_addr <= user_regi(user_regi'left downto user_regi'left-dmem_data_t'length+1); + jtag_ld_din <= user_regi(dmem_data_t'left downto 0); + jtag_ld_clk <= bs_update1; + jtag_ld_we <= bs_sel; + +sipo: + process (bs_rst, bs_clk1, bs_tdi, bs_shift) + begin + if bs_rst = '1' then + user_regi <= (others => '0'); + elsif rising_edge(bs_clk1) then + if bs_shift = '1' then + user_regi <= bs_tdi & user_regi(user_regi'left downto 1); + end if; + end if; + end process; + +piso: + process (bs_rst, bs_clk1, bs_shift, user_rego) + begin + bs_tdo <= user_rego(0); + if bs_rst = '1' then + user_rego <= (others => '0'); + elsif rising_edge(bs_clk1) then + if bs_shift = '1' then + user_rego <= user_rego(0) & user_rego(user_rego'left downto 1); + else + user_rego <= (user_rego'left downto dmem_data_t'length => '0') & jtag_ld_dout; +-- user_rego <= id; + end if; + end if; + end process; + +-------------------------------------------------------------------------- +-- ROM Read/Write +-------------------------------------------------------------------------- +PROM_READ: + process(clk, ce) + begin + if rising_edge(clk) and ce = '1' then + dout <= xmem_rom(to_integer(addr)); + end if; + end process; + +PROM_WRITE: + process(jtag_ld_clk, jtag_ld_we) + begin + if rising_edge(jtag_ld_clk) then + if jtag_ld_we = '1' then + xmem_rom(to_integer(jtag_ld_addr)) <= jtag_ld_din; + else + jtag_ld_dout <= xmem_rom(to_integer(jtag_ld_addr)); + end if; + end if; + end process; + +-------------------------------------------------------------------------- +end loadable; diff --git a/tools/mips/flashgen b/tools/mips/flashgen new file mode 100755 index 0000000000000000000000000000000000000000..7eeb8d3545c2ffde8a8a08627f7b2956b95937ac GIT binary patch literal 17544 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 literal 0 HcmV?d00001 diff --git a/tools/mips/flashgen_tlb b/tools/mips/flashgen_tlb new file mode 100755 index 0000000000000000000000000000000000000000..6a2ec5e10d2e0b7298f9fd0b9241f3ea840b124b GIT binary patch literal 17544 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 literal 0 HcmV?d00001 diff --git a/tools/mips/packhex b/tools/mips/packhex new file mode 100755 index 0000000000000000000000000000000000000000..23148f24e8efc700e02557a01489406b0be2617d GIT binary patch literal 18816 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 literal 0 HcmV?d00001 diff --git a/tools/mips/ramgen b/tools/mips/ramgen new file mode 100755 index 0000000000000000000000000000000000000000..76f517fcc8acb9ed0c3de9ce85dba04b975ce613 GIT binary patch literal 25728 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 literal 0 HcmV?d00001 diff --git a/tools/mips/romgen b/tools/mips/romgen new file mode 100755 index 0000000000000000000000000000000000000000..16bef3c0a59677da67c98163cb2f240e9bcf5eac GIT binary patch literal 25928 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 literal 0 HcmV?d00001