Skip to content

Commit

Permalink
sim: Add I2C memory simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
agkaminski committed Sep 17, 2023
1 parent d34abb3 commit 3a38d76
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*.asm
*.map
*.lib
.vscode
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@ Handheld PC based on the original 6502 CPU that fits into a pocket.

# Simulator

Pocket265 simulator is there to make FW developement easier, but it can be also used to get a feel for the computer usage. I2C memory and UART are not simulated. It uses [simak65](https://github.com/agkaminski/simak65) simulator code.
Pocket265 simulator is there to make FW developement easier, but it can be also used to get a feel for the computer usage. Only UART is not simulated. It uses [simak65](https://github.com/agkaminski/simak65) simulator code.

## Usage

./pocket265-sim [OPTIONS]

where options are:
- -c: path to the computer ROM (obligatory),
- -f: simulated CPU frequency, from 0 (max speed, no throttling) to 20000000 (Hz),
- -r: simulated RAM size in bytes,
- -e: simulated EEPROM file (will be updated after end of the simulation, if write is performed!),
- -h: usage.

## Builing

To run the simulation first build and install [simak65](https://github.com/agkaminski/simak65):
<pre>
Expand All @@ -51,6 +64,8 @@ then build the simulator:
$ (cd sim && make all)
</pre>

## Running the simulation

In the `sim/` directory is a script that starts the simulator with memory initialized with Pocket265 firmware - `runsim.sh`. To run the simulator:
<pre>
$ cd sim
Expand Down
2 changes: 1 addition & 1 deletion sim/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CFLAGS := -g -Wall -Werror -Wextra -Wno-type-limits -O2 -ansi -std=gnu99
DEBUG := -DNDEBUG

OUT = pocket265-sim
OBJ = pocket265.o
OBJ = pocket265.o i2c.o
LIB = simak65

%.o: %.c
Expand Down
146 changes: 146 additions & 0 deletions sim/i2c.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/* 24xx I2C memory simulator
* Copyright A.K. 2018, 2023
*/

#include <stdlib.h>
#include <string.h>
#include "i2c.h"

void i2c_step(struct i2c_ctx *ctx, const struct i2c_bus *in, struct i2c_bus *out)
{
*out = ctx->prevout;

if (in->scl) {
if (ctx->previn.sda && !in->sda) {
/* Start condition */
ctx->state = i2c_dev_addr;
ctx->regbit = 0;
}
else if (!ctx->previn.sda && in->sda) {
/* Stop condtion */
ctx->state = i2c_idle;
}
}

if (in->scl && !ctx->previn.scl) {
/* Rising edge */
switch (ctx->state) {
case i2c_dev_addr:
ctx->reg = (ctx->reg << 1) | !!in->sda;
if (++ctx->regbit > 7) {
ctx->regbit = 0;
if (ctx->devaddr == (ctx->reg >> 1)) {
if (ctx->reg & 1)
ctx->next = i2c_read;
else
ctx->next = i2c_addr;
ctx->state = i2c_ack;
}
else {
ctx->state = i2c_idle;
}
}
break;

case i2c_addr:
ctx->address = (ctx->address << 1) | !!in->sda;
++ctx->regbit;
if (ctx->regbit == 8) {
ctx->state = i2c_ack;
ctx->next = i2c_addr;
}
else if (ctx->regbit > 15) {
ctx->regbit = 0;
ctx->next = i2c_write;
ctx->state = i2c_ack;
}
break;

case i2c_write:
ctx->reg = (ctx->reg << 1) | !!in->sda;
if (++ctx->regbit > 7) {
ctx->regbit = 0;
ctx->mem[ctx->address % ctx->memsz] = ctx->reg;
++ctx->address;
ctx->next = i2c_write;
ctx->state = i2c_ack;
}
break;

case i2c_read:
++ctx->regbit;
if (ctx->regbit == 9) {
if (in->sda) {
ctx->state = i2c_idle;
ctx->regbit = 0;
}
else {
ctx->regbit = 0;
++ctx->address;
}
}
break;

case i2c_ack:
ctx->state = ctx->next;
break;

default:
break;
}
}
else if (!in->scl && ctx->previn.scl) {
/* Falling ege */
switch (ctx->state) {
case i2c_ack:
out->sda = 0;
break;

case i2c_read:
if (ctx->regbit == 0)
ctx->reg = ctx->mem[ctx->address % ctx->memsz];

if (ctx->regbit <= 7) {
out->sda = !!(ctx->reg & 0x80);
ctx->reg <<= 1;
}
else {
out->sda = 1;
}
break;

default:
out->sda = 1;
break;
}
}

ctx->previn = *in;
ctx->prevout = *out;
}

void i2c_destroy(struct i2c_ctx *ctx)
{
free(ctx->mem);
ctx->mem = NULL;
}

int i2c_init(struct i2c_ctx *ctx, size_t memsz, uint8_t devaddr)
{
ctx->previn = (struct i2c_bus){ .sda = 1, .scl = 1 };
ctx->prevout = (struct i2c_bus){ .sda = 1, .scl = 1 };

ctx->state = i2c_idle;
ctx->address = 0;
ctx->reg = 0;
ctx->regbit = 0;
ctx->devaddr = devaddr;

ctx->mem = malloc(memsz);
if (ctx->mem == NULL) {
return -1;
}
ctx->memsz = memsz;

return 0;
}
49 changes: 49 additions & 0 deletions sim/i2c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* 24xx I2C memory simulator
* Copyright A.K. 2018, 2023
*/

#ifndef I2C_H_
#define I2C_H_

#include <stdint.h>
#include <stddef.h>

struct i2c_bus {
int sda;
int scl;
};

enum i2c_state {
i2c_idle,
i2c_dev_addr,
i2c_addr,
i2c_read,
i2c_write,
i2c_ack
};

struct i2c_ctx {
struct i2c_bus previn;
struct i2c_bus prevout;

uint8_t *mem;
size_t memsz;

uint16_t address;

uint8_t devaddr;

uint8_t reg;
uint8_t regbit;

enum i2c_state state;
enum i2c_state next;
};

void i2c_step(struct i2c_ctx *ctx, const struct i2c_bus *in, struct i2c_bus *out);

void i2c_destroy(struct i2c_ctx *ctx);

int i2c_init(struct i2c_ctx *ctx, size_t memsz, uint8_t devaddr);

#endif
Loading

0 comments on commit 3a38d76

Please sign in to comment.