Dit is voor iedereen die interesseert is in FPGA. Na vloeken met de Picoblaze van xilinx die maar 8bit is. Ik had 16 nodig voor wat bit gemanipuleer. Heb ik mijn eigen CPU geschreven om mijn kennis van verilog te vergroten en de werking van een processor. Dit is wat ik nu heb.

-2 clockcyclus per instructie
-16 16bit registers
-11 instructies
-4 interrupts (nog niet af)
-16bit input and output
-8bit adres lijnen
-reset (nog niet af)
-en k.i.s.s Asembler (nog niet af)

Hier is de sourcecode:
http://robin.tussen.be/SPU16BIT.v

Hier is het Instruction Set
http://robin.tussen.be/SPU.doc

To Do

De interrupts werken niet mee.

Mijn vraag naar jullie willen je mijn processor eens goed testen? Een heel simpel assembler komt er aan maar ik kan tot morgen niet aan mijn gebruikelijk PC waar de asembler opstaat.

Alvast bedankt

Robin

Een klein kickje met een vraagje? Heeft iemand een goei idee voor een interrupt controller? Ik zit hier een beetje vast.

Robin

uw in code is verkeerd. is identiek aan je out code.
en gebruik ene case constructie voor je opvodes i s veel simpeler om te lezen

je hebt ook geen default state : je machine kan in deadlock gaan.

tweak volgt ...


