[Oberon] PO2013 - Real time measurement

Magnus Karlsson magnus at saanlima.com
Mon Dec 17 15:25:18 CET 2018


On 12/17/2018 1:10 AM, Tomas Kral wrote:
>> You can connect as many slaves to the ribbon as you wish, but you can
>> only communicate with a single slave while others are not active.
> Wojtek,
>
> Thank you, I wanted to achieve a small system bus for Oberon devices,
> inexpensive, with as few components as possible. I learnt SPI cannot be
> addressed, but only chip selected by `CS'. My idea was to drive `CS'
> by Oberon through `GPIO' + interrupt, being a master letting only one
> party to talk, the other listen, at at time.
>
> Thus having a simple HUB, for some A/D sensors. Alternatively to have
> also D/D, D/A outputs.
>
> Designing a small control system, so to speak.
>
Tomas,

If you want to experiment with I2C then you can try the I2C controller I 
wrote for RISC5 a few years ago.  It emulates the I2C controller found 
in many ARM microcontrollers (master-mode only) at register-level.  I 
did not write any software for it, Chris Burrows did and he might share 
his code with you.

See attached verilog file I2C.v (and modified RISC5Top.v which shows you 
how to add it to the system).

Magnus

-------------- next part --------------
/******************************************************************************
*  I2C.v
*
*  Copyright (c) 2016, Magnus Karlsson
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without 
*  modification, are permitted provided that the following conditions are met:
*
*  1. Redistributions of source code must retain the above copyright notice, 
*     this list of conditions and the following disclaimer.
*  2. Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions and the following disclaimer in the documentation
*     and/or other materials provided with the distribution.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
*  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
*  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/

module I2C (
  input clk,
  input rst,
  inout SCL,
  inout SDA,
  input [15:0] sclh,
  input [15:0] scll,
  output [7:0] control,
  output [4:0] status,
  output [7:0] data,
  input [7:0] wrdata,
  input wr_conset,
  input wr_data,
  input wr_conclr
);

  parameter 
  IDLE     = 22'b0000000000000000000001,
  START    = 22'b0000000000000000000010,
  ADDR1    = 22'b0000000000000000000100,
  ADDR2    = 22'b0000000000000000001000,
  ADDR3    = 22'b0000000000000000010000,
  ADDR4    = 22'b0000000000000000100000,
  ADDR5    = 22'b0000000000000001000000,
  WRITE1   = 22'b0000000000000010000000,
  WRITE2   = 22'b0000000000000100000000,
  WRITE3   = 22'b0000000000001000000000,
  WRITE4   = 22'b0000000000010000000000,
  WRITE5   = 22'b0000000000100000000000,
  READ1    = 22'b0000000001000000000000,
  READ2    = 22'b0000000010000000000000,
  READ3    = 22'b0000000100000000000000,
  READ4    = 22'b0000001000000000000000,
  READ5    = 22'b0000010000000000000000,
  STOP1    = 22'b0000100000000000000000,
  STOP2    = 22'b0001000000000000000000,
  STOP3    = 22'b0010000000000000000000,
  RESTART1 = 22'b0100000000000000000000,
  RESTART2 = 22'b1000000000000000000000;

  
  reg [21:0] state, next_state;
  reg [7:0] data_sr, next_data_sr;
  reg read, next_read;
  reg [4:0] status_reg, next_status_reg;
  reg [2:0] bitcnt, next_bitcnt;
  reg ack_out, next_ack_out;
  reg SDA_out, next_SDA_out;
  reg SCL_out, next_SCL_out;
  reg [15:0] delay_counter, next_delay_counter;
  reg set_interrupt, clr_stop;
  reg enable, start, stop, interrupt, ack_enable;

  assign SDA = SDA_out ? 1'bz : 1'b0;
  assign SCL = SCL_out ? 1'bz : 1'b0;
  assign SDA_in = SDA;
  assign SCL_in = SCL;
  
  assign status = interrupt ? status_reg : 5'h1f;
  assign data = data_sr;
  assign control = {1'b0, enable, start, stop, interrupt, ack_enable, 2'b00};

  // control register bits
  always @ (posedge clk) begin
    if (~rst | (wr_conclr & wrdata[6]))
      enable <= 1'b0;
    else if (wr_conset & wrdata[6])
      enable <= 1'b1;
    if (~rst | (wr_conclr & wrdata[5]))
      start <= 1'b0;
    else if (wr_conset & wrdata[5])
      start <= 1'b1;
    if (~rst | ~enable | clr_stop)
      stop <= 1'b0;
    else if (wr_conset & wrdata[4])
      stop <= 1'b1;
    if (~rst | ~enable | (wr_conclr & wrdata[3]))
      interrupt <= 1'b0;
    else if (set_interrupt)
      interrupt <= 1'b1;
    if (~rst | (wr_conclr & wrdata[2]))
      ack_enable <= 1'b0;
    else if (wr_conset & wrdata[2])
      ack_enable <= 1'b1;
  end

  always @ (posedge clk) begin
    if (~enable) begin
      state <= IDLE;
      data_sr <= 8'd0;
      read <= 1'b0;
      status_reg <= 5'd0;
      bitcnt <= 3'd0;
      ack_out <= 1'b0;
      SDA_out <= 1'b1;
      SCL_out <= 1'b1;
      delay_counter <= 16'd0;
    end else begin
      state <= next_state;
      data_sr <= (interrupt & wr_data) ? wrdata : next_data_sr;
      read <= next_read;
      status_reg <= next_status_reg;
      bitcnt <= next_bitcnt;
      ack_out <= next_ack_out;
      SDA_out <= next_SDA_out;
      SCL_out <= next_SCL_out;
      delay_counter <= next_delay_counter;
    end
  end

  always @ * begin
    next_state = state;
    next_data_sr = data_sr;
    next_read = read;
    next_status_reg = status_reg;
    next_bitcnt = bitcnt;
    next_delay_counter = delay_counter;
    next_ack_out = ack_out;
    next_SDA_out = SDA_out;
    next_SCL_out = SCL_out;
    set_interrupt = 1'b0;
    clr_stop = 1'b0;
    case (state)
      IDLE: begin
        next_SDA_out = 1'b1;
        next_SCL_out = 1'b1;
        next_delay_counter = 16'd0;
        if (start & ~interrupt) begin
          next_status_reg = 5'h01;
          next_SDA_out = 1'b0;
          next_state = START;
        end
      end
      START: begin
        next_delay_counter = delay_counter + 1'b1;
        if (delay_counter == sclh) begin
          next_SCL_out = 1'b0;
          set_interrupt = 1'b1;
          next_delay_counter = 16'd0;
          next_state = ADDR1;
        end
      end
      ADDR1: begin
        next_delay_counter = 16'd0;
        if (~interrupt) begin
          if (stop) begin
            next_SCL_out = 1'b1;
            next_state = STOP1;
          end else if (start) begin
            next_SDA_out = 1'b1;
            next_state = RESTART1;
          end else begin
            next_bitcnt = 3'd0;
            next_read = data_sr[0];
            next_SDA_out = data_sr[7];
            next_state = ADDR2;
          end
        end
      end
      ADDR2: begin
        next_delay_counter = delay_counter + 1'b1;
         if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_data_sr = {data_sr[6:0], SDA_in};
          next_delay_counter = 16'd0;
          next_state = ADDR3;
        end
      end
      ADDR3: begin
        // clock stretching
        next_delay_counter = SCL_in ? delay_counter + 1'b1 : 16'd0;
        if(delay_counter == sclh) begin
          next_SCL_out = 1'b0;
          next_bitcnt = bitcnt + 1'b1;
          next_delay_counter = 16'd0;
          if (bitcnt == 3'd7) begin
            next_SDA_out = 1'b1;
            next_state = ADDR4;
          end else begin
            next_SDA_out = data_sr[7];
            next_state = ADDR2;
          end
        end
      end
      ADDR4: begin
        next_delay_counter = delay_counter + 1'b1;
         if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_delay_counter = 16'd0;
          next_state = ADDR5;
        end
      end
      ADDR5: begin
        // clock stretching
        next_delay_counter = SCL_in ? delay_counter + 1'b1 : 16'd0;
        if(delay_counter == sclh) begin
          next_SCL_out = 1'b0;
          next_delay_counter = 16'd0;
          next_status_reg = read ? (SDA_in ? 5'h9 : 5'h8)
                                 : (SDA_in ? 5'h4 : 5'h3);
          set_interrupt = 1'b1;
          next_state = read ? READ1 : WRITE1;
        end
      end

      WRITE1: begin
        next_delay_counter = 16'd0;
        if (~interrupt) begin
          if (stop) begin
            next_state = STOP1;
          end else if (start) begin
            next_SDA_out = 1'b1;
            next_state = RESTART1;
          end else begin
            next_bitcnt = 3'd0;
            next_SDA_out = data_sr[7];
            next_state = WRITE2;
          end
        end
      end
      WRITE2: begin
        next_delay_counter = delay_counter + 1'b1;
         if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_data_sr = {data_sr[6:0], SDA_in};
          next_delay_counter = 16'd0;
          next_state = WRITE3;
        end
      end
      WRITE3: begin
        // clock stretching
        next_delay_counter = SCL_in ? delay_counter + 1'b1 : 16'd0;
        if(delay_counter == sclh) begin
          next_SCL_out = 1'b0;
          next_bitcnt = bitcnt + 1'b1;
          next_delay_counter = 16'd0;
          if (bitcnt == 3'd7) begin
            next_SDA_out = 1'b1;
            next_state = WRITE4;
          end else begin
            next_SDA_out = data_sr[7];
            next_state = WRITE2;
          end
        end
      end
      WRITE4: begin
        next_delay_counter = delay_counter + 1'b1;
         if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_delay_counter = 16'd0;
          next_state = WRITE5;
        end
      end
      WRITE5: begin
        // clock stretching
        next_delay_counter = SCL_in ? delay_counter + 1'b1 : 16'd0;
        if(delay_counter == sclh) begin
          next_SCL_out = 1'b0;
          next_delay_counter = 16'd0;
          next_status_reg = SDA_in ? 5'h6 : 5'h5;
          set_interrupt = 1'b1;
          next_state = WRITE1;
        end
      end

      READ1: begin
        next_delay_counter = 16'd0;
        if (~interrupt) begin
          if (stop) begin
            next_state = STOP1;
          end else if (start) begin
            next_SDA_out = 1'b1;
            next_state = RESTART1;
          end else begin
            next_bitcnt = 3'd0;
            next_SDA_out = 1'b1;
            next_ack_out = ~ack_enable;
            next_state = READ2;
          end
        end
      end
      READ2: begin
        next_delay_counter = delay_counter + 1'b1;
         if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_data_sr = {data_sr[6:0], SDA_in};
          next_delay_counter = 16'd0;
          next_state = READ3;
        end
      end
      READ3: begin
        // clock stretching
        next_delay_counter = SCL_in ? delay_counter + 1'b1 : 16'd0;
        if(delay_counter == sclh) begin
          next_SCL_out = 1'b0;
          next_bitcnt = bitcnt + 1'b1;
          next_delay_counter = 16'd0;
          if (bitcnt == 3'd7) begin
            next_SDA_out = ack_out;
            next_state = READ4;
          end else begin
            next_state = READ2;
          end
        end
      end
      READ4: begin
        next_delay_counter = delay_counter + 1'b1;
         if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_delay_counter = 16'd0;
          next_state = READ5;
        end
      end
      READ5: begin
        // clock stretching
        next_delay_counter = SCL_in ? delay_counter + 1'b1 : 16'd0;
        if(delay_counter == sclh) begin
          next_SCL_out = 1'b0;
          next_delay_counter = 16'd0;
          next_status_reg = ack_out ? 5'hb : 5'ha;
          set_interrupt = 1'b1;
          next_state = READ1;
        end
      end

      STOP1: begin
        next_delay_counter = delay_counter + 1'b1;
        if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_delay_counter = 16'd0;
          next_state = STOP2;
        end
      end
      STOP2: begin
        next_delay_counter = delay_counter + 1'b1;
        if (delay_counter == sclh) begin
          next_SDA_out = 1'b1;
          next_delay_counter = 16'd0;
          next_state = STOP3;
        end
      end
      STOP3: begin
        next_delay_counter = delay_counter + 1'b1;
        if (delay_counter == sclh) begin
          clr_stop = 1'b1;
          next_state = IDLE;
        end
      end

      RESTART1: begin
        next_delay_counter = delay_counter + 1'b1;
        if (delay_counter == scll) begin
          next_SCL_out = 1'b1;
          next_delay_counter = 16'd0;
          next_state = RESTART2;
        end
      end
      RESTART2: begin
        next_delay_counter = delay_counter + 1'b1;
        if (delay_counter == sclh) begin
          next_status_reg = 5'h02;
          next_SDA_out = 1'b0;
          next_state = START;
        end
      end
      default:
        next_state = IDLE;
    endcase
  end
endmodule
        
-------------- next part --------------
`timescale 1ns / 1ps  // 22.9.2015
// with SRAM, byte access, flt.-pt., and gpio
// PS/2 mouse and network 7.1.2014 PDR

