LoginSignup
0
0

4bit CPU TD4をSpartan-3A Starter Kitで動かす

Last updated at Posted at 2023-02-17

いまさらSpartan-3A Starter Kitを入手しました。
http://kozos.jp/fpga/spartan3a.html

まずはTD4を動かすのにPCでシミュレーションをやってみます。
https://qiita.com/asfdrwe/items/8dd955240b8291346384

実機ではTang Nanoでの実行がありましたが、せっかくなのでSpartan-3Aで実行してみました。

td4.v
top_td4.v
prescaler.v

こちらを参考にUCFを記載しました。
https://propella.hatenablog.com/entry/20080616/p1

NET "CLOCK" LOC = "E12"| IOSTANDARD = LVCMOS33 ;

NET "IN<0>" LOC = "V8" | IOSTANDARD = LVTTL | PULLUP ;
NET "IN<1>" LOC = "U10"| IOSTANDARD = LVTTL | PULLUP ;
NET "IN<2>" LOC = "U8" | IOSTANDARD = LVTTL | PULLUP ;
NET "IN<3>" LOC = "T9" | IOSTANDARD = L
VTTL | PULLUP ;

NET "OUT<3>" LOC = "U19" | IOSTANDARD = LVTTL | SLEW = QUIETIO | DRIVE = 4 ;
NET "OUT<2>" LOC = "U20" | IOSTANDARD = LVTTL | SLEW = QUIETIO | DRIVE = 4 ;
NET "OUT<1>" LOC = "T19" | IOSTANDARD = LVTTL | SLEW = QUIETIO | DRIVE = 4 ;
NET "OUT<0>" LOC = "R20" | IOSTANDARD = LVTTL | SLEW = QUIETIO | DRIVE = 4 ;

NET "RESET" LOC = "T15" | IOSTANDARD = LVTTL | PULLDOWN ; # SOUTH BUTTON

書籍「作ろう!CPU」を参考にtop_td4.vとprescaler.vを追加します。

td4.v
module td4 (
  input wire CLOCK,
  input wire RESET,
  input wire [3:0] IN,
  output reg [3:0] reg_out);

  reg [3:0] reg_a, reg_b;
  reg [3:0] pc = 4'b0;
  reg cflag = 1'b1;

  reg [7:0] rom[0:15];
//  initial $readmemb("ROM.bin", rom);
  initial begin
      rom[0] =  8'b10110111; // OUT 0111   # LED
      rom[1] =  8'b00000001; // ADD A,0001
      rom[2] =  8'b11100001; // JNC 0001   # loop 16 times
      rom[3] =  8'b00000001; // ADD A,0001
      rom[4] =  8'b11100011; // JNC 0011   # loop 16 times
      rom[5] =  8'b10110110; // OUT 0110   # LED
      rom[6] =  8'b00000001; // ADD A,0001
      rom[7] =  8'b11100110; // JNC 0110   # loop 16 times
      rom[8] =  8'b00000001; // ADD A,0001 
      rom[9] =  8'b11101000; // JNC 1000   # loop 16 times
      rom[10] = 8'b10110000; // OUT 0000   # LED
      rom[11] = 8'b10110100; // OUT 0100   # LED
      rom[12] = 8'b00000001; // ADD A,0001
      rom[13] = 8'b11101010; // JNC 1010   # loop 16 times
      rom[14] = 8'b10111000; // OUT 1000   # LED
      rom[15] = 8'b11111111; // JMP 1111
  end
	
  wire [7:0] opcode;
  assign opcode = rom[pc];
  assign op = opcode;

  wire [1:0] alu_sel, load_sel;
  wire jmp;
  wire [3:0] im; // IMMEDIATE 
  assign alu_sel = (opcode[7:6] == 2'b11) ? 2'b11 : opcode[5:4];
  assign load_sel = opcode[7:6];
  assign jmp = opcode[4];
  assign im = opcode[3:0];

  wire [3:0] alu_in;
  assign alu_in = (alu_sel == 2'b00) ? reg_a : // from A
                  (alu_sel == 2'b01) ? reg_b : // from B
                  (alu_sel == 2'b10) ? IN : // from input port
                                       4'b0000; // zero
  
  wire [3:0] alu_out;
  wire nextcflag;
  assign {nextcflag, alu_out} = alu_in + im;
  assign alu_data = alu_out;

  wire load_a, load_b, load_out, load_pc;
  assign load_a = (load_sel == 2'b00) ? 1'b0 : 1'b1; // negative logic
  assign load_b = (load_sel == 2'b01) ? 1'b0 : 1'b1; // negative logic
  assign load_out = (load_sel == 2'b10) ? 1'b0 : 1'b1; // negative logic
  assign load_pc = (load_sel == 2'b11 && (jmp == 1'b1 || cflag)) ? 1'b0 : 1'b1; // negative logic

  wire [3:0] next_pc;
  assign next_pc = (load_pc == 1'b0) ? alu_out : pc + 1;

  always @(posedge CLOCK or negedge RESET) begin
    if (!RESET) begin
      reg_a <= 4'b0;
      reg_b <= 4'b0;
      reg_out <= 4'b0;
      cflag <= 1'b1;
      pc <= 4'b0;
    end else begin
      reg_a <= #1 (load_a == 1'b0) ? alu_out : reg_a;
      reg_b <= #1 (load_b == 1'b0) ? alu_out : reg_b;
      reg_out <= #1 (load_out == 1'b0) ? alu_out : reg_out;
      cflag  <= #1 ~nextcflag; // negative logic carry 
      pc <= #1 next_pc;
    end
  end
endmodule
top_td4.v
`timescale 1ns / 1ps

module top_td4(
            input CLOCK,
            input RESET,
            input [3:0] IN,
            output [3:0] OUT
            );

   wire td4_CLOCK;

   prescaler #(.RATIO(10_000_000)) prescaler(
    .quick_clock(CLOCK),
    .slow_clock(td4_CLOCK)
   );

   td4 td4_0(td4_CLOCK, ~RESET, IN, OUT);   
endmodule
prescaler.v
module prescaler #(parameter RATIO = 2) (
  input  wire quick_clock,
  output reg slow_clock
);	
  reg [31:0] counter;
  wire [31:0] next_counter;
  wire inv;
  assign inv = (counter == (RATIO/2 - 1));
  assign next_counter = inv ? 32'd0 : counter + 32'd1;
  always @(posedge quick_clock) counter <= next_counter;
  wire next_slow_clock;
  assign next_slow_clock = inv ? ~slow_clock : slow_clock;
  always @(posedge quick_clock) slow_clock <= next_slow_clock;
endmodule

PCでシミュレーションもやってみます。あえてtop_td4.vではなくtd4.vを対象とします。

test_td4.v
`timescale 1ns/1ps
module test_td4;
  reg clock = 1'b0;
  reg reset_n = 1'b0;
  reg [3:0] reg_in = 4'b0000;
  wire [3:0] pc_out;

  td4 td4_1(clock, reset_n, reg_in, pc_out);

  initial begin
    $dumpfile("TD4.vcd");
    $dumpvars(0, test_td4);
    $monitor("%t: in_port = %h, out_port = %h", $time, reg_in, pc_out);
  end

  initial begin
    clock = 1'b0;
    forever begin
      #1 clock = ~clock;
    end
  end

  initial begin
    reset_n = 1'b0;

    #1 reset_n = 1'b1;
    #10000 $finish;
  end
endmodule
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0