`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//  
// Engineer: Theunis Robin
// Project: MPU
// 
// SPU: CPU CORE
// Revision : 0.1 free_electron
//
//////////////////////////////////////////////////////////////////////////////////
module SPU16BIT(
 
	input clk_i, rst_i;
	input [23:0] IDATA_i;
   	input [15:0] DATA_i;
	output wire [7:0] IADR_o;
	output reg [7:0] DADR_o;
   	output reg [15:0] DATA_o;
	output reg DREAD_o;
	output reg DWRITE_o;
	);
	
	//REGISTER BANK
	reg [15:0] REG [3:0];
	
	//INSTRUCTION FETCH
	wire OPCODE = IDATA_I[23:20];
	wire OPTK = IDATA_I[16];
	wire OPTZ = IDATA_I[17];
	wire OPTC = IDATA_I[18];
	wire OPTQ = IDATA_I[19];
	
	wire INSTDATA = IDATA_I[15:0];
	//REGISTER SELECTS
	wire regslctA = IDATA_[15:12];
	wire regslctB = IDATA_[11:8];
	wire regslctC = IDATA_[19:16];
	
	reg [1:0] STATE;
	reg [7:0] PROGRAMCNT
	
	always@(posedge clk_i) begin
	if(STATE == 1) begin
		//INIT
		DWRITE_o <= 0;
		DREAD_o <= 0;
		case (OPCODE)
		1:	begin   //JUMP
			if( (OPTQ & (OPTZ == ZERO)) | (OPTQ & (OPTC == CARRY)) | !OPTQ ) 
				if(OPTK) PROGRAMCNT <= REG[regslctA];
				else	PROGRAMCNT <= INSTDATA;    // ontbreekt hier geen state <=2 assignment ?
			end
		2:	begin 	// MOVE
				REG[regslctA] <= REG[regslctB];
				STATE <= 2;
			end
		3:      begin	//LOAD
				REG[regslctC] <= INSTDATA;
				STATE <= 2;
			end
		4:  	begin	//ADD
				REG[regslctA] <= REG[regslctB] + REG[regslctA];
				STATE <= 2;
			end
		5:	begin	//AND
				REG[regslctA] <= REG[regslctB] & REG[regslctA];
				STATE <= 2;
			end
		6:	begin // NOT
				REG[regslctA] <= !REG[regslctB];
				STATE <= 2;
			end
		7;	begin//IN	
				if(OPTK) DADR_o <= REG[regslctA];  // dit is niet juis t!!
				else	DADR_o <= INSTDATA;
			end
		8:	begin // OUt
				if(OPTK) DADR_o <= REG[regslctA];
				else	DADR_o <= INSTDATA;
			end
		endcase
	//STATE 2
	if(STATE == 2) begin
		PROGRAMCNT <= PROGRAMCNT + 1;
		STATE <= 1;
		
		if(REG[regslctA] == 0) 	ZERO <= 1;
		else 			ZERO <= 0;
		
		if(OPCODE == 7) begin
			DREAD_o <= 1;
			REG[15] <= DATA_i;
		end
		if(OPCODE == 8) begin
			DWRITE_o <= 1;
			DATA_i <= REG[15];
		end
	end
	end
endmodule

en dan de problemen

- eerste instructie ; moet je state niet veranderen ? indien ja :smijt al die state assignments weg en zet dat voorop ( net buiten de case ) je kan dan al een gans pak begin / end weghalen ook.
zo


`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//  
// Engineer: Theunis Robin
// Project: MPU
// 
// SPU: CPU CORE
// Revision : 0.1 free_electron
//
//////////////////////////////////////////////////////////////////////////////////
module SPU16BIT(
 
	input clk_i, rst_i;
	input [23:0] IDATA_i;
   	input [15:0] DATA_i;
	output wire [7:0] IADR_o;
	output reg [7:0] DADR_o;
   	output reg [15:0] DATA_o;
	output reg DREAD_o;
	output reg DWRITE_o;
	);
	
	//REGISTER BANK
	reg [15:0] REG [3:0];
	
	//INSTRUCTION FETCH
	wire OPCODE = IDATA_I[23:20];
	wire OPTK = IDATA_I[16];
	wire OPTZ = IDATA_I[17];
	wire OPTC = IDATA_I[18];
	wire OPTQ = IDATA_I[19];
	
	wire INSTDATA = IDATA_I[15:0];
	//REGISTER SELECTS
	wire regslctA = IDATA_[15:12];
	wire regslctB = IDATA_[11:8];
	wire regslctC = IDATA_[19:16];
	
	reg [1:0] STATE;
	reg [7:0] PROGRAMCNT
	
	always@(posedge clk_i) begin
	if(STATE == 1) begin
		//INIT
		DWRITE_o <= 0;
		DREAD_o <= 0;
		STATE 	<= 2;
		case (OPCODE)
		1:	begin   //JUMP
			if( (OPTQ & (OPTZ == ZERO)) | (OPTQ & (OPTC == CARRY)) | !OPTQ ) 
				if(OPTK) PROGRAMCNT <= REG[regslctA];
				else	PROGRAMCNT <= INSTDATA;    // ontbreekt hier geen state <=2 assignment ?
			end
		2:	REG[regslctA] <= REG[regslctB];
		3:      REG[regslctC] <= INSTDATA;
		4:  	REG[regslctA] <= REG[regslctB] + REG[regslctA];
		5:	REG[regslctA] <= REG[regslctB] & REG[regslctA];
		6:	REG[regslctA] <= !REG[regslctB];
		7;	if(OPTK) DADR_o <= REG[regslctA];  // dit is niet juis t!!
			else	DADR_o <= INSTDATA;
		8:	if(OPTK) DADR_o <= REG[regslctA];
			else	DADR_o <= INSTDATA;
		default : PROGRAMCNT<=PROGRAMCNT +1;  ' NOP instructie
		endcase
	//STATE 2
	if(STATE == 2) begin
		PROGRAMCNT <= PROGRAMCNT + 1;
		STATE <= 1;
		
		if(REG[regslctA] == 0) 	ZERO <= 1;
		else 			ZERO <= 0;
		
		if(OPCODE == 7) begin
			DREAD_o <= 1;
			REG[15] <= DATA_i;
		end
		if(OPCODE == 8) begin
			DWRITE_o <= 1;
			DATA_i <= REG[15];
		end
	end
	end
endmodule