module RISC5Top(
  input CLK50M,
  input [3:0] btn,
  input [7:0] swi,
  input  RxD,   // RS-232
  output TxD,
  output [7:0] leds,
  output SDled,
  output SRce0, SRce1, SRwe, SRoe,  //SRAM
  output [3:0] SRbe,
  output [18:0] SRadr,
  inout [31:0] SRdat,
  input [1:0] MISO,          // SPI - SD card & network
  output [1:0] SCLK, MOSI,
  output [1:0] SS,
  output NEN,  // network enable
  output hsync, vsync, // video controller
  output [7:0] RGB,
  input PS2C, PS2D,    // keyboard
  inout msclk, msdat,
  inout [7:0] gpio,
  inout SDA, SCL);

// IO addresses for input / output
// 0  milliseconds / --
// 1  switches / LEDs
// 2  RS-232 data / RS-232 data (start)
// 3  RS-232 status / RS-232 control
// 4  SPI data / SPI data (start)
// 5  SPI status / SPI control
// 6  PS2 keyboard / --
// 7  mouse / --
// 8  general-purpose I/O data
// 9  general-purpose I/O tri-state control
// 10 I2C Control set
// 11 I2C Status (read-only)
// 12 I2C Data
// 13 I2C Clock high count
// 14 I2C Clock low count
// 15 I2C Control clear (write-only)

