Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #1516

Re: Writing a forth kernel

From Chris Hinsley <chris.hinsley@gmail.com>
Newsgroups comp.lang.forth
Date 2011-04-25 16:17 +0100
Message-ID <2011042516174712230-chrishinsley@gmailcom> (permalink)
References <cea3f9ee-8e66-4a64-acda-53e46ace819c@34g2000pru.googlegroups.com> <abWdnfSMsqLrKDfQnZ2dnUVZ8sudnZ2d@brightview.co.uk>
Subject Re: Writing a forth kernel

Show all headers | View raw


On 2011-04-17 10:40:06 +0100, Jan Coombs said:

> On 17/04/11 02:11, usao wrote:
>> Im working on a home-brew 12-bit CPU, and would like to impelement
>> FORTH on it. Where can I locate details on how to implement a kernel
>> using assembly?
> 
> This recent Novix style CPU is about the same size as yours, and has 
> documentation, code support and applications:
> 
>    "J1 is a small (200 lines of Verilog) stack-based CPU, intended
>    for FPGAs. A complete J1 with 16Kbytes of RAM fits easily on a
>    small Xilinx FPGA"
> 
>    http://www.excamera.com/sphinx/fpga-j1.html
> 
> I'm part way through translating the code into MyHDL, a sort of 
> interactive hardware development environment. This is a learning 
> exercise for me, so, if I haven't learned enough I'll have a go at 
> Bernd Paysan's b16 processor next.
> 
> For machines this small the compiler is often PC hosted, but it might 
> be interesting to include one on a very small target like one of these.
> 
> Jan Coombs

Jan, here's a version of the J1 I did, with a bit of code tidying and 
renameing to help me understand it better. Any errors are mine and not 
nesseserally the original authors.

I was going to paramaterise it so you can pick and choose the width of 
the registers and depth of the stacks etc. But in case this helps at 
all have a copy of this.