ik heb er maar meteen een NOp tussengezet ook. als de processor in jouw code ene onbekende instructie tegenkomt blokkeert hij ( state verandert niet waardoor de program counter niet ophoogt ....
mijn 'default' zorgt er voor datvoor een onbekende instructie de program countr verhoogt.
je in /out code snap ik niet.

waar is de 'reset' code ?

[Bericht gewijzigd door free_electron op (92%)]

UPDATED:
versie 231

@FE

Mijn IN en OUT code heb ik verandert. Ik had de verkeerde versie online gezet. Van al die IF's heb ik een case gemaakt
en een default toegevoegt. Nu is opcode 0 eigenlijk een NOP.
Dank voor uw professionele blik

Robin

blijf nog efkes online. ik ga eerst ontbijten (net wakker .. klets verilog bekijken op nuchtere maag is niet gezond ... ) kzal er dan nog eens paar blikken op werpen.

een tros 'wires' kan je beter vervangen door `define.
en ik heb zo nog ene paar iedeeen.
je kan met een paar extra lijnen code dat ding veel krachtiger maken ( setbit , clearbit , testbit handig om io operaties te doen. + en - is ook altijd leuk

jump instructie ... gosub/ return instructie met stack .... ( ik zou met vast stack werken. das eenvoudigst. stack die 8 diep is bijvoorbeeld.

die gosub return heb je nodig om interrupts te processen. je moet current vector pushen jumpen en daarnaa terugkeren.

IEEP!!! Dat is veel verandert. Lekker kort :p


`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//  
// Engineer: Theunis Robin
// Project: MPU
// 
// SPU: CPU CORE
//
//////////////////////////////////////////////////////////////////////////////////
module SPU16BIT(clk_i, rst_i,INTA_i,INTB_i,INTC_i,INTD_i,INTACK_o, IADR_o, IDATA_i, DADR_o, DATA_i, DATA_o, DREAD_o , DWRITE_o);
   //CTL 
	input clk_i;
   input rst_i;
	//DATA INPUTS
   input wire [23:0] IDATA_i;
   input wire [15:0] DATA_i;
	//DATA OUT's
   output reg [7:0] IADR_o;
	output reg [7:0] DADR_o;
   output reg [15:0] DATA_o;
	output reg DREAD_o;
	output reg DWRITE_o;
	
	//REGISTER BANK
	reg [15:0] REG [0:15];
	
	//INTERRUPT ENABLE
	reg INTENABLE;
	
	//INTERRUPT INPUTS
	input wire INTA_i;
	input wire INTB_i;
	input wire INTC_i;
	input wire INTD_i;
	//ACK OUTOUT
	output wire [7:0] INTACK_o;
	
	reg INTA_fired;
	reg INTB_fired;
	reg INTC_fired;
	reg INTD_fired;
		wire INT_fired = INTA_fired | INTB_fired | INTC_fired | INTD_fired;


	//INTERRUPT ADRESS
	reg [7:0] INTA;
	reg [7:0] INTB;
	reg [7:0] INTC;
	reg [7:0] INTD;

	reg [7:0] DEBUG;
	//STACK ADR
	reg [7:0] STACK;

	//INSTRUCTION FETCH
	wire [3:0] OPCODE = IDATA_i[23:20];
	wire OPTK = IDATA_i[16];
	wire OPTZ = IDATA_i[17];
	wire OPTC = IDATA_i[18];
	wire OPTQ = IDATA_i[19];
	
	wire INSTDATA = IDATA_i[15:0];
	//REGISTER SELECTS
	wire regslctA = IDATA_i[15:12];
	wire regslctB = IDATA_i[11:8];
	wire regslctC = IDATA_i[19:16];
	wire INTslct =	IDATA_i[17:16];
	wire INTadr = 	IDATA_i[7:0];
	reg STATE;
	
	reg [7:0] PROGRAMCNT;
	reg [7:0] DATAREGOUT;
	reg [7:0] DATAREGIN;
	
	reg ZERO;
	reg CARRY;
assign  INTACK_o = DEBUG ;
always@(posedge clk_i)
begin
	if (INTA_i) begin
		INTA_fired <= 1;
	end
	if(STATE == 0) begin
		INTA_fired <= 0;
		INTB_fired <= 0;
		INTC_fired <= 0;
		INTD_fired <= 0;
		//INIT
		case(OPCODE)
		//JUMP
		1: begin
			if( (OPTQ & (OPTZ == ZERO)) | (OPTQ & (OPTC == CARRY)) | !OPTQ ) begin 
				if(OPTK) begin
					PROGRAMCNT <= REG[regslctA];
				end else begin		
					PROGRAMCNT <= INSTDATA;
				end
				STATE <= 1;
			end
		end
		//END JUMP
	
		//MOVE
		2: begin
			REG[regslctA] <= REG[regslctB];
			STATE <= 1;
		end
		//END MOVE
		
		//LOAD
		3: begin
			REG[regslctC] <= INSTDATA;
			STATE <= 1;
		end
		//END LOAD
		
		//ADD
		4: begin
			REG[regslctA] <= REG[regslctB] + REG[regslctA];
			STATE <= 1;
		end
		//END ADD
		
		//AND
		5: begin
			REG[regslctA] <= REG[regslctB] & REG[regslctA];
			STATE <= 1;
		end
		//END AND
		
		//NOT
		6: begin
			REG[regslctA] <= !REG[regslctB];
			STATE <= 1;
		end
		//END NOT
			
		//IN	
		7: begin
			DADR_o <= REG[regslctA];
			DREAD_o <= 1;
			STATE <= 1;
		end
		//END IN
		
		//OUT
		8: begin
			DADR_o <= REG[regslctA];
			DATA_o <= REG[regslctB];
			DWRITE_o <= 1;
			STATE <= 1;
		end
		//END OUT
		
		//LOAD INT
		9: begin
			case (INTslct)
				0: INTA <= INTadr;
				1: INTB <= INTadr;
				2: INTC <= INTadr;
				3: INTD <= INTadr;
			endcase
			STATE <= 1;
		end
		//ENDLOADINT
		
		//ENABLE/DISABLE INT
		10: begin
			INTENABLE <= OPTK;
			STATE <= 1;
		end
		//INT
		//RETURN
		11: begin
			PROGRAMCNT <= STACK;
			STATE <= 1;
		end
		default:
			STATE <= 1;
		
		endcase
	end
	//END STATE 1

	//STATE 2
	if(STATE==1) begin
		STATE <= 0;
		
		if(!INT_fired) begin
			if(!(OPCODE==1) & !(OPCODE==11)) begin
				PROGRAMCNT <= PROGRAMCNT + 1;
			end
		end else begin
			STACK <= PROGRAMCNT;
			if(INTA_fired) begin
				PROGRAMCNT <= INTA;
			end else begin
				if(INTB_fired) begin
					PROGRAMCNT <= INTB;
				end else begin
					if(INTC_fired) begin
						PROGRAMCNT <= INTC;
					end else begin
						if(INTD_fired) begin
							PROGRAMCNT <= INTD;
						end
					end
				end
			end
		end
		
		IADR_o <= PROGRAMCNT;
		
		//ZERO CHECH
		if(REG[regslctA] == 0) begin
			ZERO <= 1;
		end else begin
			ZERO <= 0;
		end
		
		//READ DELAY
		if(OPCODE == 7) begin
			REG[regslctB] <= DATA_i;
		end
		DWRITE_o <= 0;
		DREAD_o <= 0;
		
	end
	//END STATE 2
end
endmodule

Hier is de in/out wel goed. In de eerste state verander ik het adress en en de dataout en in het tweede state lees ik de data in.

PS Drink een goeie tas koffie.

Robin

doe je daar nu om om dat zo langdradig te schrijven ?
zet die state <=1 woorop en gooi dat uit al de cases.
je bent van een gans pak begin en ends vanaf.
en verplaats je commentaar naar achter ene regel. in plaats van 36 paginas code heb je dan1 pagina...

en gebruik verilog 2000 syntax dan hoef je die module ports en de in/out definities geen 2x te schrijven.vermindert kans op fouten en kans op miserie.

een paar ideen : maak een harvard machine met gesplit code/data memory. dat maakt je compilerbouw en je core bouw eenvoudiger.
als je dan je datadomain wilt scalen lukt dat heel makkelijk

je hebt 3 move instructies nodig :
- rom naar register.
- ram naar register.
- register naar ram.

alle operaties draaien op registers.
we gaan even van een 16 bit programawoord uit. en 32 registers in de cpu.
das 5 bit voor source en 5 bit voor target. dat laat 6 bit voor instructies. in een code word.

ik zou een flag register maken met daarin de volgende flags : carry , equal , less , more ,check

[6bit operand] [target reg] [source reg]

vor de compiler :

de machiene heeft 32 registers.
normaal werk je met R0 en R1 ( kan met andere dingen zijn ) maar voor de meeste operaties : deze.
R2 en R3 worden gebruikt om jumps te berekenen
R4,R5,R6 en R7 zijn scratchpad.

de preserve restore en conswap operands kunnen een blok van 8 registers verplaatsen of 'swappen' met ene ander blok . dat maakt dat je heel snel interrupt kan afhandelen. geef elke interrupt zijn eigen 'blok' al wat je moet doen is even een preserve/ restore of een conswap en de boel draait verder. geen gemodder met berekeningen.

de SFR zijn hardware registers. die hebben hun eigen poort. daar kan je dan configuratie registers aan knopen zoals bijvoorbeeld een baudrate generator of de interrupt control registers.

de stack is ene memory apart. ( in fpga zijn vaak memories aanwezig : knoop een deidcated memory aan de stack bus vast. zo heb je weer een zorg minder.



reg  [15:0]  code_pointer  , data_pointer  ,sfr_pointer;
reg  [15:0]  data_to_ram   , data_to_sfr;
wire [15:0]  data_from_ram , data_from_rom ,data_from_sfr;
reg  [7:0]   stack_pointer;
reg  [15:0]  data_tostack;
wire [15:0] data_from_stack;

`define  OPERAND     data_from_rom[16:10]
`define  TARGET      reg[data_from_rom[9:5]]
`define  SOURCE      reg[data_from_rom[4:0]]

// instruction set

`define CLEAR     0   
`define AND       1   
`define OR        2
`define NOT       3
`define EXOR      4
`define SETBIT    5
`define CLEARBIT  6 
`define SHIFTL    7
`define SHIFTR    8
`define ROR       9
`define ROL       10
`define ADD       11
`define SUB       12
`define INCR      20
`define DECR      21
`define MUL       22
`define DIV       23

// context operaties

`define PRESERVE 40  // bewaar de context
`define RESTORE  41  // restore context
`define CONSWAP  42

// vanaf nu zijn dit 2 cycli instructies : de instructe gevolgd door het address ( altijd 16 bit )
`define MOVTSFR   48  // move to SFR
`define MOVFSFR   49  // move from SFR
`define MOVTRAM   50  // move register to RAM
`define MOVFRAM   51  // move from RAM
`define MOVFROM   52  // move from ROM
`define PUSH      53  // push iets op de stack
`define POP       54  // haal van de stack
`define GOSUB     55
`define RETURN    56
`define JMP       57   // jump absolute
`define JMPC      58   // jump carry
`define JMPT      59   // jump on testbit
`define JMPE      60   // jump if equal
`define JMPNE     61   // jump not equal
`define JMPL      62   // jump less
`define JMPM      63   // jump if more

// de processor states. de meest instructies zijn 1 clocktick
// voor sprongopdrachten is een argument nodig.
// voor interrupts is er een cleanup.
reg [1:0] CPU_STATE;

`define DECODE      0  // decodeer instructie
`define FETCH_ARG   1  // haal argumetn op
`define APPLY_ARG   2  // voer argument uit
`define NEXT        3  // cleanup

//de state machine wordt simpel :
always @(posedge clk) begin
case (CPU_STATE)
    `DECODE    : begin
                 case(OPERAND)
                     `CLEAR   : `TARGET <=0;
                     `AND     : `TARGET <= (`TARGET && `SOURCE);
                     // enzxovoort
                 endcase
                 end
    `FETCH_ARG : begin
                 end
    `APPLY_ARG : begin
                 end
    `NEXT        begin
                 end
    default    : CPU_STATE <= `DECODE;
endcase
if(reset) begin
   CPU_STATE <= `DECODE;
   // wis all registers
   // wis all SFR's
   // wis de stack memories.
end