reg rst, clk;
wire[23:0] adr;
wire [3:0] iowadr; // word address
wire [31:0] inbus, inbus0;  // data to RISC core
wire [31:0] outbus;  // data from RISC core
wire rd, wr, ben, ioenb, dspreq;

wire [7:0] dataTx, dataRx, dataKbd;
wire rdyRx, doneRx, startTx, rdyTx, rdyKbd, doneKbd;
wire [27:0] dataMs;
reg bitrate;  // for RS232
wire limit;  // of cnt0

reg [7:0] Lreg;
reg [15:0] cnt0;
reg [31:0] cnt1; // milliseconds

wire [31:0] spiRx;
wire spiStart, spiRdy;
reg [3:0] spiCtrl;
wire [17:0] vidadr;
reg [7:0] gpout, gpoc;
wire [7:0] gpin;
wire [7:0] i2c_control, i2c_data;
wire [4:0] i2c_status;
wire wr_i2c_conset, wr_i2c_data, wr_i2c_conclr;
reg [15:0] i2c_sclh, i2c_scll;

RISC5 riscx(.clk(clk), .rst(rst), .rd(rd), .wr(wr), .ben(ben), .stallX(dspreq),
   .adr(adr), .codebus(inbus0), .inbus(inbus), .outbus(outbus));
