summaryrefslogtreecommitdiff
path: root/tb/interval_timer.cpp
blob: 7ab15d857dda2b30603ef32a1616496ade1a0038 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <cstdint>

#include "avalon.hpp"
#include "interval_timer.hpp"

namespace taller::avalon
{
	interval_timer::interval_timer(std::uint32_t base) noexcept
	: slave(base, 32, 4)
	{}

	void interval_timer::tick() noexcept
	{
		if(!status_run)
		{
			return;
		} else if(count > 0)
		{
			--count;
		} else
		{
			count = period;
			status_to = 1;
			status_run = control_cont;
		}
	}

	bool interval_timer::read(std::uint32_t addr, std::uint32_t &data) noexcept
	{
		switch(addr)
		{
			case 0:
				data
					= status_run << 1
					| status_to  << 0;

				break;

			case 1:
				data
					= control_cont << 1
					| control_ito  << 0;

				break;

			case 2:
				data = period & 0xffff;
				break;

			case 3:
				data = period >> 16;
				break;

			case 4:
				data = snap & 0xffff;
				break;

			case 5:
				data = snap >> 16;
				break;
		}

		return true;
	}

	bool interval_timer::write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) noexcept
	{
		switch(addr)
		{
			case 0:
				status_to = 0;
				break;

			case 1:
				control_ito = !!(data & (1 << 0));
				control_cont = !!(data & (1 << 1));

				status_run = (status_run && !!(data & (1 << 3))) || !!(data & (1 << 2));
				break;

			case 2:
				period = (period & 0xffff'0000) | (data & 0xffff);
				count = period;
				break;

			case 3:
				period = (period & 0xffff) | (data & 0xffff) << 16;
				count = period;
				break;

			case 4:
			case 5:
				snap = count;
				break;
		}

		return true;
	}

	bool interval_timer::irq() noexcept
	{
		return control_ito && status_to;
	}
}