aboutsummaryrefslogtreecommitdiff
path: root/arch/riscv/usr/basics.c
blob: 4fa7df3cee88af3e6537411127c6736ff35dac7d (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

/*
 * Returns the factorial for the given unsigned 64-bit integer.
 *
 * Implemented in factorial.S
 */
int factorial(uint64_t n);

void test_factorial(void)
{
	assert(factorial(0) == 0);
	assert(factorial(1) == 1);
	assert(factorial(2) == 2);
	assert(factorial(3) == 6);
	assert(factorial(4) == 24);

	printf("factorial:\t\tOK\n");
}

/*
 * Returns a pointer with the same given string but reversed. Note that the
 * string cannot be in read-only space since the reversal is done in-place.
 *
 * Implemented in string.S
 */
char * reverse_string(char *str);

void test_reverse_string(void)
{
	assert(reverse_string(NULL) == NULL);
	assert(reverse_string("") == "");

	char s1[] = "This is a string.";
	assert(strcmp(reverse_string(s1), ".gnirts a si sihT") == 0);

	char s2[] = ".";
	assert(strcmp(reverse_string(s2), ".") == 0);

	printf("reverse_string:\t\tOK\n");
}

/*
 * Returns true if the given string is a palindrome, false otherwise.
 *
 * Implemented in string.S
 */
bool is_palindrome(char *str);

void test_is_palindrome(void)
{
	assert(is_palindrome(NULL) == 0);
	assert(is_palindrome("") == 0);
	assert(is_palindrome("aba") == 1);
	assert(is_palindrome("aaa") == 1);
	assert(is_palindrome("This is a a si sihT") == 1);

	printf("is_palindrome:\t\tOK\n");
}

/*
 * This function computes the following: ((a + b) * b) + b. Don't try to make
 * sense of it, that's the computation I ended up while messing with RISC-V
 * floating point instructions. Anyways, with this in mind, it will return if
 * the computed value is greater than 10.
 *
 * Implemented in float.S
 */
bool greater_than_ten(double a, uint64_t b);

void test_greater_than_ten(void)
{
	assert(!greater_than_ten(2.3, 1)); // 4.3
	assert(greater_than_ten(2.3, 2));  // 10.6

	printf("greater_than_ten:\tOK\n");
}

/*
 * Atomically add the integer pointed by `a` with the given `value`. Although
 * this is a function, I've checked that the assembly produced by GCC on a
 * decent optimization level actually inlines this.
 */
static inline void atomic_add(uint64_t *a, uint64_t value)
{
	/*
	 * The execution is a mere `amoadd` instruction, but it will set on `t0` the
	 * result. If it fails (e.g. the address was already being used), then `t0 =
	 * 0` and `beqz` will instruct it to try again.
	 *
	 * Note that on the board I'm using the 'Zawrs' extension is not available.
	 * Otherwise I could have re-arranged to code to something like this:
	 *
	 *     amoadd.d t0, %1, %0
	 *     bnez t0, .Latomic_add_done
	 *     wrs.nto
	 *  .Latomic_add_done:
	 *
	 * This is an extra instruction but it saves on power if the memory address
	 * happens to be used at access time.
	 *
	 * As for the 'A' RISC-V specific constraint, it means "An address that is
	 * held in a general-purpose register". GCC will then do the magic and
	 * convert it to `amoadd.d t0, a1, (a0)` or something like that. Note that
	 * we have to pass the '+' constraint modifier because the address will be
	 * both read and written atomically.
	 *
	 * See: https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html.
	 */
    asm volatile(".Latomic_add_retry:\n\t"
				 "amoadd.d t0, %1, %0\n\t"
				 "beqz t0, .Latomic_add_retry"
				 : "+A" (*a)
				 : "r" (value)
				 : "memory");
}

void test_atomic_add(void)
{
	uint64_t i = 4;

	atomic_add(&i, 0);
	assert(i == 4);

	atomic_add(&i, 2);
	assert(i == 6);

	printf("atomic_add:\t\tOK\n");
}

int main()
{
	test_factorial();
	test_reverse_string();
	test_is_palindrome();
	test_greater_than_ten();
	test_atomic_add();
}