RS232R receiver(.clk(clk), .rst(rst), .RxD(RxD), .fsel(bitrate), .done(doneRx),
   .data(dataRx), .rdy(rdyRx));
RS232T transmitter(.clk(clk), .rst(rst), .start(startTx), .fsel(bitrate),
   .data(dataTx), .TxD(TxD), .rdy(rdyTx));
SPI spi(.clk(clk), .rst(rst), .start(spiStart), .dataTx(outbus),
   .fast(spiCtrl[2]), .dataRx(spiRx), .rdy(spiRdy),
 	.SCLK(SCLK[0]), .MOSI(MOSI[0]), .MISO(MISO[0] & MISO[1]));
VID vid(.clk(clk), .req(dspreq), .inv(swi[7]),
   .vidadr(vidadr), .viddata(inbus0), .RGB(RGB), .hsync(hsync), .vsync(vsync));
PS2 kbd(.clk(clk), .rst(rst), .done(doneKbd), .rdy(rdyKbd), .shift(),
   .data(dataKbd), .PS2C(PS2C), .PS2D(PS2D));
MouseM Ms(.clk(clk), .rst(rst), .msclk(msclk), .msdat(msdat), .out(dataMs));
I2C i2c(.clk(clk), .rst(rst), .SDA(SDA), .SCL(SCL), .sclh(i2c_sclh),
   .scll(i2c_scll), .control(i2c_control), .status(i2c_status),
   .data(i2c_data), .wrdata(outbus[7:0]), .wr_conset(wr_i2c_conset),
   .wr_data(wr_i2c_data), .wr_conclr(wr_i2c_conclr));

