[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