module j1(
	 input i_clk, input i_rst, input [15:0] io_din,
	 output io_rd, output io_wr, output [15:0] io_addr, output [15:0] io_dout);

	// pc register
	reg [12:0] pc;
	reg [12:0] _pc;

	// instruction word
	wire [15:0] insn;

	// data stack
	reg [4:0] dsp;		// data stack index
	reg [4:0] _dsp;		// next data stack index

	reg [15:0] ds0;	// current top of data stack
	reg [15:0] _ds0;	// next top of data stack

	// return stack
	reg [4:0] rsp;		// return stack index
	reg [4:0] _rsp;		// next return stack index
	reg [15:0] _rs0;	// next top of return stack

	wire _dsW;			// data stack write signal
	reg _rsW;			// return stack write signal

	wire _ramWE;		 // RAM write enable
	wire [15:0] ramrd;

	// next pc value if we need it
	wire [15:0] pc_plus_1;
	assign pc_plus_1 = pc + 1;

	// internal data and return stacks.
	// each clock we may write the top of data stack
	// or the next top of return stack
	// plus we read out the next on data stack and current top of return stack
	reg [15:0] dstack[0:31];
	reg [15:0] rstack[0:31];
	always @(posedge i_clk)
	begin
		if (_dsW) dstack[_dsp] = ds0;
		if (_rsW) rstack[_rsp] = _rs0;
	end
	wire [15:0] ds1 = dstack[dsp];
	wire [15:0] rs0 = rstack[rsp];

	// opcode for the ALU operation.
	// For jump and call the operation is T, for 0branch it is N.
	// For ALU ops it is loaded from the instruction field.
	reg [3:0] opcode;
	always @(*)
	begin
		case (insn[14:13])
			2'b00: opcode = 0;			// jump
			2'b10: opcode = 0;			// call
			2'b01: opcode = 1;			// 0branch
			2'b11: opcode = insn[11:8];	// ALU
		endcase
	end

	// this bit is specific to your FPGA !
	// it's a dual port ram with A and B channels
	// instructions are coming from A
	`define RAMS 3
	`define w (16 >> `RAMS)
	`define w1 (`w - 1)
	genvar i;
	generate
		for (i = 0; i < (1 << `RAMS); i=i+1)
		begin : ram
			// RAMB16_S18_S18
			RAMB16_S2_S2
			ram(
			.DIA(0),
			// .DIPA(0),
			.DOA(insn[`w*i+`w1:`w*i]),
			.WEA(0),
			.ENA(1),
			.CLKA(i_clk),
			.ADDRA({_pc}),

			.DIB(ds1[`w*i+`w1:`w*i]),
			// .DIPB(2'b0),
			.WEB(_ramWE & (_ds0[15:14] == 0)),
			.ENB(|_ds0[15:14] == 0),
			.CLKB(i_clk),
			.ADDRB(_ds0[15:1]),
			.DOB(ramrd[`w*i+`w1:`w*i]));
		end
	endgenerate

	// compute next top of data stack
	always @(*)
	begin
		if (insn[15])
		begin
			// immediate value from instruction word
			_ds0 = {1'b0, insn[14:0]};
		end
		else
		begin
			// do the alu opcode
			case (opcode)
				4'b0000: _ds0 = ds0;
				4'b0001: _ds0 = ds1;
				4'b0010: _ds0 = ds0 + ds1;
				4'b0011: _ds0 = ds0 & ds1;
				4'b0100: _ds0 = ds0 | ds1;
				4'b0101: _ds0 = ds0 ^ ds1;
				4'b0110: _ds0 = ~ds0;
				4'b0111: _ds0 = {16{(ds1 == ds0)}};
				4'b1000: _ds0 = {16{($signed(ds1) < $signed(ds0))}};
				4'b1001: _ds0 = ds1 >> ds0[3:0];
				4'b1010: _ds0 = ds0 - 1;
				4'b1011: _ds0 = rs0;
				4'b1100: _ds0 = |ds0[15:14] ? io_din : ramrd;
				4'b1101: _ds0 = ds1 << ds0[3:0];
				4'b1110: _ds0 = {rsp, 3'b000, dsp};
				4'b1111: _ds0 = {16{(ds1 < ds0)}};
			endcase
		end
	end

	wire is_alu = (insn[15:13] == 3'b011);
	wire is_lit = (insn[15]);

	assign io_rd = (is_alu & (insn[11:8] == 4'hc));
	assign io_wr = _ramWE;
	assign io_addr = ds0;
	assign io_dout = ds1;

	assign _ramWE = is_alu & insn[5];
	assign _dsW = is_lit | (is_alu & insn[7]);

	// data and return stack deltas if needed
	wire [1:0] dd = insn[1:0];
	wire [1:0] rd = insn[3:2];

	always @(*)
	begin
		if (is_lit)
		begin
			// literal
			_dsp = dsp + 1;
			_rsp = rsp;
			_rsW = 0;
			_rs0 = _pc;
		end
		else if (is_alu)
		begin
			// alu op
			_dsp = dsp + {{3{dd[1]}}, dd};
			_rsp = rsp + {{3{rd[1]}}, rd};
			_rsW = insn[6];
			_rs0 = ds0;
		end
		else
		begin
			// jump, call or 0branch
			// what happens to data stack
			if (insn[15:13] == 3'b001)
			begin
				// 0branch we drop the condition
				_dsp = dsp - 1;
			end
			else
			begin
				_dsp = dsp;
			end

			// what happens to return stack
			if (insn[15:13] == 3'b010)
			begin
				// call
				_rsp = rsp + 1;
				_rsW = 1;
				_rs0 = {pc_plus_1[14:0], 1'b0};
			end
			else
			begin
				// jump or 0branch
				_rsp = rsp;
				_rsW = 0;
				_rs0 = _pc;
			end
		end
	end

	// what the next pc is going to be
	always @(*)
	begin
		if (i_rst)
		begin
			// reset
			_pc = pc;
		end
		else
		begin
			if ((insn[15:13] == 3'b000)
				| ((insn[15:13] == 3'b001) & (|ds0 == 0))
				| (insn[15:13] == 3'b010))
			begin
				// going to be jump, 0branch or call
				_pc = insn[12:0];
			end
			else if (is_alu & insn[12])
			begin
				// alu op also includes a return
				_pc = rs0[15:1];
			end
			else
			begin
				// or just moving on to next instruction
				_pc = pc_plus_1;
			end
		end
	end

	// each clock update to the next state or reset
	always @(posedge i_clk)
	begin
		if (i_rst)
		begin
			// reset
			pc <= 0;
			dsp <= 0;
			rsp <= 0;
			ds0 <= 0;
		end
		else
		begin
			// next state
			pc <= _pc;
			dsp <= _dsp;
			rsp <= _rsp;
			ds0 <= _ds0;
		end
	end
endmodule

Back to comp.lang.forth | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

Writing a forth kernel usao <rwatki@gmail.com> - 2011-04-16 18:11 -0700
  Re: Writing a forth kernel BruceMcF <agila61@netscape.net> - 2011-04-16 19:18 -0700
  Re: Writing a forth kernel "P.M.Lawrence" <pml540114@gmail.com> - 2011-04-17 00:21 -0700
  Re: Writing a forth kernel Jan Coombs <jan_2011-02@murray-microft.co.uk> - 2011-04-17 10:40 +0100
    Re: Writing a forth kernel Chris Hinsley <chris.hinsley@gmail.com> - 2011-04-25 16:17 +0100
      Re: Writing a forth kernel Jan Coombs <jan_2011-02@murray-microft.co.uk> - 2011-04-29 00:51 +0100
    Re: Writing a forth kernel wzab <wzab01@gmail.com> - 2011-05-13 09:04 -0700
      Re: Writing a forth kernel Jan Coombs <jan_2011-02@murray-microft.co.uk> - 2011-05-13 23:49 +0100
  Re: Writing a forth kernel Albert van der Horst <albert@spenarnc.xs4all.nl> - 2011-04-17 22:38 +0000
  Re: Writing a forth kernel Tarkin <tarkin000@gmail.com> - 2011-04-26 08:14 -0700
    Re: Writing a forth kernel Chris Hinsley <chris.hinsley@gmail.com> - 2011-04-27 02:09 +0100

csiph-web