assign iowadr = adr[5:2];
assign ioenb = (adr[23:6] == 18'h3FFFF);
assign inbus = ~ioenb ? inbus0 :
   ((iowadr == 0) ? cnt1 :
    (iowadr == 1) ? {20'b0, btn, swi} :
    (iowadr == 2) ? {24'b0, dataRx} :
    (iowadr == 3) ? {30'b0, rdyTx, rdyRx} :
    (iowadr == 4) ? spiRx :
    (iowadr == 5) ? {31'b0, spiRdy} :
    (iowadr == 6) ? {3'b0, rdyKbd, dataMs} :
    (iowadr == 7) ? {24'b0, dataKbd} :
    (iowadr == 8) ? {24'b0, gpin} :
    (iowadr == 9) ? {24'b0, gpoc} :
    (iowadr == 10) ? {24'b0, i2c_control} :
    (iowadr == 11) ? {24'b0, i2c_status, 3'b000} :
    (iowadr == 12) ? {24'b0, i2c_data} :
    (iowadr == 13) ? {16'b0, i2c_sclh} :
    (iowadr == 14) ? {16'b0, i2c_scll} : 0);
	 
assign SRce0 = ben & adr[1];
assign SRce1 = ben & ~adr[1];
assign SRbe0 = ben & adr[0];
assign SRbe1 = ben & ~adr[0];
assign SRwe = ~wr | clk;
assign SRoe = wr;
assign SRbe = {SRbe1, SRbe0, SRbe1, SRbe0};
assign SRadr = dspreq ? vidadr : adr[19:2];

genvar i;
generate // tri-state buffer for SRAM
  for (i = 0; i < 32; i = i+1)
  begin: bufblock
    IOBUF SRbuf (.I(outbus[i]), .O(inbus0[i]), .IO(SRdat[i]), .T(~wr));
  end
endgenerate

generate // tri-state buffer for gpio port
  for (i = 0; i < 8; i = i+1)
  begin: gpioblock
    IOBUF gpiobuf (.I(gpout[i]), .O(gpin[i]), .IO(gpio[i]), .T(~gpoc[i]));
  end
endgenerate

assign dataTx = outbus[7:0];
assign startTx = wr & ioenb & (iowadr == 2);
assign doneRx = rd & ioenb & (iowadr == 2);
assign limit = (cnt0 == 24999);
assign leds = Lreg;
assign spiStart = wr & ioenb & (iowadr == 4);
assign SS = ~spiCtrl[1:0];  //active low slave select
assign MOSI[1] = MOSI[0], SCLK[1] = SCLK[0], NEN = spiCtrl[3];
assign doneKbd = rd & ioenb & (iowadr == 7);
assign SDled = spiCtrl[0];
assign wr_i2c_conset = wr & ioenb & (iowadr == 10);
assign wr_i2c_data = wr & ioenb & (iowadr == 12);
assign wr_i2c_conclr = wr & ioenb & (iowadr == 15);

always @(posedge clk)
begin
  rst <= ((cnt1[4:0] == 0) & limit) ? ~btn[3] : rst;
  Lreg <= ~rst ? 0 : (wr & ioenb & (iowadr == 1)) ? outbus[7:0] : Lreg;
  cnt0 <= limit ? 0 : cnt0 + 1;
  cnt1 <= cnt1 + limit;
  spiCtrl <= ~rst ? 0 : (wr & ioenb & (iowadr == 5)) ? outbus[3:0] : spiCtrl;
  bitrate <= ~rst ? 0 : (wr & ioenb & (iowadr == 3)) ? outbus[0] : bitrate;
  gpout <= (wr & ioenb & (iowadr == 8)) ? outbus[7:0] : gpout;
  gpoc <= ~rst ? 0 : (wr & ioenb & (iowadr == 9)) ? outbus[7:0] : gpoc;
  i2c_sclh <= ~rst ? 0 : (wr & ioenb & (iowadr == 13)) ? outbus[15:0] : i2c_sclh;
  i2c_scll <= ~rst ? 0 : (wr & ioenb & (iowadr == 14)) ? outbus[15:0] : i2c_scll;
end

always @ (posedge CLK50M) clk <= ~clk;
endmodule


More information about the Oberon mailing list